]> Pileus Git - ~andy/gtk/blob - gtk/gtktexttagtable.c
General cleanup of the log attr iteration stuff. This should make e.g. the
[~andy/gtk] / gtk / gtktexttagtable.c
1
2 #include "gtktexttagtable.h"
3 #include "gtksignal.h"
4
5 #include <stdlib.h>
6
7 enum {
8   TAG_CHANGED,
9   TAG_ADDED,
10   TAG_REMOVED,
11   LAST_SIGNAL
12 };
13
14 enum {
15   LAST_ARG
16 };
17
18 static void gtk_text_tag_table_init         (GtkTextTagTable      *table);
19 static void gtk_text_tag_table_class_init   (GtkTextTagTableClass *klass);
20 static void gtk_text_tag_table_finalize     (GObject              *object);
21 static void gtk_text_tag_table_set_property (GObject              *object,
22                                              guint                 prop_id,
23                                              const GValue         *value,
24                                              GParamSpec           *pspec,
25                                              const gchar          *trailer);
26 static void gtk_text_tag_table_get_property (GObject              *object,
27                                              guint                 prop_id,
28                                              GValue               *value,
29                                              GParamSpec           *pspec,
30                                              const gchar          *trailer);
31
32 static GObjectClass *parent_class = NULL;
33 static guint signals[LAST_SIGNAL] = { 0 };
34
35 GType
36 gtk_text_tag_table_get_type (void)
37 {
38   static GType our_type = 0;
39
40   if (our_type == 0)
41     {
42       static const GTypeInfo our_info =
43       {
44         sizeof (GtkTextTagTableClass),
45         (GBaseInitFunc) NULL,
46         (GBaseFinalizeFunc) NULL,
47         (GClassInitFunc) gtk_text_tag_table_class_init,
48         NULL,           /* class_finalize */
49         NULL,           /* class_data */
50         sizeof (GtkTextTagTable),
51         0,              /* n_preallocs */
52         (GInstanceInitFunc) gtk_text_tag_table_init
53       };
54
55       our_type = g_type_register_static (G_TYPE_OBJECT,
56                                          "GtkTextTagTable",
57                                          &our_info,
58                                          0);
59     }
60
61   return our_type;
62 }
63
64 static void
65 gtk_text_tag_table_class_init (GtkTextTagTableClass *klass)
66 {
67   GObjectClass *object_class = G_OBJECT_CLASS (klass);
68
69   parent_class = g_type_class_peek_parent (klass);
70
71   object_class->set_property = gtk_text_tag_table_set_property;
72   object_class->get_property = gtk_text_tag_table_get_property;
73   
74   object_class->finalize = gtk_text_tag_table_finalize;
75   
76   signals[TAG_CHANGED] =
77     g_signal_newc ("tag_changed",
78                    G_TYPE_FROM_CLASS (object_class),
79                    G_SIGNAL_RUN_LAST,
80                    G_STRUCT_OFFSET (GtkTextTagTableClass, tag_changed),
81                    NULL,
82                    gtk_marshal_VOID__OBJECT_BOOLEAN,
83                    G_TYPE_NONE,
84                    2,
85                    G_TYPE_OBJECT,
86                    G_TYPE_BOOLEAN);  
87
88   signals[TAG_ADDED] =
89     g_signal_newc ("tag_added",
90                    GTK_CLASS_TYPE (object_class),
91                    G_SIGNAL_RUN_LAST,
92                    G_STRUCT_OFFSET (GtkTextTagTableClass, tag_added),
93                    NULL,
94                    gtk_marshal_VOID__OBJECT,
95                    GTK_TYPE_NONE,
96                    1,
97                    G_TYPE_OBJECT);
98
99   signals[TAG_REMOVED] =
100     g_signal_newc ("tag_removed",                   
101                    GTK_CLASS_TYPE (object_class),
102                    G_SIGNAL_RUN_LAST,
103                    G_STRUCT_OFFSET (GtkTextTagTableClass, tag_removed),
104                    NULL,
105                    gtk_marshal_VOID__OBJECT,
106                    GTK_TYPE_NONE,
107                    1,
108                    G_TYPE_OBJECT);
109 }
110
111 void
112 gtk_text_tag_table_init (GtkTextTagTable *table)
113 {
114   table->hash = g_hash_table_new (g_str_hash, g_str_equal);
115 }
116
117 /**
118  * gtk_text_tag_table_new:
119  * 
120  * Creates a new #GtkTextTagTable. The table contains no tags by
121  * default.
122  * 
123  * Return value: a new #GtkTextTagTable
124  **/
125 GtkTextTagTable*
126 gtk_text_tag_table_new (void)
127 {
128   GtkTextTagTable *table;
129
130   table = GTK_TEXT_TAG_TABLE (g_object_new (gtk_text_tag_table_get_type (), NULL));
131
132   return table;
133 }
134
135 static void
136 foreach_unref (GtkTextTag *tag, gpointer data)
137 {
138   /* We don't want to emit the remove signal here; so we just unparent
139    * and unref the tag.
140    */
141
142   tag->table = NULL;
143   g_object_unref (G_OBJECT (tag));
144 }
145
146 static void
147 gtk_text_tag_table_finalize (GObject *object)
148 {
149   GtkTextTagTable *table;
150
151   table = GTK_TEXT_TAG_TABLE (object);
152
153   gtk_text_tag_table_foreach (table, foreach_unref, NULL);
154
155   g_hash_table_destroy (table->hash);
156   g_slist_free (table->anonymous);
157
158   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
159 }
160 static void
161 gtk_text_tag_table_set_property (GObject      *object,
162                                  guint         prop_id,
163                                  const GValue *value,
164                                  GParamSpec   *pspec,
165                                  const gchar  *trailer)
166 {
167   GtkTextTagTable *table;
168
169   table = GTK_TEXT_TAG_TABLE (object);
170
171   switch (prop_id)
172     {
173
174     default:
175       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
176       break;
177     }
178 }
179
180
181 static void
182 gtk_text_tag_table_get_property (GObject      *object,
183                                  guint         prop_id,
184                                  GValue       *value,
185                                  GParamSpec   *pspec,
186                                  const gchar  *trailer)
187 {
188   GtkTextTagTable *table;
189
190   table = GTK_TEXT_TAG_TABLE (object);
191
192   switch (prop_id)
193     {
194
195     default:
196       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
197       break;
198     }
199 }
200
201 /**
202  * gtk_text_tag_table_add:
203  * @table: a #GtkTextTagTable
204  * @tag: a #GtkTextTag
205  *
206  * Add a tag to the table. The tag is assigned the highest priority
207  * in the table.
208  * 
209  **/
210 void
211 gtk_text_tag_table_add (GtkTextTagTable *table,
212                         GtkTextTag      *tag)
213 {
214   guint size;
215
216   g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
217   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
218   g_return_if_fail (tag->name == NULL ||
219                     g_hash_table_lookup (table->hash, tag->name) == NULL);
220   g_return_if_fail (tag->table == NULL);
221
222   g_object_ref (G_OBJECT (tag));
223
224   if (tag->name)
225     g_hash_table_insert (table->hash, tag->name, tag);
226   else
227     {
228       table->anonymous = g_slist_prepend (table->anonymous, tag);
229       table->anon_count += 1;
230     }
231
232   tag->table = table;
233
234   /* We get the highest tag priority, as the most-recently-added
235      tag. Note that we do NOT use gtk_text_tag_set_priority,
236      as it assumes the tag is already in the table. */
237   size = gtk_text_tag_table_size (table);
238   g_assert (size > 0);
239   tag->priority = size - 1;
240
241   g_signal_emit (G_OBJECT (table), signals[TAG_ADDED], 0, tag);
242 }
243
244 /**
245  * gtk_text_tag_table_lookup:
246  * @table: a #GtkTextTagTable 
247  * @name: name of a tag
248  * 
249  * Look up a named tag.
250  * 
251  * Return value: The tag, or %NULL if none by that name is in the table.
252  **/
253 GtkTextTag*
254 gtk_text_tag_table_lookup (GtkTextTagTable *table,
255                            const gchar     *name)
256 {
257   g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
258   g_return_val_if_fail (name != NULL, NULL);
259
260   return g_hash_table_lookup (table->hash, name);
261 }
262
263 /**
264  * gtk_text_tag_table_remove:
265  * @table: a #GtkTextTagTable
266  * @tag: a #GtkTextTag
267  * 
268  * Remove a tag from the table. This will remove the table's
269  * reference to the tag, so be careful - the tag will end
270  * up destroyed if you don't have a reference to it.
271  **/
272 void
273 gtk_text_tag_table_remove (GtkTextTagTable *table,
274                            GtkTextTag      *tag)
275 {
276   g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
277   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
278   g_return_if_fail (tag->table == table);
279
280   /* Set ourselves to the highest priority; this means
281      when we're removed, there won't be any gaps in the
282      priorities of the tags in the table. */
283   gtk_text_tag_set_priority (tag, gtk_text_tag_table_size (table) - 1);
284
285   tag->table = NULL;
286
287   if (tag->name)
288     g_hash_table_remove (table->hash, tag->name);
289   else
290     {
291       table->anonymous = g_slist_remove (table->anonymous, tag);
292       table->anon_count -= 1;
293     }
294
295   g_signal_emit (G_OBJECT (table), signals[TAG_REMOVED], 0, tag);
296
297   g_object_unref (G_OBJECT (tag));
298 }
299
300 struct ForeachData
301 {
302   GtkTextTagTableForeach func;
303   gpointer data;
304 };
305
306 static void
307 hash_foreach (gpointer key, gpointer value, gpointer data)
308 {
309   struct ForeachData *fd = data;
310
311   g_return_if_fail (GTK_IS_TEXT_TAG (value));
312
313   (* fd->func) (value, fd->data);
314 }
315
316 static void
317 list_foreach (gpointer data, gpointer user_data)
318 {
319   struct ForeachData *fd = user_data;
320
321   g_return_if_fail (GTK_IS_TEXT_TAG (data));
322
323   (* fd->func) (data, fd->data);
324 }
325
326 /**
327  * gtk_text_tag_table_foreach:
328  * @table: a #GtkTextTagTable
329  * @func: a function to call on each tag
330  * @data: user data
331  *
332  * Calls @func on each tag in @table, with user data @data.
333  * 
334  **/
335 void
336 gtk_text_tag_table_foreach (GtkTextTagTable       *table,
337                             GtkTextTagTableForeach func,
338                             gpointer               data)
339 {
340   struct ForeachData d;
341
342   g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
343   g_return_if_fail (func != NULL);
344
345   d.func = func;
346   d.data = data;
347
348   g_hash_table_foreach (table->hash, hash_foreach, &d);
349   g_slist_foreach (table->anonymous, list_foreach, &d);
350 }
351
352 /**
353  * gtk_text_tag_table_size:
354  * @table: a #GtkTextTagTable
355  * 
356  * Returns the size of the table (number of tags)
357  * 
358  * Return value: number of tags in @table
359  **/
360 gint
361 gtk_text_tag_table_size (GtkTextTagTable *table)
362 {
363   g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), 0);
364
365   return g_hash_table_size (table->hash) + table->anon_count;
366 }