forked from AuroraMiddleware/gtk
6b1407a3ec
2001-12-04 Havoc Pennington <hp@redhat.com> * gtk/gtktextbtree.c (gtk_text_btree_remove_tag_info): remove g_assert_not_reached() that was bogus, since we demand-create the tag info. reported by Chris Phelps Jump through assorted hoops to fix bug from Chris Phelps where removing tags from the table resulted in btree trying to access tag->table * gtk/gtktextbuffer.c: set up mechanics of adding/removing ourselves to the tag table * gtk/gtktexttagtable.c (_gtk_text_tag_table_add_buffer) (_gtk_text_tag_table_remove_buffer): private cruft to let us notify buffer of disappearing tags * gtk/gtktexttag.h: remove BTreeNode typedef from this public header, put it in tagprivate * gtk/gtktextbtree.c (_gtk_text_btree_new): don't connect to tag_removed; it's emitted too late. (_gtk_text_btree_notify_will_remove_tag): rename tag_remove_cb to this Padding for ABI-compat expansion * gtk/gtktexttag.h (struct _GtkTextAttributes): pad this (struct _GtkTextAppearance): one pad in here too * gtk/gtktextlayout.h (struct _GtkTextLayoutClass): padding here * gtk/gtktextview.h (struct _GtkTextViewClass): more padding, since action signals etc. seem pretty likely * gtk/gtktextbuffer.h (struct _GtkTextBufferClass): padding * gtk/gtktexttag.h (struct _GtkTextTagClass): padding * gtk/gtktexttagtable.h (struct _GtkTextTagTableClass): padding
406 lines
10 KiB
C
406 lines
10 KiB
C
|
|
#include "gtktexttagtable.h"
|
|
#include "gtkmarshalers.h"
|
|
#include "gtksignal.h"
|
|
#include "gtktextbuffer.h" /* just for the lame notify_will_remove_tag hack */
|
|
|
|
#include <stdlib.h>
|
|
|
|
enum {
|
|
TAG_CHANGED,
|
|
TAG_ADDED,
|
|
TAG_REMOVED,
|
|
LAST_SIGNAL
|
|
};
|
|
|
|
enum {
|
|
LAST_ARG
|
|
};
|
|
|
|
static void gtk_text_tag_table_init (GtkTextTagTable *table);
|
|
static void gtk_text_tag_table_class_init (GtkTextTagTableClass *klass);
|
|
static void gtk_text_tag_table_finalize (GObject *object);
|
|
static void gtk_text_tag_table_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec);
|
|
static void gtk_text_tag_table_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec);
|
|
|
|
static GObjectClass *parent_class = NULL;
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
|
|
|
GType
|
|
gtk_text_tag_table_get_type (void)
|
|
{
|
|
static GType our_type = 0;
|
|
|
|
if (our_type == 0)
|
|
{
|
|
static const GTypeInfo our_info =
|
|
{
|
|
sizeof (GtkTextTagTableClass),
|
|
(GBaseInitFunc) NULL,
|
|
(GBaseFinalizeFunc) NULL,
|
|
(GClassInitFunc) gtk_text_tag_table_class_init,
|
|
NULL, /* class_finalize */
|
|
NULL, /* class_data */
|
|
sizeof (GtkTextTagTable),
|
|
0, /* n_preallocs */
|
|
(GInstanceInitFunc) gtk_text_tag_table_init
|
|
};
|
|
|
|
our_type = g_type_register_static (G_TYPE_OBJECT,
|
|
"GtkTextTagTable",
|
|
&our_info,
|
|
0);
|
|
}
|
|
|
|
return our_type;
|
|
}
|
|
|
|
static void
|
|
gtk_text_tag_table_class_init (GtkTextTagTableClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
parent_class = g_type_class_peek_parent (klass);
|
|
|
|
object_class->set_property = gtk_text_tag_table_set_property;
|
|
object_class->get_property = gtk_text_tag_table_get_property;
|
|
|
|
object_class->finalize = gtk_text_tag_table_finalize;
|
|
|
|
signals[TAG_CHANGED] =
|
|
g_signal_new ("tag_changed",
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GtkTextTagTableClass, tag_changed),
|
|
NULL, NULL,
|
|
_gtk_marshal_VOID__OBJECT_BOOLEAN,
|
|
G_TYPE_NONE,
|
|
2,
|
|
GTK_TYPE_TEXT_TAG,
|
|
G_TYPE_BOOLEAN);
|
|
|
|
signals[TAG_ADDED] =
|
|
g_signal_new ("tag_added",
|
|
GTK_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GtkTextTagTableClass, tag_added),
|
|
NULL, NULL,
|
|
_gtk_marshal_VOID__OBJECT,
|
|
GTK_TYPE_NONE,
|
|
1,
|
|
GTK_TYPE_TEXT_TAG);
|
|
|
|
signals[TAG_REMOVED] =
|
|
g_signal_new ("tag_removed",
|
|
GTK_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_LAST,
|
|
G_STRUCT_OFFSET (GtkTextTagTableClass, tag_removed),
|
|
NULL, NULL,
|
|
_gtk_marshal_VOID__OBJECT,
|
|
GTK_TYPE_NONE,
|
|
1,
|
|
GTK_TYPE_TEXT_TAG);
|
|
}
|
|
|
|
static void
|
|
gtk_text_tag_table_init (GtkTextTagTable *table)
|
|
{
|
|
table->hash = g_hash_table_new (g_str_hash, g_str_equal);
|
|
}
|
|
|
|
/**
|
|
* gtk_text_tag_table_new:
|
|
*
|
|
* Creates a new #GtkTextTagTable. The table contains no tags by
|
|
* default.
|
|
*
|
|
* Return value: a new #GtkTextTagTable
|
|
**/
|
|
GtkTextTagTable*
|
|
gtk_text_tag_table_new (void)
|
|
{
|
|
GtkTextTagTable *table;
|
|
|
|
table = GTK_TEXT_TAG_TABLE (g_object_new (gtk_text_tag_table_get_type (), NULL));
|
|
|
|
return table;
|
|
}
|
|
|
|
static void
|
|
foreach_unref (GtkTextTag *tag, gpointer data)
|
|
{
|
|
/* We don't want to emit the remove signal here; so we just unparent
|
|
* and unref the tag.
|
|
*/
|
|
|
|
tag->table = NULL;
|
|
g_object_unref (G_OBJECT (tag));
|
|
}
|
|
|
|
static void
|
|
gtk_text_tag_table_finalize (GObject *object)
|
|
{
|
|
GtkTextTagTable *table;
|
|
|
|
table = GTK_TEXT_TAG_TABLE (object);
|
|
|
|
gtk_text_tag_table_foreach (table, foreach_unref, NULL);
|
|
|
|
g_hash_table_destroy (table->hash);
|
|
g_slist_free (table->anonymous);
|
|
|
|
g_slist_free (table->buffers);
|
|
|
|
(* G_OBJECT_CLASS (parent_class)->finalize) (object);
|
|
}
|
|
static void
|
|
gtk_text_tag_table_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkTextTagTable *table;
|
|
|
|
table = GTK_TEXT_TAG_TABLE (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
gtk_text_tag_table_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkTextTagTable *table;
|
|
|
|
table = GTK_TEXT_TAG_TABLE (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* gtk_text_tag_table_add:
|
|
* @table: a #GtkTextTagTable
|
|
* @tag: a #GtkTextTag
|
|
*
|
|
* Add a tag to the table. The tag is assigned the highest priority
|
|
* in the table.
|
|
*
|
|
* @tag must not be in a tag table already, and may not have
|
|
* the same name as an already-added tag.
|
|
**/
|
|
void
|
|
gtk_text_tag_table_add (GtkTextTagTable *table,
|
|
GtkTextTag *tag)
|
|
{
|
|
guint size;
|
|
|
|
g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
|
|
g_return_if_fail (GTK_IS_TEXT_TAG (tag));
|
|
g_return_if_fail (tag->table == NULL);
|
|
|
|
if (tag->name && g_hash_table_lookup (table->hash, tag->name))
|
|
{
|
|
g_warning ("A tag named '%s' is already in the tag table.",
|
|
tag->name);
|
|
return;
|
|
}
|
|
|
|
g_object_ref (G_OBJECT (tag));
|
|
|
|
if (tag->name)
|
|
g_hash_table_insert (table->hash, tag->name, tag);
|
|
else
|
|
{
|
|
table->anonymous = g_slist_prepend (table->anonymous, tag);
|
|
table->anon_count += 1;
|
|
}
|
|
|
|
tag->table = table;
|
|
|
|
/* We get the highest tag priority, as the most-recently-added
|
|
tag. Note that we do NOT use gtk_text_tag_set_priority,
|
|
as it assumes the tag is already in the table. */
|
|
size = gtk_text_tag_table_get_size (table);
|
|
g_assert (size > 0);
|
|
tag->priority = size - 1;
|
|
|
|
g_signal_emit (G_OBJECT (table), signals[TAG_ADDED], 0, tag);
|
|
}
|
|
|
|
/**
|
|
* gtk_text_tag_table_lookup:
|
|
* @table: a #GtkTextTagTable
|
|
* @name: name of a tag
|
|
*
|
|
* Look up a named tag.
|
|
*
|
|
* Return value: The tag, or %NULL if none by that name is in the table.
|
|
**/
|
|
GtkTextTag*
|
|
gtk_text_tag_table_lookup (GtkTextTagTable *table,
|
|
const gchar *name)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
return g_hash_table_lookup (table->hash, name);
|
|
}
|
|
|
|
/**
|
|
* gtk_text_tag_table_remove:
|
|
* @table: a #GtkTextTagTable
|
|
* @tag: a #GtkTextTag
|
|
*
|
|
* Remove a tag from the table. This will remove the table's
|
|
* reference to the tag, so be careful - the tag will end
|
|
* up destroyed if you don't have a reference to it.
|
|
**/
|
|
void
|
|
gtk_text_tag_table_remove (GtkTextTagTable *table,
|
|
GtkTextTag *tag)
|
|
{
|
|
GSList *tmp;
|
|
|
|
g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
|
|
g_return_if_fail (GTK_IS_TEXT_TAG (tag));
|
|
g_return_if_fail (tag->table == table);
|
|
|
|
/* Our little bad hack to be sure buffers don't still have the tag
|
|
* applied to text in the buffer
|
|
*/
|
|
tmp = table->buffers;
|
|
while (tmp != NULL)
|
|
{
|
|
_gtk_text_buffer_notify_will_remove_tag (GTK_TEXT_BUFFER (tmp->data),
|
|
tag);
|
|
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
/* Set ourselves to the highest priority; this means
|
|
when we're removed, there won't be any gaps in the
|
|
priorities of the tags in the table. */
|
|
gtk_text_tag_set_priority (tag, gtk_text_tag_table_get_size (table) - 1);
|
|
|
|
tag->table = NULL;
|
|
|
|
if (tag->name)
|
|
g_hash_table_remove (table->hash, tag->name);
|
|
else
|
|
{
|
|
table->anonymous = g_slist_remove (table->anonymous, tag);
|
|
table->anon_count -= 1;
|
|
}
|
|
|
|
g_signal_emit (G_OBJECT (table), signals[TAG_REMOVED], 0, tag);
|
|
|
|
g_object_unref (G_OBJECT (tag));
|
|
}
|
|
|
|
struct ForeachData
|
|
{
|
|
GtkTextTagTableForeach func;
|
|
gpointer data;
|
|
};
|
|
|
|
static void
|
|
hash_foreach (gpointer key, gpointer value, gpointer data)
|
|
{
|
|
struct ForeachData *fd = data;
|
|
|
|
g_return_if_fail (GTK_IS_TEXT_TAG (value));
|
|
|
|
(* fd->func) (value, fd->data);
|
|
}
|
|
|
|
static void
|
|
list_foreach (gpointer data, gpointer user_data)
|
|
{
|
|
struct ForeachData *fd = user_data;
|
|
|
|
g_return_if_fail (GTK_IS_TEXT_TAG (data));
|
|
|
|
(* fd->func) (data, fd->data);
|
|
}
|
|
|
|
/**
|
|
* gtk_text_tag_table_foreach:
|
|
* @table: a #GtkTextTagTable
|
|
* @func: a function to call on each tag
|
|
* @data: user data
|
|
*
|
|
* Calls @func on each tag in @table, with user data @data.
|
|
*
|
|
**/
|
|
void
|
|
gtk_text_tag_table_foreach (GtkTextTagTable *table,
|
|
GtkTextTagTableForeach func,
|
|
gpointer data)
|
|
{
|
|
struct ForeachData d;
|
|
|
|
g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
|
|
g_return_if_fail (func != NULL);
|
|
|
|
d.func = func;
|
|
d.data = data;
|
|
|
|
g_hash_table_foreach (table->hash, hash_foreach, &d);
|
|
g_slist_foreach (table->anonymous, list_foreach, &d);
|
|
}
|
|
|
|
/**
|
|
* gtk_text_tag_table_get_size:
|
|
* @table: a #GtkTextTagTable
|
|
*
|
|
* Returns the size of the table (number of tags)
|
|
*
|
|
* Return value: number of tags in @table
|
|
**/
|
|
gint
|
|
gtk_text_tag_table_get_size (GtkTextTagTable *table)
|
|
{
|
|
g_return_val_if_fail (GTK_IS_TEXT_TAG_TABLE (table), 0);
|
|
|
|
return g_hash_table_size (table->hash) + table->anon_count;
|
|
}
|
|
|
|
void
|
|
_gtk_text_tag_table_add_buffer (GtkTextTagTable *table,
|
|
gpointer buffer)
|
|
{
|
|
g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
|
|
|
|
table->buffers = g_slist_prepend (table->buffers, buffer);
|
|
}
|
|
|
|
void
|
|
_gtk_text_tag_table_remove_buffer (GtkTextTagTable *table,
|
|
gpointer buffer)
|
|
{
|
|
g_return_if_fail (GTK_IS_TEXT_TAG_TABLE (table));
|
|
|
|
table->buffers = g_slist_remove (table->buffers, buffer);
|
|
}
|