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