don't write out </apply_tag> for tags that have already been closed by the
2006-08-28 Michael Natterer <mitch@imendio.com> * gtk/gtktextbufferserialize.c (serialize_text): don't write out </apply_tag> for tags that have already been closed by the logic which turns overlapping spans into XML-able trees. Fixes broken XML when there are overlapping tags in the buffer. Also free two leaked GLists and did some cleanup. * tests/Makefile.am * tests/testrichtext.c: new test which creates randomly tagged GtkTextBuffers and serializes/deserializes them.
This commit is contained in:
committed by
Michael Natterer
parent
21ac0b0b40
commit
0fa6144940
12
ChangeLog
12
ChangeLog
@ -1,3 +1,15 @@
|
||||
2006-08-28 Michael Natterer <mitch@imendio.com>
|
||||
|
||||
* gtk/gtktextbufferserialize.c (serialize_text): don't write out
|
||||
</apply_tag> for tags that have already been closed by the logic
|
||||
which turns overlapping spans into XML-able trees. Fixes broken
|
||||
XML when there are overlapping tags in the buffer. Also free two
|
||||
leaked GLists and did some cleanup.
|
||||
|
||||
* tests/Makefile.am
|
||||
* tests/testrichtext.c: new test which creates randomly tagged
|
||||
GtkTextBuffers and serializes/deserializes them.
|
||||
|
||||
2006-08-26 Matthias Clasen <mclasen@redhat.com>
|
||||
|
||||
* gtk/gtkstatusicon.c (gtk_status_icon_reset_image_data):
|
||||
|
||||
@ -434,14 +434,13 @@ serialize_text (GtkTextBuffer *buffer,
|
||||
{
|
||||
GtkTextIter iter, old_iter;
|
||||
GSList *tag_list, *new_tag_list;
|
||||
GQueue *active_tags;
|
||||
int i;
|
||||
GSList *active_tags;
|
||||
|
||||
g_string_append (context->text_str, "<text>");
|
||||
|
||||
iter = context->start;
|
||||
tag_list = NULL;
|
||||
active_tags = g_queue_new ();
|
||||
active_tags = NULL;
|
||||
|
||||
do
|
||||
{
|
||||
@ -453,29 +452,33 @@ serialize_text (GtkTextBuffer *buffer,
|
||||
find_list_delta (tag_list, new_tag_list, &added, &removed);
|
||||
|
||||
/* Handle removed tags */
|
||||
tmp = removed;
|
||||
while (tmp)
|
||||
for (tmp = removed; tmp; tmp = tmp->next)
|
||||
{
|
||||
GtkTextTag *tag = tmp->data;
|
||||
|
||||
g_string_append (context->text_str, "</apply_tag>");
|
||||
/* Only close the tag if we didn't close it before (by using
|
||||
* the stack logic in the while() loop below)
|
||||
*/
|
||||
if (g_slist_find (active_tags, tag))
|
||||
{
|
||||
g_string_append (context->text_str, "</apply_tag>");
|
||||
|
||||
/* We might need to drop some of the tags and re-add them afterwards */
|
||||
while (g_queue_peek_head (active_tags) != tag &&
|
||||
!g_queue_is_empty (active_tags))
|
||||
{
|
||||
added = g_list_prepend (added, g_queue_pop_head (active_tags));
|
||||
g_string_append_printf (context->text_str, "</apply_tag>");
|
||||
}
|
||||
/* Drop all tags that were opened after this one (which are
|
||||
* above this on in the stack)
|
||||
*/
|
||||
while (active_tags->data != tag)
|
||||
{
|
||||
added = g_list_prepend (added, active_tags->data);
|
||||
active_tags = g_slist_remove (active_tags, active_tags->data);
|
||||
g_string_append_printf (context->text_str, "</apply_tag>");
|
||||
}
|
||||
|
||||
g_queue_pop_head (active_tags);
|
||||
|
||||
tmp = tmp->next;
|
||||
active_tags = g_slist_remove (active_tags, active_tags->data);
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle added tags */
|
||||
tmp = added;
|
||||
while (tmp)
|
||||
for (tmp = added; tmp; tmp = tmp->next)
|
||||
{
|
||||
GtkTextTag *tag = tmp->data;
|
||||
gchar *tag_name;
|
||||
@ -505,14 +508,16 @@ serialize_text (GtkTextBuffer *buffer,
|
||||
|
||||
g_string_append_printf (context->text_str, "<apply_tag id=\"%d\">", GPOINTER_TO_INT (tag_id));
|
||||
}
|
||||
g_queue_push_head (active_tags, tag);
|
||||
|
||||
tmp = tmp->next;
|
||||
active_tags = g_slist_prepend (active_tags, tag);
|
||||
}
|
||||
|
||||
g_slist_free (tag_list);
|
||||
tag_list = new_tag_list;
|
||||
|
||||
g_list_free (added);
|
||||
g_list_free (removed);
|
||||
|
||||
old_iter = iter;
|
||||
|
||||
/* Now try to go to either the next tag toggle, or if a pixbuf appears */
|
||||
@ -570,10 +575,10 @@ serialize_text (GtkTextBuffer *buffer,
|
||||
while (!gtk_text_iter_equal (&iter, &context->end));
|
||||
|
||||
/* Close any open tags */
|
||||
for (i = 0; i < g_queue_get_length (active_tags); i++) {
|
||||
for (tag_list = active_tags; tag_list; tag_list = tag_list->next)
|
||||
g_string_append (context->text_str, "</apply_tag>");
|
||||
}
|
||||
g_queue_free (active_tags);
|
||||
|
||||
g_slist_free (active_tags);
|
||||
g_string_append (context->text_str, "</text>\n</text_view_markup>\n");
|
||||
}
|
||||
|
||||
|
||||
@ -57,6 +57,7 @@ noinst_PROGRAMS = \
|
||||
testprint \
|
||||
testrgb \
|
||||
testrecentchooser \
|
||||
testrichtext \
|
||||
testselection \
|
||||
$(testsocket_programs) \
|
||||
testspinbutton \
|
||||
@ -113,6 +114,7 @@ testnouiprint_DEPENDENCIES = $(TEST_DEPS)
|
||||
testprint_DEPENDENCIES = $(TEST_DEPS)
|
||||
testrecentchooser_DEPENDENCIES = $(TEST_DEPS)
|
||||
testrgb_DEPENDENCIES = $(TEST_DEPS)
|
||||
testrichtext_DEPENDENCIES = $(TEST_DEPS)
|
||||
testselection_DEPENDENCIES = $(TEST_DEPS)
|
||||
testsocket_DEPENDENCIES = $(DEPS)
|
||||
testsocket_child_DEPENDENCIES = $(DEPS)
|
||||
@ -163,6 +165,7 @@ testnouiprint_LDADD = $(LDADDS)
|
||||
testprint_LDADD = $(LDADDS)
|
||||
testrecentchooser_LDADD = $(LDADDS)
|
||||
testrgb_LDADD = $(LDADDS)
|
||||
testrichtext_LDADD = $(LDADDS)
|
||||
testselection_LDADD = $(LDADDS)
|
||||
testsocket_LDADD = $(LDADDS)
|
||||
testsocket_child_LDADD = $(LDADDS)
|
||||
|
||||
185
tests/testrichtext.c
Normal file
185
tests/testrichtext.c
Normal file
@ -0,0 +1,185 @@
|
||||
/* testrichtext.c
|
||||
* Copyright (C) 2006 Imendio AB
|
||||
* Authors: Michael Natterer, Tim Janik
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static guint32 quick_rand32_accu = 2147483563;
|
||||
|
||||
static inline guint32
|
||||
quick_rand32 (void)
|
||||
{
|
||||
quick_rand32_accu = 1664525 * quick_rand32_accu + 1013904223;
|
||||
return quick_rand32_accu;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
delete_event (GtkWidget *widget,
|
||||
GdkEventAny *event,
|
||||
gpointer user_data)
|
||||
{
|
||||
gtk_main_quit ();
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
text_tag_enqueue (GtkTextTag *tag,
|
||||
gpointer data)
|
||||
{
|
||||
GSList **slist_p = data;
|
||||
*slist_p = g_slist_prepend (*slist_p, tag);
|
||||
}
|
||||
|
||||
static const gchar *example_text =
|
||||
"vkndsk vfds vkfds vkdsv fdlksnvkfdvnkfdvnkdsnvs\n"
|
||||
"kmvofdmvfdsvkv fdskvnkfdv nnd.mckfdvnknsknvdnvs"
|
||||
"fdlvmfdsvlkfdsmvnskdnvfdsnvf sbskjnvlknfd cvdvnd"
|
||||
"mvlfdsv vfdkjv m, ds vkfdks v df,v j kfds v d\n"
|
||||
"vnfdskv kjvnfv cfdkvndfnvcm fd,vk kdsf vj d\n"
|
||||
"KLJHkjh kjh klhjKLJH Kjh kjl h34kj h34kj3h klj 23 "
|
||||
"kjlkjlhsdjk 34kljh klj hklj 23k4jkjkjh234kjh 52kj "
|
||||
"2h34 sdaf ukklj kjl32l jkkjl 23j jkl ljk23 jkl\n"
|
||||
"hjhjhj2hj23jh jh jk jk2h3 hj kjj jk jh21 jhhj32.";
|
||||
|
||||
static GdkAtom
|
||||
setup_buffer (GtkTextBuffer *buffer)
|
||||
{
|
||||
const guint tlen = strlen (example_text);
|
||||
const guint tcount = 17;
|
||||
GtkTextTag *tags[tcount];
|
||||
GtkTextTagTable *ttable = gtk_text_buffer_get_tag_table (buffer);
|
||||
GSList *node, *slist = NULL;
|
||||
GdkAtom atom;
|
||||
guint i;
|
||||
|
||||
/* cleanup */
|
||||
gtk_text_buffer_set_text (buffer, "", 0);
|
||||
gtk_text_tag_table_foreach (ttable, text_tag_enqueue, &slist);
|
||||
for (node = slist; node; node = node->next)
|
||||
gtk_text_tag_table_remove (ttable, node->data);
|
||||
g_slist_free (slist);
|
||||
|
||||
/* create new tags */
|
||||
for (i = 0; i < tcount; i++)
|
||||
{
|
||||
char *s = g_strdup_printf ("tag%u", i);
|
||||
tags[i] = gtk_text_buffer_create_tag (buffer, s,
|
||||
"weight", quick_rand32() >> 31 ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
|
||||
"style", quick_rand32() >> 31 ? PANGO_STYLE_OBLIQUE : PANGO_STYLE_NORMAL,
|
||||
"underline", quick_rand32() >> 31,
|
||||
NULL);
|
||||
g_free (s);
|
||||
}
|
||||
|
||||
/* assign text and tags */
|
||||
gtk_text_buffer_set_text (buffer, example_text, -1);
|
||||
for (i = 0; i < tcount * 5; i++)
|
||||
{
|
||||
gint a = quick_rand32() % tlen, b = quick_rand32() % tlen;
|
||||
GtkTextIter start, end;
|
||||
gtk_text_buffer_get_iter_at_offset (buffer, &start, MIN (a, b));
|
||||
gtk_text_buffer_get_iter_at_offset (buffer, &end, MAX (a, b));
|
||||
gtk_text_buffer_apply_tag (buffer, tags[i % tcount], &start, &end);
|
||||
}
|
||||
|
||||
/* return serialization format */
|
||||
atom = gtk_text_buffer_register_deserialize_tagset (buffer, NULL);
|
||||
gtk_text_buffer_deserialize_set_can_create_tags (buffer, atom, TRUE);
|
||||
|
||||
return atom;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
test_serialize_deserialize (GtkTextBuffer *buffer,
|
||||
GdkAtom atom,
|
||||
GError **error)
|
||||
{
|
||||
GtkTextIter start, end;
|
||||
guint8 *spew;
|
||||
gsize spew_length;
|
||||
gboolean success;
|
||||
|
||||
gtk_text_buffer_get_bounds (buffer, &start, &end);
|
||||
|
||||
spew = gtk_text_buffer_serialize (buffer, buffer, atom,
|
||||
&start, &end, &spew_length);
|
||||
|
||||
success = gtk_text_buffer_deserialize (buffer, buffer, atom, &end,
|
||||
spew, spew_length, error);
|
||||
|
||||
g_free (spew);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
gint
|
||||
main (gint argc,
|
||||
gchar *argv[])
|
||||
{
|
||||
GtkWidget *window;
|
||||
GtkWidget *sw;
|
||||
GtkWidget *view;
|
||||
GtkTextBuffer *buffer;
|
||||
GdkAtom atom;
|
||||
guint i, broken = 0;
|
||||
|
||||
gtk_init (&argc, &argv);
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_widget_set_size_request (window, 400, 300);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
|
||||
GTK_SHADOW_IN);
|
||||
gtk_container_set_border_width (GTK_CONTAINER (sw), 12);
|
||||
gtk_container_add (GTK_CONTAINER (window), sw);
|
||||
|
||||
g_signal_connect (window, "delete-event",
|
||||
G_CALLBACK (delete_event),
|
||||
NULL);
|
||||
|
||||
buffer = gtk_text_buffer_new (NULL);
|
||||
view = gtk_text_view_new_with_buffer (buffer);
|
||||
g_object_unref (buffer);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (sw), view);
|
||||
|
||||
gtk_widget_show_all (window);
|
||||
if (0)
|
||||
gtk_main ();
|
||||
|
||||
for (i = 0; i < 250; i++)
|
||||
{
|
||||
GError *error = NULL;
|
||||
g_printerr ("creating randomly tagged text buffer with accu=0x%x...\n", quick_rand32_accu);
|
||||
atom = setup_buffer (buffer);
|
||||
if (test_serialize_deserialize (buffer, atom, &error))
|
||||
g_printerr ("ok.\n");
|
||||
else
|
||||
{
|
||||
g_printerr ("FAIL: serialization/deserialization failed:\n %s\n", error->message);
|
||||
broken += 1;
|
||||
}
|
||||
g_clear_error (&error);
|
||||
}
|
||||
|
||||
return broken > 0;
|
||||
}
|
||||
Reference in New Issue
Block a user