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