~dbusmenu-team/libdbusmenu/trunk.16.10

« back to all changes in this revision

Viewing changes to libdbusmenu-glib/menuitem.c

Merging in the properties branch to provide some basis to work with.

Show diffs side-by-side

added added

removed removed

Lines of Context:
30
30
#include "config.h"
31
31
#endif
32
32
#include "menuitem.h"
 
33
#include "menuitem-marshal.h"
33
34
 
34
35
/* Private */
35
36
/**
37
38
        @id: The ID of this menu item
38
39
        @children: A list of #DbusmenuMenuitem objects that are
39
40
              children to this one.
 
41
        @properties: All of the properties on this menu item.
40
42
 
41
43
        These are the little secrets that we don't want getting
42
44
        out of data that we have.  They can still be gotten using
47
49
{
48
50
        guint id;
49
51
        GList * children;
50
 
};
 
52
        GHashTable * properties;
 
53
};
 
54
 
 
55
/* Signals */
 
56
enum {
 
57
        PROPERTY_CHANGED,
 
58
        ITEM_ACTIVATED,
 
59
        CHILD_ADDED,
 
60
        CHILD_REMOVED,
 
61
        LAST_SIGNAL
 
62
};
 
63
 
 
64
static guint signals[LAST_SIGNAL] = { 0 };
51
65
 
52
66
/* Properties */
53
67
enum {
81
95
        object_class->set_property = set_property;
82
96
        object_class->get_property = get_property;
83
97
 
 
98
        /**
 
99
                DbusmenuMenuitem::property-changed:
 
100
                @arg0: The #DbusmenuMenuitem object.
 
101
                @arg1: The name of the property that changed
 
102
                @arg2: The new value of the property
 
103
 
 
104
                Emitted everytime a property on a menuitem is either
 
105
                updated or added.
 
106
        */
 
107
        signals[PROPERTY_CHANGED] =   g_signal_new(DBUSMENU_MENUITEM_SIGNAL_PROPERTY_CHANGED,
 
108
                                                   G_TYPE_FROM_CLASS(klass),
 
109
                                                   G_SIGNAL_RUN_LAST,
 
110
                                                   G_STRUCT_OFFSET(DbusmenuMenuitemClass, property_changed),
 
111
                                                   NULL, NULL,
 
112
                                                   _dbusmenu_menuitem_marshal_VOID__STRING_STRING,
 
113
                                                   G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
 
114
        /**
 
115
                DbusmenuMenuitem::item-activated:
 
116
                @arg0: The #DbusmenuMenuitem object.
 
117
 
 
118
                Emitted on the objects on the server side when
 
119
                they are signaled on the client side.
 
120
        */
 
121
        signals[ITEM_ACTIVATED] =   g_signal_new(DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED,
 
122
                                                   G_TYPE_FROM_CLASS(klass),
 
123
                                                   G_SIGNAL_RUN_LAST,
 
124
                                                   G_STRUCT_OFFSET(DbusmenuMenuitemClass, item_activated),
 
125
                                                   NULL, NULL,
 
126
                                                   _dbusmenu_menuitem_marshal_VOID__VOID,
 
127
                                                   G_TYPE_NONE, 0, G_TYPE_NONE);
 
128
        /**
 
129
                DbusmenuMenuitem::child-added:
 
130
                @arg0: The #DbusmenuMenuitem which is the parent.
 
131
                @arg1: The #DbusmenuMenuitem which is the child.
 
132
 
 
133
                Signaled when the child menuitem has been added to
 
134
                the parent.
 
135
        */
 
136
        signals[CHILD_ADDED] =        g_signal_new(DBUSMENU_MENUITEM_SIGNAL_CHILD_ADDED,
 
137
                                                   G_TYPE_FROM_CLASS(klass),
 
138
                                                   G_SIGNAL_RUN_LAST,
 
139
                                                   G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_added),
 
140
                                                   NULL, NULL,
 
141
                                                   _dbusmenu_menuitem_marshal_VOID__OBJECT,
 
142
                                                   G_TYPE_NONE, 2, G_TYPE_OBJECT);
 
143
        /**
 
144
                DbusmenuMenuitem::child-removed:
 
145
                @arg0: The #DbusmenuMenuitem which was the parent.
 
146
                @arg1: The #DbusmenuMenuitem which was the child.
 
147
 
 
148
                Signaled when the child menuitem has been requested to
 
149
                be removed from the parent.  This signal is called when
 
150
                it has been removed from the list but not yet had
 
151
                #g_object_unref called on it.
 
152
        */
 
153
        signals[CHILD_REMOVED] =      g_signal_new(DBUSMENU_MENUITEM_SIGNAL_CHILD_REMOVED,
 
154
                                                   G_TYPE_FROM_CLASS(klass),
 
155
                                                   G_SIGNAL_RUN_LAST,
 
156
                                                   G_STRUCT_OFFSET(DbusmenuMenuitemClass, child_removed),
 
157
                                                   NULL, NULL,
 
158
                                                   _dbusmenu_menuitem_marshal_VOID__OBJECT,
 
159
                                                   G_TYPE_NONE, 2, G_TYPE_OBJECT);
 
160
 
84
161
        g_object_class_install_property (object_class, PROP_ID,
85
162
                                         g_param_spec_uint("id", "ID for the menu item",
86
163
                                                      "This is a unique indentifier for the menu item.",
99
176
 
100
177
        priv->id = 0; 
101
178
        priv->children = NULL;
 
179
 
 
180
        priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
102
181
        
103
182
        return;
104
183
}
114
193
static void
115
194
dbusmenu_menuitem_finalize (GObject *object)
116
195
{
 
196
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(object);
 
197
 
 
198
        if (priv->properties != NULL) {
 
199
                g_hash_table_destroy(priv->properties);
 
200
                priv->properties = NULL;
 
201
        }
117
202
 
118
203
        G_OBJECT_CLASS (dbusmenu_menuitem_parent_class)->finalize (object);
119
204
        return;
181
266
dbusmenu_menuitem_new_with_id (guint id)
182
267
{
183
268
        DbusmenuMenuitem * mi = g_object_new(DBUSMENU_TYPE_MENUITEM, "id", id, NULL);
184
 
        g_debug("New Menuitem id %d goal id %d", dbusmenu_menuitem_get_id(mi), id);
 
269
        /* g_debug("New Menuitem id %d goal id %d", dbusmenu_menuitem_get_id(mi), id); */
185
270
        return mi;
186
271
}
187
272
 
223
308
        return priv->children;
224
309
}
225
310
 
 
311
/* For all the taken children we need to signal
 
312
   that they were removed */
 
313
static void
 
314
take_children_signal (gpointer data, gpointer user_data)
 
315
{
 
316
        g_signal_emit(G_OBJECT(user_data), signals[CHILD_REMOVED], 0, DBUSMENU_MENUITEM(data), TRUE);
 
317
        return;
 
318
}
 
319
 
226
320
/**
227
321
        dbusmenu_menuitem_take_children:
228
322
        @mi: The #DbusmenMenuitem to take the children from.
243
337
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
244
338
        GList * children = priv->children;
245
339
        priv->children = NULL;
 
340
        g_list_foreach(children, take_children_signal, mi);
246
341
        return children;
247
342
}
248
343
 
294
389
 
295
390
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
296
391
        priv->children = g_list_append(priv->children, child);
 
392
        g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, TRUE);
297
393
        return TRUE;
298
394
}
299
395
 
316
412
 
317
413
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
318
414
        priv->children = g_list_remove(priv->children, child);
 
415
        g_signal_emit(G_OBJECT(mi), signals[CHILD_REMOVED], 0, child, TRUE);
319
416
        return TRUE;
320
417
}
321
418
 
339
436
 
340
437
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
341
438
        priv->children = g_list_insert(priv->children, child, position);
 
439
        g_signal_emit(G_OBJECT(mi), signals[CHILD_ADDED], 0, child, TRUE);
342
440
        return TRUE;
343
441
}
344
442
 
371
469
        return NULL;
372
470
}
373
471
 
 
472
typedef struct {
 
473
        DbusmenuMenuitem * mi;
 
474
        guint id;
 
475
} find_id_t;
 
476
 
 
477
/* Basically the heart of the find_id that matches the
 
478
   API of GFunc.  Unfortunately, this goes through all the
 
479
   children, but it rejects them quickly. */
 
480
static void
 
481
find_id_helper (gpointer in_mi, gpointer in_find_id)
 
482
{
 
483
        DbusmenuMenuitem * mi = (DbusmenuMenuitem *)in_mi;
 
484
        find_id_t * find_id = (find_id_t *)in_find_id;
 
485
 
 
486
        if (find_id->mi != NULL) return;
 
487
        if (find_id->id == dbusmenu_menuitem_get_id(mi)) {
 
488
                find_id->mi = mi;
 
489
                return;
 
490
        }
 
491
 
 
492
        g_list_foreach(dbusmenu_menuitem_get_children(mi), find_id_helper, in_find_id);
 
493
        return;
 
494
}
 
495
 
 
496
/**
 
497
        dbusmenu_menuitem_find_id:
 
498
        @mi: #DbusmenuMenuitem at the top of the tree to search
 
499
        @id: ID of the #DbusmenuMenuitem to search for
 
500
 
 
501
        This function searchs the whole tree of children that
 
502
        are attached to @mi.  This could be quite a few nodes, all
 
503
        the way down the tree.  It is a depth first search.
 
504
 
 
505
        Return value: The #DbusmenuMenuitem with the ID of @id
 
506
                or #NULL if there isn't such a menu item in the tree
 
507
                represented by @mi.
 
508
*/
 
509
DbusmenuMenuitem *
 
510
dbusmenu_menuitem_find_id (DbusmenuMenuitem * mi, guint id)
 
511
{
 
512
        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
 
513
        find_id_t find_id = {mi: NULL, id: id};
 
514
        find_id_helper(mi, &find_id);
 
515
        return find_id.mi;
 
516
}
 
517
 
 
518
/**
 
519
        dbusmenu_menuitem_property_set:
 
520
        @mi: The #DbusmenuMenuitem to set the property on.
 
521
        @property: Name of the property to set.
 
522
        @value: The value of the property.
 
523
 
 
524
        Takes the pair of @property and @value and places them as a
 
525
        property on @mi.  If a property already exists by that name,
 
526
        then the value is set to the new value.  If not, the property
 
527
        is added.  If the value is changed or the property was previously
 
528
        unset then the signal #DbusmenuMenuitem::prop-changed will be
 
529
        emitted by this function.
 
530
 
 
531
        Return value:  A boolean representing if the property value was set.
 
532
*/
374
533
gboolean
375
534
dbusmenu_menuitem_property_set (DbusmenuMenuitem * mi, const gchar * property, const gchar * value)
376
535
{
377
 
 
378
 
        return FALSE;
 
536
        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
 
537
        g_return_val_if_fail(property != NULL, FALSE);
 
538
 
 
539
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
 
540
        /* g_debug("Setting a property.  ID: %d  Prop: %s  Value: %s", priv->id, property, value); */
 
541
 
 
542
        gpointer lookup = g_hash_table_lookup(priv->properties, property);
 
543
        if (g_strcmp0((gchar *)lookup, value) == 0) {
 
544
                /* The value is the same as the value currently in the
 
545
                   table so we don't really care.  Just say everything's okay */
 
546
                return TRUE;
 
547
        }
 
548
 
 
549
        gchar * lprop = g_strdup(property);
 
550
        gchar * lval = g_strdup(value);
 
551
 
 
552
        g_hash_table_insert(priv->properties, lprop, lval);
 
553
        g_signal_emit(G_OBJECT(mi), signals[PROPERTY_CHANGED], 0, property, value, TRUE);
 
554
 
 
555
        return TRUE;
379
556
}
380
557
 
 
558
/**
 
559
        dbusmenu_menuitem_property_get:
 
560
        @mi: The #DbusmenuMenuitem to look for the property on.
 
561
        @property: The property to grab.
 
562
 
 
563
        Look up a property on @mi and return the value of it if
 
564
        it exits.  #NULL will be returned if the property doesn't
 
565
        exist.
 
566
 
 
567
        Return value: A string with the value of the property
 
568
                that shouldn't be free'd.  Or #NULL if the property
 
569
                is not set.
 
570
*/
381
571
const gchar *
382
572
dbusmenu_menuitem_property_get (DbusmenuMenuitem * mi, const gchar * property)
383
573
{
384
 
 
385
 
        return NULL;
 
574
        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
 
575
        g_return_val_if_fail(property != NULL, NULL);
 
576
 
 
577
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
 
578
 
 
579
        return (const gchar *)g_hash_table_lookup(priv->properties, property);
386
580
}
387
581
 
 
582
/**
 
583
        dbusmenu_menuitem_property_exit:
 
584
        @mi: The #DbusmenuMenuitem to look for the property on.
 
585
        @property: The property to look for.
 
586
 
 
587
        Checkes to see if a particular property exists on @mi and 
 
588
        returns #TRUE if so.
 
589
 
 
590
        Return value: A boolean checking to see if the property is available
 
591
*/
388
592
gboolean
389
593
dbusmenu_menuitem_property_exist (DbusmenuMenuitem * mi, const gchar * property)
390
594
{
391
 
 
392
 
        return FALSE;
 
595
        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), FALSE);
 
596
        g_return_val_if_fail(property != NULL, FALSE);
 
597
 
 
598
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
 
599
 
 
600
        gpointer value = g_hash_table_lookup(priv->properties, property);
 
601
 
 
602
        return value != NULL;
 
603
}
 
604
 
 
605
/**
 
606
        dbusmenu_menuitem_properties_list:
 
607
        @mi: #DbusmenuMenuitem to list the properties on
 
608
 
 
609
        This functiong gets a list of the names of all the properties
 
610
        that are set on this menu item.  This data on the list is owned
 
611
        by the menuitem but the list is not and should be freed using
 
612
        g_list_free() when the calling function is done with it.
 
613
 
 
614
        Return value: A list of strings or NULL if there are none.
 
615
*/
 
616
GList *
 
617
dbusmenu_menuitem_properties_list (DbusmenuMenuitem * mi)
 
618
{
 
619
        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), NULL);
 
620
 
 
621
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
 
622
        return g_hash_table_get_keys(priv->properties);
 
623
}
 
624
 
 
625
static void
 
626
copy_helper (gpointer in_key, gpointer in_value, gpointer in_data)
 
627
{
 
628
        GHashTable * table = (GHashTable *)in_data;
 
629
        g_hash_table_insert(table, in_key, in_value);
 
630
        return;
 
631
}
 
632
 
 
633
/**
 
634
        dbusmenu_menuitem_properties_copy:
 
635
        @mi: #DbusmenuMenuitem that we're interested in the properties of
 
636
 
 
637
        This function takes the properties of a #DbusmenuMenuitem
 
638
        and puts them into a #GHashTable that is referenced by the
 
639
        key of a string and has the value of a string.  The hash
 
640
        table may not have any entries if there aren't any or there
 
641
        is an error in processing.  It is the caller's responsibility
 
642
        to destroy the created #GHashTable.
 
643
 
 
644
        Return value: A brand new #GHashTable that contains all of the
 
645
                properties that are on this #DbusmenuMenuitem @mi.
 
646
*/
 
647
GHashTable *
 
648
dbusmenu_menuitem_properties_copy (DbusmenuMenuitem * mi)
 
649
{
 
650
        GHashTable * ret = g_hash_table_new(g_str_hash, g_str_equal);
 
651
 
 
652
        g_return_val_if_fail(DBUSMENU_IS_MENUITEM(mi), ret);
 
653
 
 
654
        DbusmenuMenuitemPrivate * priv = DBUSMENU_MENUITEM_GET_PRIVATE(mi);
 
655
        g_hash_table_foreach(priv->properties, copy_helper, ret);
 
656
 
 
657
        return ret;
393
658
}
394
659
 
395
660
/**
422
687
        return;
423
688
}
424
689
 
 
690
typedef struct {
 
691
        void (*func) (DbusmenuMenuitem * mi, gpointer data);
 
692
        gpointer data;
 
693
} foreach_struct_t;
 
694
 
 
695
static void
 
696
foreach_helper (gpointer data, gpointer user_data)
 
697
{
 
698
        dbusmenu_menuitem_foreach(DBUSMENU_MENUITEM(data), ((foreach_struct_t *)user_data)->func, ((foreach_struct_t *)user_data)->data);
 
699
        return;
 
700
}
 
701
 
 
702
/**
 
703
        dbusmenu_menuitem_foreach:
 
704
        @mi: The #DbusmenItem to start from
 
705
        @func: Function to call on every node in the tree
 
706
        @data: User data to pass to the function
 
707
 
 
708
        This calls the function @func on this menu item and all
 
709
        of the children of this item.  And their children.  And
 
710
        their children.  And... you get the point.  It will get
 
711
        called on the whole tree.
 
712
*/
 
713
void
 
714
dbusmenu_menuitem_foreach (DbusmenuMenuitem * mi, void (*func) (DbusmenuMenuitem * mi, gpointer data), gpointer data)
 
715
{
 
716
        g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
 
717
        g_return_if_fail(func != NULL);
 
718
 
 
719
        func(mi, data);
 
720
        GList * children = dbusmenu_menuitem_get_children(mi);
 
721
        foreach_struct_t foreach_data = {func: func, data: data};
 
722
        g_list_foreach(children, foreach_helper, &foreach_data);
 
723
        return;
 
724
}
 
725
 
 
726
/**
 
727
        dbusmenu_menuitem_activate:
 
728
        @mi: The #DbusmenuMenuitem to send the signal on.
 
729
 
 
730
        Emits the #DbusmenuMenuitem::item-activate signal on this
 
731
        menu item.  Called by server objects when they get the
 
732
        appropriate DBus signals from the client.
 
733
*/
 
734
void
 
735
dbusmenu_menuitem_activate (DbusmenuMenuitem * mi)
 
736
{
 
737
        g_return_if_fail(DBUSMENU_IS_MENUITEM(mi));
 
738
        g_signal_emit(G_OBJECT(mi), signals[ITEM_ACTIVATED], 0, TRUE);
 
739
        return;
 
740
}