source: trunk/third/bonobo/bonobo/bonobo-ui-component.c @ 16855

Revision 16855, 37.6 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16854, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2/*
3 * gnome-component-ui.c: Client UI signal multiplexer and verb repository.
4 *
5 * Author:
6 *     Michael Meeks (michael@helixcode.com)
7 *
8 * Copyright 1999, 2000 Helix Code, Inc.
9 */
10#include <config.h>
11#include <gnome.h>
12#include <bonobo/bonobo-exception.h>
13#include <bonobo/bonobo-ui-xml.h>
14#include <bonobo/bonobo-ui-component.h>
15#include <bonobo/bonobo-ui-util.h>
16#include <gnome-xml/tree.h>
17#include <gnome-xml/parser.h>
18
19#define PARENT_TYPE BONOBO_X_OBJECT_TYPE
20
21static GtkObjectClass *bonobo_ui_component_parent_class;
22
23enum {
24        EXEC_VERB,
25        UI_EVENT,
26        LAST_SIGNAL
27};
28static guint signals[LAST_SIGNAL] = { 0 };
29
30#define GET_CLASS(c) (BONOBO_UI_COMPONENT_CLASS (GTK_OBJECT (c)->klass))
31
32typedef struct {
33        char              *id;
34        BonoboUIListenerFn cb;
35        gpointer           user_data;
36        GDestroyNotify     destroy_fn;
37} UIListener;
38
39typedef struct {
40        char          *cname;
41        BonoboUIVerbFn cb;
42        gpointer       user_data;
43        GDestroyNotify destroy_fn;
44} UIVerb;
45
46struct _BonoboUIComponentPrivate {
47        GHashTable        *verbs;
48        GHashTable        *listeners;
49        char              *name;
50        Bonobo_UIContainer container;
51};
52
53static inline BonoboUIComponent *
54bonobo_ui_from_servant (PortableServer_Servant servant)
55{
56        return BONOBO_UI_COMPONENT (bonobo_object_from_servant (servant));
57}
58
59static gboolean
60verb_destroy (gpointer dummy, UIVerb *verb, gpointer dummy2)
61{
62        if (verb) {
63                if (verb->destroy_fn)
64                        verb->destroy_fn (verb->user_data);
65                verb->destroy_fn = NULL;
66                g_free (verb->cname);
67                g_free (verb);
68        }
69        return TRUE;
70}
71
72static gboolean
73listener_destroy (gpointer dummy, UIListener *l, gpointer dummy2)
74{
75        if (l) {
76                if (l->destroy_fn)
77                        l->destroy_fn (l->user_data);
78                l->destroy_fn = NULL;
79                g_free (l->id);
80                g_free (l);
81        }
82        return TRUE;
83}
84
85static void
86ui_event (BonoboUIComponent           *component,
87          const char                  *id,
88          Bonobo_UIComponent_EventType type,
89          const char                  *state)
90{
91        UIListener *list;
92
93        g_return_if_fail (component != NULL);
94        g_return_if_fail (component->priv != NULL);
95
96        list = g_hash_table_lookup (component->priv->listeners, id);
97        if (list && list->cb)
98                list->cb (component, id, type,
99                          state, list->user_data);
100}
101
102static CORBA_char *
103impl_Bonobo_UIComponent_describeVerbs (PortableServer_Servant servant,
104                                       CORBA_Environment     *ev)
105{
106        g_warning ("FIXME: Describe verbs unimplemented");
107        return CORBA_string_dup ("<NoUIVerbDescriptionCodeYet/>");
108}
109
110static void
111impl_Bonobo_UIComponent_execVerb (PortableServer_Servant servant,
112                                  const CORBA_char      *cname,
113                                  CORBA_Environment     *ev)
114{
115        BonoboUIComponent *component;
116        UIVerb *verb;
117
118        component = bonobo_ui_from_servant (servant);
119
120        g_return_if_fail (component != NULL);
121        g_return_if_fail (component->priv != NULL);
122
123        bonobo_object_ref (BONOBO_OBJECT (component));
124       
125/*      g_warning ("TESTME: Exec verb '%s'", cname);*/
126
127        verb = g_hash_table_lookup (component->priv->verbs, cname);
128        if (verb && verb->cb)
129                verb->cb (component, verb->user_data, cname);
130        else
131                g_warning ("FIXME: verb '%s' not found, emit exception", cname);
132
133        gtk_signal_emit (GTK_OBJECT (component),
134                         signals [EXEC_VERB],
135                         cname);
136
137        bonobo_object_unref (BONOBO_OBJECT (component));
138}
139
140static void
141impl_Bonobo_UIComponent_uiEvent (PortableServer_Servant             servant,
142                                 const CORBA_char                  *id,
143                                 const Bonobo_UIComponent_EventType type,
144                                 const CORBA_char                  *state,
145                                 CORBA_Environment                 *ev)
146{
147        BonoboUIComponent *component;
148
149        component = bonobo_ui_from_servant (servant);
150
151/*      g_warning ("TESTME: Event '%s' '%d' '%s'\n", path, type, state);*/
152
153        bonobo_object_ref (BONOBO_OBJECT (component));
154
155        gtk_signal_emit (GTK_OBJECT (component),
156                         signals [UI_EVENT], id, type, state);
157
158        bonobo_object_unref (BONOBO_OBJECT (component));
159}
160
161
162/**
163 * bonobo_ui_component_add_verb_full:
164 * @component: the component to add it to
165 * @cname: the programmatic name of the verb
166 * @fn: the callback function for invoking it
167 * @user_data: the associated user data for the callback
168 * @destroy_fn: a destroy function for the callback data
169 *
170 * Add a verb to the UI component, that can be invoked by
171 * the container.
172 **/
173void
174bonobo_ui_component_add_verb_full (BonoboUIComponent  *component,
175                                   const char         *cname,
176                                   BonoboUIVerbFn      fn,
177                                   gpointer            user_data,
178                                   GDestroyNotify      destroy_fn)
179{
180        UIVerb *verb;
181        BonoboUIComponentPrivate *priv;
182
183        g_return_if_fail (cname != NULL);
184        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
185        g_return_if_fail (component->priv != NULL);
186
187        priv = component->priv;
188
189        if ((verb = g_hash_table_lookup (priv->verbs, cname))) {
190                g_hash_table_remove (priv->verbs, cname);
191                verb_destroy (NULL, verb, NULL);
192        }
193
194        verb = g_new (UIVerb, 1);
195        verb->cname      = g_strdup (cname);
196        verb->cb         = fn;
197        verb->user_data  = user_data;
198        verb->destroy_fn = destroy_fn;
199
200        g_hash_table_insert (priv->verbs, verb->cname, verb);
201}
202
203/**
204 * bonobo_ui_component_add_verb:
205 * @component: the component to add it to
206 * @cname: the programmatic name of the verb
207 * @fn: the callback function for invoking it
208 * @user_data: the associated user data for the callback
209 *
210 * Add a verb to the UI component, that can be invoked by
211 * the container.
212 **/
213void
214bonobo_ui_component_add_verb (BonoboUIComponent  *component,
215                              const char         *cname,
216                              BonoboUIVerbFn      fn,
217                              gpointer            user_data)
218{
219        bonobo_ui_component_add_verb_full (
220                component, cname, fn, user_data, NULL);
221}
222
223typedef struct {
224        gboolean    by_name;
225        const char *name;
226        gboolean    by_func;
227        gpointer    func;
228        gboolean    by_data;
229        gpointer    user_data;
230} RemoveInfo;
231
232static gboolean
233remove_verb (gpointer   key,
234             gpointer   value,
235             gpointer   user_data)
236{
237        RemoveInfo *info = user_data;
238        UIVerb     *verb = value;
239
240        if (info->by_name && info->name &&
241            !strcmp (verb->cname, info->name))
242                return verb_destroy (NULL, verb, NULL);
243
244        else if (info->by_func &&
245                 (BonoboUIVerbFn) info->func == verb->cb)
246                return verb_destroy (NULL, verb, NULL);
247
248        else if (info->by_data &&
249                 (BonoboUIVerbFn) info->user_data == verb->user_data)
250                return verb_destroy (NULL, verb, NULL);
251
252        return FALSE;
253}
254
255/**
256 * bonobo_ui_component_remove_verb:
257 * @component: the component to add it to
258 * @cname: the programmatic name of the verb
259 *
260 * Remove a verb by it's unique name
261 **/
262void
263bonobo_ui_component_remove_verb (BonoboUIComponent  *component,
264                                 const char         *cname)
265{
266        RemoveInfo info;
267
268        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
269        g_return_if_fail (component->priv != NULL);
270
271        memset (&info, 0, sizeof (info));
272
273        info.by_name = TRUE;
274        info.name = cname;
275
276        g_hash_table_foreach_remove (component->priv->verbs, remove_verb, &info);
277}
278
279/**
280 * bonobo_ui_component_remove_verb_by_func:
281 * @component: the component to add it to
282 * @fn: the function pointer
283 *
284 * remove any verb handled by @fn.
285 **/
286void
287bonobo_ui_component_remove_verb_by_func (BonoboUIComponent  *component,
288                                         BonoboUIVerbFn      fn)
289{
290        RemoveInfo info;
291
292        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
293        g_return_if_fail (component->priv != NULL);
294
295        memset (&info, 0, sizeof (info));
296
297        info.by_func = TRUE;
298        info.func = (gpointer) fn;
299
300        g_hash_table_foreach_remove (component->priv->verbs, remove_verb, &info);
301}
302
303/**
304 * bonobo_ui_component_remove_verb_by_func:
305 * @component: the component to add it to
306 * @user_data: the function pointer
307 *
308 * remove any verb with associated @user_data pointer
309 **/
310void
311bonobo_ui_component_remove_verb_by_data (BonoboUIComponent  *component,
312                                         gpointer            user_data)
313{
314        RemoveInfo info;
315
316        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
317        g_return_if_fail (component->priv != NULL);
318
319        memset (&info, 0, sizeof (info));
320
321        info.by_data = TRUE;
322        info.user_data = user_data;
323
324        g_hash_table_foreach_remove (component->priv->verbs, remove_verb, &info);
325}
326
327/**
328 * bonobo_ui_component_add_listener_full:
329 * @component: the component to add it to
330 * @id: the programmatic name of the id
331 * @fn: the callback function for invoking it
332 * @user_data: the associated user data for the callback
333 * @destroy_fn: a destroy function for the callback data
334 *
335 * Add a listener for stateful events.
336 **/
337void
338bonobo_ui_component_add_listener_full (BonoboUIComponent  *component,
339                                       const char         *id,
340                                       BonoboUIListenerFn  fn,
341                                       gpointer            user_data,
342                                       GDestroyNotify      destroy_fn)
343{
344        UIListener *list;
345        BonoboUIComponentPrivate *priv;
346
347        g_return_if_fail (fn != NULL);
348        g_return_if_fail (id != NULL);
349        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
350        g_return_if_fail (component->priv != NULL);
351
352        priv = component->priv;
353
354        if ((list = g_hash_table_lookup (priv->listeners, id))) {
355                g_hash_table_remove (priv->listeners, id);
356                listener_destroy (NULL, list, NULL);
357        }
358
359        list = g_new (UIListener, 1);
360        list->cb = fn;
361        list->id = g_strdup (id);
362        list->user_data = user_data;
363        list->destroy_fn = destroy_fn;
364
365        g_hash_table_insert (priv->listeners, list->id, list); 
366}
367
368/**
369 * bonobo_ui_component_add_listener:
370 * @component: the component to add it to
371 * @id: the programmatic name of the id
372 * @fn: the callback function for invoking it
373 * @user_data: the associated user data for the callback
374 *
375 * Add a listener for stateful events.
376 **/
377void
378bonobo_ui_component_add_listener (BonoboUIComponent  *component,
379                                  const char         *id,
380                                  BonoboUIListenerFn  fn,
381                                  gpointer            user_data)
382{
383        bonobo_ui_component_add_listener_full (
384                component, id, fn, user_data, NULL);
385}
386
387static gboolean
388remove_listener (gpointer       key,
389                 gpointer       value,
390                 gpointer       user_data)
391{
392        RemoveInfo *info = user_data;
393        UIListener     *listener = value;
394
395        if (info->by_name && info->name &&
396            !strcmp (listener->id, info->name))
397                return listener_destroy (NULL, listener, NULL);
398
399        else if (info->by_func &&
400                 (BonoboUIListenerFn) info->func == listener->cb)
401                return listener_destroy (NULL, listener, NULL);
402
403        else if (info->by_data &&
404                 (BonoboUIListenerFn) info->user_data == listener->user_data)
405                return listener_destroy (NULL, listener, NULL);
406
407        return FALSE;
408}
409
410/**
411 * bonobo_ui_component_remove_listener:
412 * @component: the component to add it to
413 * @cname: the programmatic name of the id
414 *
415 * Remove any listener by its unique id
416 **/
417void
418bonobo_ui_component_remove_listener (BonoboUIComponent  *component,
419                                     const char         *cname)
420{
421        RemoveInfo info;
422
423        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
424        g_return_if_fail (component->priv != NULL);
425
426        memset (&info, 0, sizeof (info));
427
428        info.by_name = TRUE;
429        info.name = cname;
430
431        g_hash_table_foreach_remove (component->priv->listeners, remove_listener, &info);
432}
433
434/**
435 * bonobo_ui_component_remove_by_func:
436 * @component: the component to add it to
437 * @fn: the function pointer
438 *
439 * Remove any listener with associated function @fn
440 **/
441void
442bonobo_ui_component_remove_listener_by_func (BonoboUIComponent  *component,
443                                             BonoboUIListenerFn      fn)
444{
445        RemoveInfo info;
446
447        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
448        g_return_if_fail (component->priv != NULL);
449
450        memset (&info, 0, sizeof (info));
451
452        info.by_func = TRUE;
453        info.func = (gpointer) fn;
454
455        g_hash_table_foreach_remove (component->priv->listeners, remove_listener, &info);
456}
457
458/**
459 * bonobo_ui_component_remove_by_data:
460 * @component: the component to add it to
461 * @user_data: the user_data pointer
462 *
463 * Remove any listener with associated user_data @user_data
464 **/
465void
466bonobo_ui_component_remove_listener_by_data (BonoboUIComponent  *component,
467                                             gpointer            user_data)
468{
469        RemoveInfo info;
470
471        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
472        g_return_if_fail (component->priv != NULL);
473
474        memset (&info, 0, sizeof (info));
475
476        info.by_data = TRUE;
477        info.user_data = user_data;
478
479        g_hash_table_foreach_remove (component->priv->listeners, remove_listener, &info);
480}
481
482static void
483bonobo_ui_component_destroy (GtkObject *object)
484{
485        BonoboUIComponent *comp = (BonoboUIComponent *) object;
486        BonoboUIComponentPrivate *priv = comp->priv;
487
488        if (priv) {
489                g_hash_table_foreach_remove (
490                        priv->verbs, (GHRFunc) verb_destroy, NULL);
491                g_hash_table_destroy (priv->verbs);
492                priv->verbs = NULL;
493
494                g_hash_table_foreach_remove (
495                        priv->listeners,
496                        (GHRFunc) listener_destroy, NULL);
497                g_hash_table_destroy (priv->listeners);
498                priv->listeners = NULL;
499
500                g_free (priv->name);
501
502                g_free (priv);
503        }
504        comp->priv = NULL;
505
506        bonobo_ui_component_parent_class->destroy (object);
507}
508
509/**
510 * bonobo_ui_component_construct:
511 * @ui_component: the UI component itself
512 * @name: the name of the UI component
513 *
514 * Construct the UI component with name @name
515 *
516 * Return value: a constructed UI component or NULL on error
517 **/
518BonoboUIComponent *
519bonobo_ui_component_construct (BonoboUIComponent *ui_component,
520                               const char        *name)
521{
522        g_return_val_if_fail (BONOBO_IS_UI_COMPONENT (ui_component), NULL);
523
524        ui_component->priv->name = g_strdup (name);
525
526        return ui_component;
527}
528
529/**
530 * bonobo_ui_component_new:
531 * @name: the name of the UI component
532 *
533 * Create a new UI component with the specified name
534 *
535 * Return value: a new UI component
536 **/
537BonoboUIComponent *
538bonobo_ui_component_new (const char *name)
539{
540        BonoboUIComponent *component;
541
542        component = gtk_type_new (BONOBO_UI_COMPONENT_TYPE);
543        if (!component)
544                return NULL;
545
546        return BONOBO_UI_COMPONENT (
547                bonobo_ui_component_construct (
548                        component, name));
549}
550
551/**
552 * bonobo_ui_component_new_default:
553 * @void:
554 *
555 * Create a UI component with a unique default name
556 * constructed from various available system properties.
557 *
558 * Return value: a new UI component
559 **/
560BonoboUIComponent *
561bonobo_ui_component_new_default (void)
562{
563        char              *name;
564        BonoboUIComponent *component;
565
566        static int idx = 0;
567
568        name = g_strdup_printf (
569                "%s-%s-%d-%d",
570                gnome_app_id ? gnome_app_id : "unknown",
571                gnome_app_version ? gnome_app_version : "-.-",
572                getpid (), idx++);
573
574        component = bonobo_ui_component_new (name);
575       
576        g_free (name);
577
578        return component;
579}
580
581/**
582 * bonobo_ui_component_set_name:
583 * @component: the UI component
584 * @name: the new name
585 *
586 * Set the @name of the UI @component
587 **/
588void
589bonobo_ui_component_set_name (BonoboUIComponent  *component,
590                              const char         *name)
591{
592        g_return_if_fail (name != NULL);
593        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
594       
595        g_free (component->priv->name);
596        component->priv->name = g_strdup (name);
597}
598
599/**
600 * bonobo_ui_component_get_name:
601 * @component: the UI component
602 *
603 * Return value: the name of the UI @component
604 **/
605const char *
606bonobo_ui_component_get_name (BonoboUIComponent  *component)
607{
608        g_return_val_if_fail (BONOBO_IS_UI_COMPONENT (component), NULL);
609       
610        return component->priv->name;
611}
612
613/**
614 * bonobo_ui_component_set:
615 * @component: the component
616 * @path: the path to set
617 * @xml: the xml to set
618 * @ev: the (optional) CORBA exception environment
619 *
620 * Set the @xml fragment into the remote #BonoboUIContainer's tree
621 * attached to @component at the specified @path
622 *
623 * If you see blank menu items ( or just separators ) it's
624 * likely that you should be using #bonobo_ui_component_set_translate
625 * which substantialy deprecates this routine.
626 **/
627void
628bonobo_ui_component_set (BonoboUIComponent  *component,
629                         const char         *path,
630                         const char         *xml,
631                         CORBA_Environment  *ev)
632{
633        GET_CLASS (component)->xml_set (component, path, xml, ev);
634}
635
636static void
637impl_xml_set (BonoboUIComponent  *component,
638              const char         *path,
639              const char         *xml,
640              CORBA_Environment  *ev)
641{
642        CORBA_Environment *real_ev, tmp_ev;
643        Bonobo_UIContainer container;
644        char              *name;
645
646        g_return_if_fail (xml != NULL);
647        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
648        g_return_if_fail (component->priv != NULL);
649        container = component->priv->container;
650        g_return_if_fail (container != CORBA_OBJECT_NIL);
651
652        if (xml [0] == '\0')
653                return;
654
655        if (ev)
656                real_ev = ev;
657        else {
658                CORBA_exception_init (&tmp_ev);
659                real_ev = &tmp_ev;
660        }
661
662        name = component->priv->name ? component->priv->name : "";
663
664        Bonobo_UIContainer_setNode (container, path, xml,
665                                    name, real_ev);
666
667        if (BONOBO_EX (real_ev) && !ev)
668                g_warning ("Serious exception on node_set '$%s' of '%s' to '%s'",
669                           bonobo_exception_get_text (real_ev), xml, path);
670
671        if (!ev)
672                CORBA_exception_free (&tmp_ev);
673}
674
675/**
676 * bonobo_ui_component_set_tree:
677 * @component: the component
678 * @path: the path to set
679 * @node: the #BonoboUINode representation of an xml tree to set
680 * @ev: the (optional) CORBA exception environment
681 *
682 * Set the @xml fragment into the remote #BonoboUIContainer's tree
683 * attached to @component at the specified @path
684 *
685 * It is likely that you don't want this routine, but want
686 * bonobo_ui_component_set_translate.
687 **/
688void
689bonobo_ui_component_set_tree (BonoboUIComponent *component,
690                              const char        *path,
691                              BonoboUINode      *node,
692                              CORBA_Environment *ev)
693{
694        char *str;
695
696        str = bonobo_ui_node_to_string (node, TRUE);   
697
698/*      fprintf (stderr, "Merging '%s'\n", str); */
699       
700        bonobo_ui_component_set (
701                component, path, str, ev);
702
703        bonobo_ui_node_free_string (str);
704}
705
706/**
707 * bonobo_ui_component_set_translate:
708 * @component: the component
709 * @path: the path to set
710 * @xml: the non translated xml to set
711 * @ev: the (optional) CORBA exception environment
712 *
713 * This routine parses the XML strings, and converts any:
714 * _label="Hello World" type strings into the translated,
715 * and encoded format expected by the remote #BonoboUIContainer.
716 **/
717void
718bonobo_ui_component_set_translate (BonoboUIComponent  *component,
719                                   const char         *path,
720                                   const char         *xml,
721                                   CORBA_Environment  *ev)
722{
723        BonoboUINode *node;
724
725        if (!xml)
726                return;
727
728        node = bonobo_ui_node_from_string (xml);
729
730        bonobo_ui_util_translate_ui (node);
731
732        bonobo_ui_component_set_tree (component, path, node, ev);
733
734        bonobo_ui_node_free (node);
735}
736
737/**
738 * bonobo_ui_component_get:
739 * @component: the component
740 * @path: the path to get
741 * @recurse: whether to get child nodes of @path
742 * @ev: the (optional) CORBA exception environment
743 *
744 * This routine fetches a chunk of the XML tree in the
745 * #BonoboUIContainer associated with @component pointed
746 * to by @path. If @recurse then the child nodes of @path
747 * are returned too, otherwise they are not.
748 *
749 * Return value: an XML string
750 **/
751CORBA_char *
752bonobo_ui_component_get (BonoboUIComponent *component,
753                         const char        *path,
754                         gboolean           recurse,
755                         CORBA_Environment *ev)
756{
757        return GET_CLASS (component)->xml_get (component, path, recurse, ev);
758}
759
760static CORBA_char *
761impl_xml_get (BonoboUIComponent *component,
762              const char        *path,
763              gboolean           recurse,
764              CORBA_Environment *ev)
765{
766        CORBA_Environment *real_ev, tmp_ev;
767        CORBA_char *xml;
768        Bonobo_UIContainer container;
769
770        g_return_val_if_fail (BONOBO_IS_UI_COMPONENT (component), NULL);
771        g_return_val_if_fail (component->priv != NULL, NULL);
772        container = component->priv->container;
773        g_return_val_if_fail (container != CORBA_OBJECT_NIL, NULL);
774
775        if (ev)
776                real_ev = ev;
777        else {
778                CORBA_exception_init (&tmp_ev);
779                real_ev = &tmp_ev;
780        }
781
782        xml = Bonobo_UIContainer_getNode (container, path, !recurse, real_ev);
783
784        if (BONOBO_EX (real_ev)) {
785                if (!ev)
786                        g_warning ("Serious exception getting node '%s' '$%s'",
787                                   path, bonobo_exception_get_text (real_ev));
788
789                if (!ev)
790                        CORBA_exception_free (&tmp_ev);
791
792                return NULL;
793        }
794
795        if (!ev)
796                CORBA_exception_free (&tmp_ev);
797
798        return xml;
799}
800
801/**
802 * bonobo_ui_component_get_tree:
803 * @component: the component
804 * @path: the path to get
805 * @recurse: whether to get child nodes of @path
806 * @ev: the (optional) CORBA exception environment
807 *
808 * This routine fetches a chunk of the XML tree in the
809 * #BonoboUIContainer associated with @component pointed
810 * to by @path. If @recurse then the child nodes of @path
811 * are returned too, otherwise they are not.
812 *
813 * Return value: an #BonoboUINode XML representation
814 **/
815BonoboUINode *
816bonobo_ui_component_get_tree (BonoboUIComponent  *component,
817                              const char         *path,
818                              gboolean            recurse,
819                              CORBA_Environment  *ev)
820{       
821        char *xml;
822        BonoboUINode *node;
823
824        xml = bonobo_ui_component_get (component, path, recurse, ev);
825
826        if (!xml)
827                return NULL;
828
829        node = bonobo_ui_node_from_string (xml);
830
831        CORBA_free (xml);
832
833        if (!node)
834                return NULL;
835
836        return node;
837}
838
839/**
840 * bonobo_ui_component_rm:
841 * @component: the component
842 * @path: the path to set
843 * @ev: the (optional) CORBA exception environment
844 *
845 * This routine removes a chunk of the XML tree in the
846 * #BonoboUIContainer associated with @component pointed
847 * to by @path.
848 **/
849void
850bonobo_ui_component_rm (BonoboUIComponent  *component,
851                        const char         *path,
852                        CORBA_Environment  *ev)
853{
854        GET_CLASS (component)->xml_rm (component, path, ev);
855}
856
857static void
858impl_xml_rm (BonoboUIComponent  *component,
859             const char         *path,
860             CORBA_Environment  *ev)
861{
862        BonoboUIComponentPrivate *priv;
863        CORBA_Environment *real_ev, tmp_ev;
864        Bonobo_UIContainer container;
865
866        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
867        g_return_if_fail (component->priv != NULL);
868        container = component->priv->container;
869        g_return_if_fail (container != CORBA_OBJECT_NIL);
870
871        if (ev)
872                real_ev = ev;
873        else {
874                CORBA_exception_init (&tmp_ev);
875                real_ev = &tmp_ev;
876        }
877
878        priv = component->priv;
879
880        Bonobo_UIContainer_removeNode (
881                container, path, priv->name, real_ev);
882
883        if (!ev && BONOBO_EX (real_ev))
884                g_warning ("Serious exception removing path  '%s' '%s'",
885                           path, bonobo_exception_get_text (real_ev));
886
887        if (!ev)
888                CORBA_exception_free (&tmp_ev);
889}
890
891
892/**
893 * bonobo_ui_component_object_set:
894 * @component: the component
895 * @path: the path to set
896 * @control: a CORBA object reference
897 * @ev: the (optional) CORBA exception environment
898 *
899 * This registers the @control CORBA object into the
900 * #BonoboUIContainer associated with this @component at
901 * the specified @path. This is most often used to associate
902 * controls with a certain path.
903 **/
904void
905bonobo_ui_component_object_set (BonoboUIComponent  *component,
906                                const char         *path,
907                                Bonobo_Unknown      control,
908                                CORBA_Environment  *ev)
909{
910        CORBA_Environment *real_ev, tmp_ev;
911        Bonobo_UIContainer container;
912
913        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
914        g_return_if_fail (component->priv != NULL);
915        container = component->priv->container;
916        g_return_if_fail (container != CORBA_OBJECT_NIL);
917
918        if (ev)
919                real_ev = ev;
920        else {
921                CORBA_exception_init (&tmp_ev);
922                real_ev = &tmp_ev;
923        }
924
925        Bonobo_UIContainer_setObject (container, path, control, real_ev);
926
927        if (!ev && BONOBO_EX (real_ev))
928                g_warning ("Serious exception setting object '%s' '%s'",
929                           path, bonobo_exception_get_text (real_ev));
930
931        if (!ev)
932                CORBA_exception_free (&tmp_ev);
933}
934
935/**
936 * bonobo_ui_component_object_get:
937 * @component: the component
938 * @path: the path to set
939 * @ev: the (optional) CORBA exception environment
940 *
941 * This returns the @control CORBA object registered with the
942 * #BonoboUIContainer associated with this @component at
943 * the specified @path.
944 *
945 * Returns: the associated remote CORBA object.
946 **/
947Bonobo_Unknown
948bonobo_ui_component_object_get (BonoboUIComponent  *component,
949                                const char         *path,
950                                CORBA_Environment  *ev)
951{
952        CORBA_Environment *real_ev, tmp_ev;
953        Bonobo_Unknown     ret;
954        Bonobo_UIContainer container;
955
956        g_return_val_if_fail (BONOBO_IS_UI_COMPONENT (component),
957                              CORBA_OBJECT_NIL);
958        g_return_val_if_fail (component->priv != NULL,
959                              CORBA_OBJECT_NIL);
960        container = component->priv->container;
961        g_return_val_if_fail (container != CORBA_OBJECT_NIL,
962                              CORBA_OBJECT_NIL);
963
964        if (ev)
965                real_ev = ev;
966        else {
967                CORBA_exception_init (&tmp_ev);
968                real_ev = &tmp_ev;
969        }
970
971        ret = Bonobo_UIContainer_getObject (container, path, real_ev);
972
973        if (!ev && BONOBO_EX (real_ev))
974                g_warning ("Serious exception getting object '%s' '%s'",
975                           path, bonobo_exception_get_text (real_ev));
976
977        if (!ev)
978                CORBA_exception_free (&tmp_ev);
979
980        return ret;
981}
982
983/**
984 * bonobo_ui_component_add_verb_list_with_data:
985 * @component: the component
986 * @list: the list of verbs
987 * @user_data: the user data passed to the verb callbacks
988 *
989 * This is a helper function to save registering verbs individualy
990 * it allows registration of a great batch of verbs at one time
991 * in a list of #BonoboUIVerb terminated by #BONOBO_UI_VERB_END
992 **/
993void
994bonobo_ui_component_add_verb_list_with_data (BonoboUIComponent  *component,
995                                             BonoboUIVerb       *list,
996                                             gpointer            user_data)
997{
998        BonoboUIVerb *l;
999
1000        g_return_if_fail (list != NULL);
1001        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
1002
1003        bonobo_object_ref (BONOBO_OBJECT (component));
1004
1005        for (l = list; l && l->cname; l++) {
1006                bonobo_ui_component_add_verb (
1007                        component, l->cname, l->cb,
1008                        user_data?user_data:l->user_data);
1009        }
1010
1011        bonobo_object_unref (BONOBO_OBJECT (component));
1012}
1013
1014/**
1015 * bonobo_ui_component_add_verb_list:
1016 * @component: the component
1017 * @list: the list of verbs.
1018 *
1019 * Add a list of verbs with no associated user_data, you probably
1020 * want #bonobo_ui_component_add_verb_list_with_data
1021 **/
1022void
1023bonobo_ui_component_add_verb_list (BonoboUIComponent  *component,
1024                                   BonoboUIVerb       *list)
1025{
1026        bonobo_ui_component_add_verb_list_with_data (component, list, NULL);
1027}
1028
1029/**
1030 * bonobo_ui_component_freeze:
1031 * @component: the component
1032 * @ev: the (optional) CORBA exception environment
1033 *
1034 * This increments the freeze count on the remote associated
1035 * #BonoboUIContainer, this means that a batch of update operations
1036 * can be performed without a re-render penalty per update.
1037 *
1038 * NB. if your GUI is frozen / not updating you probably have a
1039 * freeze / thaw reference leak/
1040 **/
1041void
1042bonobo_ui_component_freeze (BonoboUIComponent *component,
1043                            CORBA_Environment *ev)
1044{
1045        GET_CLASS (component)->freeze (component, ev);
1046}
1047
1048static void
1049impl_freeze (BonoboUIComponent *component,
1050             CORBA_Environment *ev)
1051{
1052        CORBA_Environment *real_ev, tmp_ev;
1053        Bonobo_UIContainer container;
1054
1055        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
1056        g_return_if_fail (component->priv != NULL);
1057        container = component->priv->container;
1058        g_return_if_fail (container != CORBA_OBJECT_NIL);
1059
1060        if (ev)
1061                real_ev = ev;
1062        else {
1063                CORBA_exception_init (&tmp_ev);
1064                real_ev = &tmp_ev;
1065        }
1066
1067        Bonobo_UIContainer_freeze (container, real_ev);
1068
1069        if (BONOBO_EX (real_ev) && !ev)
1070                g_warning ("Serious exception on UI freeze '$%s'",
1071                           bonobo_exception_get_text (real_ev));
1072
1073        if (!ev)
1074                CORBA_exception_free (&tmp_ev);
1075}
1076
1077/**
1078 * bonobo_ui_component_thaw:
1079 * @component: the component
1080 * @ev: the (optional) CORBA exception environment
1081 *
1082 * This decrements the freeze count on the remote associated
1083 * #BonoboUIContainer, this means that a batch of update operations
1084 * can be performed without a re-render penalty per update.
1085 *
1086 * NB. if your GUI is frozen / not updating you probably have a
1087 * freeze / thaw reference leak/
1088 **/
1089void
1090bonobo_ui_component_thaw (BonoboUIComponent *component,
1091                          CORBA_Environment *ev)
1092{
1093        GET_CLASS (component)->thaw (component, ev);
1094}
1095
1096static void
1097impl_thaw (BonoboUIComponent *component,
1098           CORBA_Environment *ev)
1099{
1100        CORBA_Environment *real_ev, tmp_ev;
1101        Bonobo_UIContainer container;
1102
1103        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
1104        g_return_if_fail (component->priv != NULL);
1105        container = component->priv->container;
1106        g_return_if_fail (container != CORBA_OBJECT_NIL);
1107
1108        if (ev)
1109                real_ev = ev;
1110        else {
1111                CORBA_exception_init (&tmp_ev);
1112                real_ev = &tmp_ev;
1113        }
1114
1115        Bonobo_UIContainer_thaw (container, real_ev);
1116
1117        if (BONOBO_EX (real_ev) && !ev)
1118                g_warning ("Serious exception on UI thaw '$%s'",
1119                           bonobo_exception_get_text (real_ev));
1120
1121        if (!ev)
1122                CORBA_exception_free (&tmp_ev);
1123}
1124
1125/**
1126 * bonobo_ui_component_set_prop:
1127 * @component: the component
1128 * @path: the path to set the property on
1129 * @prop: the property name
1130 * @value: the property value
1131 * @opt_ev: the (optional) CORBA exception environment
1132 *
1133 * This helper function sets an XML property ( or attribute )
1134 * on the XML node pointed at by @path. It does this by
1135 * a read / modify / write process. If you find yourself
1136 * doing this a lot, you need to consider batching this process.
1137 **/
1138void
1139bonobo_ui_component_set_prop (BonoboUIComponent  *component,
1140                              const char         *path,
1141                              const char         *prop,
1142                              const char         *value,
1143                              CORBA_Environment  *opt_ev)
1144{
1145        g_return_if_fail (component != NULL);
1146        g_return_if_fail (component->priv != NULL);
1147
1148        if (prop && (!strcmp (prop, "label") || !strcmp (prop, "tip"))) {
1149                char *encoded = bonobo_ui_util_encode_str (value);
1150                GET_CLASS (component)->set_prop (component, path, prop, encoded, opt_ev);
1151                g_free (encoded);
1152        } else
1153                GET_CLASS (component)->set_prop (component, path, prop, value, opt_ev);
1154}
1155
1156static void
1157impl_set_prop (BonoboUIComponent  *component,
1158               const char         *path,
1159               const char         *prop,
1160               const char         *value,
1161               CORBA_Environment  *opt_ev)
1162{
1163        char *full_path;
1164
1165        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
1166
1167        full_path = alloca (strlen (path) + 1 + strlen (prop) + 1);
1168        strcpy (full_path, path);
1169        strcat (full_path, "#");
1170        strcat (full_path, prop);
1171
1172        bonobo_object_ref (BONOBO_OBJECT (component));
1173
1174        bonobo_ui_component_set (
1175                component, full_path, value, opt_ev);
1176
1177        bonobo_object_unref (BONOBO_OBJECT (component));
1178}
1179
1180/**
1181 * bonobo_ui_component_get_prop:
1182 * @component: the component
1183 * @path: the path to set the property on
1184 * @prop: the property name
1185 * @value: the property value
1186 * @opt_ev: the (optional) CORBA exception environment
1187 *
1188 * This helper function fetches an XML property ( or attribute )
1189 * from the XML node pointed at by @path in the #BonoboUIContainer
1190 * associated with @component
1191 *
1192 * Return value: the xml property value or NULL - free with g_free.
1193 **/
1194gchar *
1195bonobo_ui_component_get_prop (BonoboUIComponent *component,
1196                              const char        *path,
1197                              const char        *prop,
1198                              CORBA_Environment *opt_ev)
1199{
1200        char *txt;
1201
1202        g_return_val_if_fail (component != NULL, NULL);
1203        g_return_val_if_fail (component->priv != NULL, NULL);
1204
1205        txt = GET_CLASS (component)->get_prop (component, path, prop, opt_ev);
1206       
1207        if (prop && (!strcmp (prop, "label") || !strcmp (prop, "tip"))) {
1208                char *decoded;
1209                gboolean err;
1210
1211                decoded = bonobo_ui_util_decode_str (txt, &err);
1212                if (err)
1213                        g_warning ("Encoding error getting prop '%s' at path '%s'",
1214                                   prop, path);
1215                g_free (txt);
1216
1217                return decoded;
1218        } else
1219                return txt;
1220}
1221
1222static gchar *
1223impl_get_prop (BonoboUIComponent *component,
1224               const char        *path,
1225               const char        *prop,
1226               CORBA_Environment *opt_ev)
1227{
1228        char *full_path;
1229        xmlChar *ans;
1230        gchar   *ret;
1231        CORBA_Environment *real_ev, tmp_ev;
1232
1233        g_return_val_if_fail (BONOBO_IS_UI_COMPONENT (component), NULL);
1234        g_return_val_if_fail (component->priv != NULL, NULL);
1235
1236        full_path = alloca (strlen (path) + 1 + strlen (prop) + 1);
1237        strcpy (full_path, path);
1238        strcat (full_path, "#");
1239        strcat (full_path, prop);
1240
1241        bonobo_object_ref (BONOBO_OBJECT (component));
1242
1243        if (opt_ev)
1244                real_ev = opt_ev;
1245        else {
1246                /* Hack to avoid warnings for reading non-existant properties */
1247                CORBA_exception_init (&tmp_ev);
1248                real_ev = &tmp_ev;
1249        }
1250
1251        ans = bonobo_ui_component_get (component, full_path, FALSE, real_ev);
1252
1253        if (ans) {
1254                ret = g_strdup (ans);
1255                CORBA_free (ans);
1256        } else
1257                ret = NULL;
1258
1259        bonobo_object_unref (BONOBO_OBJECT (component));
1260
1261        if (!opt_ev)
1262                CORBA_exception_free (&tmp_ev);
1263       
1264        return ret;
1265}
1266
1267/**
1268 * bonobo_ui_component_path_exists:
1269 * @component: the component
1270 * @path: the path to set the property on
1271 * @ev: the (optional) CORBA exception environment
1272 *
1273 * Return value: TRUE if the path exists in the container.
1274 **/
1275gboolean
1276bonobo_ui_component_path_exists (BonoboUIComponent *component,
1277                                 const char        *path,
1278                                 CORBA_Environment *ev)
1279{
1280        return GET_CLASS (component)->exists (component, path, ev);
1281}
1282
1283static gboolean
1284impl_exists (BonoboUIComponent *component,
1285             const char        *path,
1286             CORBA_Environment *ev)
1287{
1288        gboolean ret;
1289        Bonobo_UIContainer container;
1290        CORBA_Environment *real_ev, tmp_ev;
1291
1292        g_return_val_if_fail (BONOBO_IS_UI_COMPONENT (component), FALSE);
1293        g_return_val_if_fail (component->priv != NULL, FALSE);
1294        container = component->priv->container;
1295        g_return_val_if_fail (container != CORBA_OBJECT_NIL, FALSE);
1296
1297        if (ev)
1298                real_ev = ev;
1299        else {
1300                CORBA_exception_init (&tmp_ev);
1301                real_ev = &tmp_ev;
1302        }
1303
1304        ret = Bonobo_UIContainer_exists (container, path, real_ev);
1305
1306        if (BONOBO_EX (real_ev)) {
1307                ret = FALSE;
1308                if (!ev)
1309                        g_warning ("Serious exception on path_exists '$%s'",
1310                                   bonobo_exception_get_text (real_ev));
1311        }
1312
1313        if (!ev)
1314                CORBA_exception_free (&tmp_ev);
1315
1316        return ret;
1317}
1318
1319/**
1320 * bonobo_ui_component_set_status:
1321 * @component: the component
1322 * @text: the new status text
1323 * @ev: the (optional) CORBA exception environment
1324 *
1325 * This sets the contents of the status bar to @text in the
1326 * remote #BonoboUIContainer associated with @component.
1327 * This is done by setting the contents of the /status/main
1328 * node.
1329 **/
1330void
1331bonobo_ui_component_set_status (BonoboUIComponent *component,
1332                                const char        *text,
1333                                CORBA_Environment *opt_ev)
1334{
1335        if (text == NULL ||
1336            text [0] == '\0') { /* Remove what was there to reveal other msgs */
1337                bonobo_ui_component_rm (component, "/status/main/*", opt_ev);
1338        } else {
1339                char *str, *encoded;
1340
1341                encoded = bonobo_ui_util_encode_str (text);
1342                str = g_strdup_printf ("<item name=\"main\">%s</item>", encoded);
1343                g_free (encoded);
1344               
1345                bonobo_ui_component_set (component, "/status", str, opt_ev);
1346               
1347                g_free (str);
1348        }
1349}
1350
1351/**
1352 * bonobo_ui_component_unset_container:
1353 * @component: the component
1354 *
1355 * This dis-associates the @component from its associated
1356 * #BonoboUIContainer.
1357 **/
1358void
1359bonobo_ui_component_unset_container (BonoboUIComponent *component)
1360{
1361        Bonobo_UIContainer container;
1362
1363        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
1364        g_return_if_fail (component->priv != NULL);
1365
1366        bonobo_object_ref (BONOBO_OBJECT (component));
1367
1368        container = component->priv->container;
1369        component->priv->container = CORBA_OBJECT_NIL;
1370
1371        if (container != CORBA_OBJECT_NIL) {
1372                CORBA_Environment  ev;
1373                char              *name;
1374
1375                CORBA_exception_init (&ev);
1376
1377                name = component->priv->name ? component->priv->name : "";
1378
1379                Bonobo_UIContainer_removeNode (container, "/", name, &ev);
1380                Bonobo_UIContainer_deregisterComponent (container, name, &ev);
1381               
1382                if (BONOBO_EX (&ev))
1383                        g_warning ("Serious exception deregistering component '%s'",
1384                                   bonobo_exception_get_text (&ev));
1385
1386                CORBA_exception_free (&ev);
1387
1388                bonobo_object_release_unref (container, NULL);
1389        }
1390
1391        bonobo_object_unref (BONOBO_OBJECT (component));
1392}
1393
1394/**
1395 * bonobo_ui_component_set_container:
1396 * @component: the component
1397 * @container: a remote container object.
1398 *
1399 * This associates this @component with a remote @container
1400 * object.
1401 **/
1402void
1403bonobo_ui_component_set_container (BonoboUIComponent *component,
1404                                   Bonobo_UIContainer container)
1405{
1406        Bonobo_UIContainer ref_cont;
1407
1408        g_return_if_fail (BONOBO_IS_UI_COMPONENT (component));
1409        g_return_if_fail (component->priv != NULL);
1410
1411        bonobo_object_ref (BONOBO_OBJECT (component));
1412
1413        if (container != CORBA_OBJECT_NIL) {
1414                Bonobo_UIComponent corba_component;
1415                char              *name;
1416                CORBA_Environment  ev;
1417
1418                ref_cont =             
1419                        bonobo_object_dup_ref (container, NULL);
1420
1421                CORBA_exception_init (&ev);
1422
1423                corba_component = BONOBO_OBJREF (component);
1424
1425                name = component->priv->name ? component->priv->name : "";
1426
1427                Bonobo_UIContainer_registerComponent (
1428                        ref_cont, name, corba_component, &ev);
1429
1430                if (BONOBO_EX (&ev))
1431                        g_warning ("Serious exception registering component '$%s'",
1432                                   bonobo_exception_get_text (&ev));
1433               
1434                CORBA_exception_free (&ev);
1435        } else
1436                ref_cont = CORBA_OBJECT_NIL;
1437
1438        bonobo_ui_component_unset_container (component);
1439
1440        component->priv->container = ref_cont;
1441
1442        bonobo_object_unref (BONOBO_OBJECT (component));
1443}
1444
1445/**
1446 * bonobo_ui_component_get_container:
1447 * @component: the component.
1448 *
1449 * Return value: the associated remote container
1450 **/
1451Bonobo_UIContainer
1452bonobo_ui_component_get_container (BonoboUIComponent *component)
1453{
1454        g_return_val_if_fail (BONOBO_IS_UI_COMPONENT (component),
1455                              CORBA_OBJECT_NIL);
1456        g_return_val_if_fail (component->priv != NULL,
1457                              CORBA_OBJECT_NIL);
1458       
1459        return component->priv->container;
1460}
1461
1462static void
1463bonobo_ui_component_class_init (BonoboUIComponentClass *klass)
1464{
1465        GtkObjectClass *object_class = (GtkObjectClass *) klass;
1466        BonoboUIComponentClass *uclass = BONOBO_UI_COMPONENT_CLASS (klass);
1467        POA_Bonobo_UIComponent__epv *epv = &klass->epv;
1468       
1469        bonobo_ui_component_parent_class = gtk_type_class (PARENT_TYPE);
1470
1471        object_class->destroy = bonobo_ui_component_destroy;
1472
1473        uclass->ui_event = ui_event;
1474
1475        signals [EXEC_VERB] = gtk_signal_new (
1476                "exec_verb", GTK_RUN_FIRST,
1477                object_class->type,
1478                GTK_SIGNAL_OFFSET (BonoboUIComponentClass, exec_verb),
1479                gtk_marshal_NONE__STRING,
1480                GTK_TYPE_NONE, 1, GTK_TYPE_STRING);
1481
1482        signals [UI_EVENT] = gtk_signal_new (
1483                "ui_event", GTK_RUN_FIRST,
1484                object_class->type,
1485                GTK_SIGNAL_OFFSET (BonoboUIComponentClass, ui_event),
1486                gtk_marshal_NONE__POINTER_INT_POINTER,
1487                GTK_TYPE_NONE, 3, GTK_TYPE_STRING, GTK_TYPE_INT,
1488                GTK_TYPE_STRING);
1489
1490        gtk_object_class_add_signals (object_class, signals, LAST_SIGNAL);
1491
1492        uclass->freeze   = impl_freeze;
1493        uclass->thaw     = impl_thaw;
1494        uclass->xml_set  = impl_xml_set;
1495        uclass->xml_get  = impl_xml_get;
1496        uclass->xml_rm   = impl_xml_rm;
1497        uclass->set_prop = impl_set_prop;
1498        uclass->get_prop = impl_get_prop;
1499        uclass->exists   = impl_exists;
1500
1501        epv->describeVerbs = impl_Bonobo_UIComponent_describeVerbs;
1502        epv->execVerb      = impl_Bonobo_UIComponent_execVerb;
1503        epv->uiEvent       = impl_Bonobo_UIComponent_uiEvent;
1504}
1505
1506static void
1507bonobo_ui_component_init (BonoboUIComponent *component)
1508{
1509        BonoboUIComponentPrivate *priv;
1510
1511        priv = g_new0 (BonoboUIComponentPrivate, 1);
1512        priv->verbs = g_hash_table_new (g_str_hash, g_str_equal);
1513        priv->listeners = g_hash_table_new (g_str_hash, g_str_equal);
1514        priv->container = CORBA_OBJECT_NIL;
1515
1516        component->priv = priv;
1517}
1518
1519BONOBO_X_TYPE_FUNC_FULL (BonoboUIComponent,
1520                           Bonobo_UIComponent,
1521                           PARENT_TYPE,
1522                           bonobo_ui_component);
Note: See TracBrowser for help on using the repository browser.