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