]> Pileus Git - ~andy/gtk/blob - gtk/gtktexttagtable.c
remove g_assert_not_reached() that was bogus, since we demand-create the
[~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   /* 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_slist_free (table->buffers);
159   
160   (* G_OBJECT_CLASS (parent_class)->finalize) (object);
161 }
162 static void
163 gtk_text_tag_table_set_property (GObject      *object,
164                                  guint         prop_id,
165                                  const GValue *value,
166                                  GParamSpec   *pspec)
167 {
168   GtkTextTagTable *table;
169
170   table = GTK_TEXT_TAG_TABLE (object);
171
172   switch (prop_id)
173     {
174
175     default:
176       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
177       break;
178     }
179 }
180
181
182 static void
183 gtk_text_tag_table_get_property (GObject      *object,
184                                  guint         prop_id,
185                                  GValue       *value,
186                                  GParamSpec   *pspec)
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  * @tag must not be in a tag table already, and may not have
210  * the same name as an already-added tag.
211  **/
212 void
213 gtk_text_tag_table_add (GtkTextTagTable *table,
214                         GtkTextTag      *tag)
215 {
216   guint size;
217
218   g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
219   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
220   g_return_if_fail (tag->table == NULL);
221
222   if (tag->name && g_hash_table_lookup (table->hash, tag->name))
223     {
224       g_warning ("A tag named '%s' is already in the tag table.",
225                  tag->name);
226       return;
227     }
228   
229   g_object_ref (G_OBJECT (tag));
230
231   if (tag->name)
232     g_hash_table_insert (table->hash, tag->name, tag);
233   else
234     {
235       table->anonymous = g_slist_prepend (table->anonymous, tag);
236       table->anon_count += 1;
237     }
238
239   tag->table = table;
240
241   /* We get the highest tag priority, as the most-recently-added
242      tag. Note that we do NOT use gtk_text_tag_set_priority,
243      as it assumes the tag is already in the table. */
244   size = gtk_text_tag_table_get_size (table);
245   g_assert (size > 0);
246   tag->priority = size - 1;
247
248   g_signal_emit (G_OBJECT (table), signals[TAG_ADDED], 0, tag);
249 }
250
251 /**
252  * gtk_text_tag_table_lookup:
253  * @table: a #GtkTextTagTable 
254  * @name: name of a tag
255  * 
256  * Look up a named tag.
257  * 
258  * Return value: The tag, or %NULL if none by that name is in the table.
259  **/
260 GtkTextTag*
261 gtk_text_tag_table_lookup (GtkTextTagTable *table,
262                            const gchar     *name)
263 {
264   g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
265   g_return_val_if_fail (name != NULL, NULL);
266
267   return g_hash_table_lookup (table->hash, name);
268 }
269
270 /**
271  * gtk_text_tag_table_remove:
272  * @table: a #GtkTextTagTable
273  * @tag: a #GtkTextTag
274  * 
275  * Remove a tag from the table. This will remove the table's
276  * reference to the tag, so be careful - the tag will end
277  * up destroyed if you don't have a reference to it.
278  **/
279 void
280 gtk_text_tag_table_remove (GtkTextTagTable *table,
281                            GtkTextTag      *tag)
282 {
283   GSList *tmp;
284   
285   g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
286   g_return_if_fail (GTK_IS_TEXT_TAG (tag));
287   g_return_if_fail (tag->table == table);
288
289   /* Our little bad hack to be sure buffers don't still have the tag
290    * applied to text in the buffer
291    */
292   tmp = table->buffers;
293   while (tmp != NULL)
294     {
295       _gtk_text_buffer_notify_will_remove_tag (GTK_TEXT_BUFFER (tmp->data),
296                                                tag);
297       
298       tmp = tmp->next;
299     }
300   
301   /* Set ourselves to the highest priority; this means
302      when we're removed, there won't be any gaps in the
303      priorities of the tags in the table. */
304   gtk_text_tag_set_priority (tag, gtk_text_tag_table_get_size (table) - 1);
305
306   tag->table = NULL;
307
308   if (tag->name)
309     g_hash_table_remove (table->hash, tag->name);
310   else
311     {
312       table->anonymous = g_slist_remove (table->anonymous, tag);
313       table->anon_count -= 1;
314     }
315
316   g_signal_emit (G_OBJECT (table), signals[TAG_REMOVED], 0, tag);
317
318   g_object_unref (G_OBJECT (tag));
319 }
320
321 struct ForeachData
322 {
323   GtkTextTagTableForeach func;
324   gpointer data;
325 };
326
327 static void
328 hash_foreach (gpointer key, gpointer value, gpointer data)
329 {
330   struct ForeachData *fd = data;
331
332   g_return_if_fail (GTK_IS_TEXT_TAG (value));
333
334   (* fd->func) (value, fd->data);
335 }
336
337 static void
338 list_foreach (gpointer data, gpointer user_data)
339 {
340   struct ForeachData *fd = user_data;
341
342   g_return_if_fail (GTK_IS_TEXT_TAG (data));
343
344   (* fd->func) (data, fd->data);
345 }
346
347 /**
348  * gtk_text_tag_table_foreach:
349  * @table: a #GtkTextTagTable
350  * @func: a function to call on each tag
351  * @data: user data
352  *
353  * Calls @func on each tag in @table, with user data @data.
354  * 
355  **/
356 void
357 gtk_text_tag_table_foreach (GtkTextTagTable       *table,
358                             GtkTextTagTableForeach func,
359                             gpointer               data)
360 {
361   struct ForeachData d;
362
363   g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
364   g_return_if_fail (func != NULL);
365
366   d.func = func;
367   d.data = data;
368
369   g_hash_table_foreach (table->hash, hash_foreach, &d);
370   g_slist_foreach (table->anonymous, list_foreach, &d);
371 }
372
373 /**
374  * gtk_text_tag_table_get_size:
375  * @table: a #GtkTextTagTable
376  * 
377  * Returns the size of the table (number of tags)
378  * 
379  * Return value: number of tags in @table
380  **/
381 gint
382 gtk_text_tag_table_get_size (GtkTextTagTable *table)
383 {
384   g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), 0);
385
386   return g_hash_table_size (table->hash) + table->anon_count;
387 }
388
389 void
390 _gtk_text_tag_table_add_buffer (GtkTextTagTable *table,
391                                 gpointer         buffer)
392 {
393   g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
394
395   table->buffers = g_slist_prepend (table->buffers, buffer);
396 }
397
398 void
399 _gtk_text_tag_table_remove_buffer (GtkTextTagTable *table,
400                                    gpointer         buffer)
401 {
402   g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
403
404   table->buffers = g_slist_remove (table->buffers, buffer);
405 }