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