Merge branch 'async-highlight' into 'master'

gtk-demo: Async highlight

See merge request GNOME/gtk!2435
This commit is contained in:
Matthias Clasen 2020-08-13 21:15:09 +00:00
commit 6191404656
5 changed files with 515 additions and 202 deletions

458
demos/gtk-demo/fontify.c Normal file
View File

@ -0,0 +1,458 @@
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include <gtk/gtk.h>
#include <glib/gstdio.h>
#ifdef HAVE_GIO_UNIX
#include <gio/gunixoutputstream.h>
#include <fcntl.h>
#endif
/* This is the guts of gtk_text_buffer_insert_markup,
* copied here so we can make an incremental version.
*/
static void
insert_tags_for_attributes (GtkTextBuffer *buffer,
PangoAttrIterator *iter,
GtkTextIter *start,
GtkTextIter *end)
{
GtkTextTagTable *table;
PangoAttribute *attr;
GtkTextTag *tag;
char name[256];
table = gtk_text_buffer_get_tag_table (buffer);
#define STRING_ATTR(pango_attr_name, attr_name) \
attr = pango_attr_iterator_get (iter, pango_attr_name); \
if (attr) \
{ \
const char *string = ((PangoAttrString*)attr)->value; \
g_snprintf (name, 256, #attr_name "=%s", string); \
tag = gtk_text_tag_table_lookup (table, name); \
if (!tag) \
{ \
tag = gtk_text_tag_new (name); \
g_object_set (tag, #attr_name, string, NULL); \
gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \
} \
gtk_text_buffer_apply_tag (buffer, tag, start, end); \
}
#define INT_ATTR(pango_attr_name, attr_name) \
attr = pango_attr_iterator_get (iter, pango_attr_name); \
if (attr) \
{ \
int value = ((PangoAttrInt*)attr)->value; \
g_snprintf (name, 256, #attr_name "=%d", value); \
tag = gtk_text_tag_table_lookup (table, name); \
if (!tag) \
{ \
tag = gtk_text_tag_new (name); \
g_object_set (tag, #attr_name, value, NULL); \
gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \
} \
gtk_text_buffer_apply_tag (buffer, tag, start, end); \
}
#define FLOAT_ATTR(pango_attr_name, attr_name) \
attr = pango_attr_iterator_get (iter, pango_attr_name); \
if (attr) \
{ \
float value = ((PangoAttrFloat*)attr)->value; \
g_snprintf (name, 256, #attr_name "=%g", value); \
tag = gtk_text_tag_table_lookup (table, name); \
if (!tag) \
{ \
tag = gtk_text_tag_new (name); \
g_object_set (tag, #attr_name, value, NULL); \
gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \
} \
gtk_text_buffer_apply_tag (buffer, tag, start, end); \
}
#define RGBA_ATTR(pango_attr_name, attr_name) \
attr = pango_attr_iterator_get (iter, pango_attr_name); \
if (attr) \
{ \
PangoColor *color; \
GdkRGBA rgba; \
color = &((PangoAttrColor*)attr)->color; \
rgba.red = color->red / 65535.; \
rgba.green = color->green / 65535.; \
rgba.blue = color->blue / 65535.; \
rgba.alpha = 1.; \
char *str = gdk_rgba_to_string (&rgba); \
g_snprintf (name, 256, #attr_name "=%s", str); \
g_free (str); \
tag = gtk_text_tag_table_lookup (table, name); \
if (!tag) \
{ \
tag = gtk_text_tag_new (name); \
g_object_set (tag, #attr_name, &rgba, NULL); \
gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \
} \
gtk_text_buffer_apply_tag (buffer, tag, start, end); \
}
attr = pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE);
if (attr)
{
const char *language = pango_language_to_string (((PangoAttrLanguage*)attr)->value);
g_snprintf (name, 256, "language=%s", language);
tag = gtk_text_tag_table_lookup (table, name);
if (!tag)
{
tag = gtk_text_tag_new (name);
g_object_set (tag, "language", language, NULL);
gtk_text_tag_table_add (table, tag);
g_object_unref (tag);
}
gtk_text_buffer_apply_tag (buffer, tag, start, end);
}
STRING_ATTR (PANGO_ATTR_FAMILY, family)
INT_ATTR (PANGO_ATTR_STYLE, style)
INT_ATTR (PANGO_ATTR_WEIGHT, weight)
INT_ATTR (PANGO_ATTR_VARIANT, variant)
INT_ATTR (PANGO_ATTR_STRETCH, stretch)
INT_ATTR (PANGO_ATTR_SIZE, size)
attr = pango_attr_iterator_get (iter, PANGO_ATTR_FONT_DESC);
if (attr)
{
PangoFontDescription *desc = ((PangoAttrFontDesc*)attr)->desc;
char *str = pango_font_description_to_string (desc);
g_snprintf (name, 256, "font-desc=%s", str);
g_free (str);
tag = gtk_text_tag_table_lookup (table, name);
if (!tag)
{
tag = gtk_text_tag_new (name);
g_object_set (tag, "font-desc", desc, NULL);
gtk_text_tag_table_add (table, tag);
g_object_unref (tag);
}
gtk_text_buffer_apply_tag (buffer, tag, start, end);
}
RGBA_ATTR (PANGO_ATTR_FOREGROUND, foreground_rgba)
RGBA_ATTR (PANGO_ATTR_BACKGROUND, background_rgba)
INT_ATTR (PANGO_ATTR_UNDERLINE, underline)
RGBA_ATTR (PANGO_ATTR_UNDERLINE_COLOR, underline_rgba)
INT_ATTR (PANGO_ATTR_OVERLINE, overline)
RGBA_ATTR (PANGO_ATTR_OVERLINE_COLOR, overline_rgba)
INT_ATTR (PANGO_ATTR_STRIKETHROUGH, strikethrough)
RGBA_ATTR (PANGO_ATTR_STRIKETHROUGH_COLOR, strikethrough_rgba)
INT_ATTR (PANGO_ATTR_RISE, rise)
FLOAT_ATTR (PANGO_ATTR_SCALE, scale)
INT_ATTR (PANGO_ATTR_FALLBACK, fallback)
INT_ATTR (PANGO_ATTR_LETTER_SPACING, letter_spacing)
STRING_ATTR (PANGO_ATTR_FONT_FEATURES, font_features)
INT_ATTR (PANGO_ATTR_ALLOW_BREAKS, allow_breaks)
INT_ATTR (PANGO_ATTR_SHOW, show_spaces)
INT_ATTR (PANGO_ATTR_INSERT_HYPHENS, insert_hyphens)
}
typedef struct
{
GMarkupParseContext *parser;
char *markup;
gsize pos;
gsize len;
GtkTextBuffer *buffer;
GtkTextIter iter;
GtkTextMark *mark;
PangoAttrList *attributes;
char *text;
PangoAttrIterator *attr;
} MarkupData;
static void
free_markup_data (MarkupData *mdata)
{
g_free (mdata->markup);
g_clear_pointer (&mdata->parser, g_markup_parse_context_free);
gtk_text_buffer_delete_mark (mdata->buffer, mdata->mark);
g_clear_pointer (&mdata->attr, pango_attr_iterator_destroy);
g_clear_pointer (&mdata->attributes, pango_attr_list_unref);
g_free (mdata->text);
g_object_unref (mdata->buffer);
g_free (mdata);
}
static gboolean
insert_markup_idle (gpointer data)
{
MarkupData *mdata = data;
gint64 begin;
begin = g_get_monotonic_time ();
do
{
int start, end;
int start_offset;
GtkTextIter start_iter;
if (g_get_monotonic_time () - begin > G_TIME_SPAN_MILLISECOND)
{
g_idle_add (insert_markup_idle, data);
return G_SOURCE_REMOVE;
}
pango_attr_iterator_range (mdata->attr, &start, &end);
if (end == G_MAXINT) /* last chunk */
end = start - 1; /* resulting in -1 to be passed to _insert */
start_offset = gtk_text_iter_get_offset (&mdata->iter);
gtk_text_buffer_insert (mdata->buffer, &mdata->iter, mdata->text + start, end - start);
gtk_text_buffer_get_iter_at_offset (mdata->buffer, &start_iter, start_offset);
insert_tags_for_attributes (mdata->buffer, mdata->attr, &start_iter, &mdata->iter);
gtk_text_buffer_get_iter_at_mark (mdata->buffer, &mdata->iter, mdata->mark);
}
while (pango_attr_iterator_next (mdata->attr));
free_markup_data (mdata);
return G_SOURCE_REMOVE;
}
static gboolean
parse_markup_idle (gpointer data)
{
MarkupData *mdata = data;
gint64 begin;
GError *error = NULL;
begin = g_get_monotonic_time ();
do {
if (g_get_monotonic_time () - begin > G_TIME_SPAN_MILLISECOND)
{
g_idle_add (parse_markup_idle, data);
return G_SOURCE_REMOVE;
}
if (!g_markup_parse_context_parse (mdata->parser,
mdata->markup + mdata->pos,
MIN (4096, mdata->len - mdata->pos),
&error))
{
g_warning ("Invalid markup string: %s", error->message);
g_error_free (error);
free_markup_data (mdata);
return G_SOURCE_REMOVE;
}
mdata->pos += 4096;
} while (mdata->pos < mdata->len);
if (!pango_markup_parser_finish (mdata->parser,
&mdata->attributes,
&mdata->text,
NULL,
&error))
{
g_warning ("Invalid markup string: %s", error->message);
g_error_free (error);
free_markup_data (mdata);
return G_SOURCE_REMOVE;
}
if (!mdata->attributes)
{
gtk_text_buffer_insert (mdata->buffer, &mdata->iter, mdata->text, -1);
free_markup_data (mdata);
return G_SOURCE_REMOVE;
}
mdata->attr = pango_attr_list_get_iterator (mdata->attributes);
insert_markup_idle (data);
return G_SOURCE_REMOVE;
}
/* Takes a ref on @buffer while it is operating,
* and consumes @markup.
*/
static void
insert_markup (GtkTextBuffer *buffer,
GtkTextIter *iter,
char *markup,
int len)
{
MarkupData *data;
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
data = g_new0 (MarkupData, 1);
data->buffer = g_object_ref (buffer);
data->iter = *iter;
data->markup = markup;
data->len = len;
data->parser = pango_markup_parser_new (0);
data->pos = 0;
/* create mark with right gravity */
data->mark = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
parse_markup_idle (data);
}
static void
fontify_finish (GObject *source,
GAsyncResult *result,
gpointer data)
{
GSubprocess *subprocess = G_SUBPROCESS (source);
GtkTextBuffer *buffer = data;
GBytes *stdout_buf = NULL;
GBytes *stderr_buf = NULL;
GError *error = NULL;
if (!g_subprocess_communicate_finish (subprocess,
result,
&stdout_buf,
&stderr_buf,
&error))
{
g_clear_pointer (&stdout_buf, g_bytes_unref);
g_clear_pointer (&stderr_buf, g_bytes_unref);
g_warning ("%s", error->message);
g_clear_error (&error);
g_object_unref (subprocess);
g_object_unref (buffer);
return;
}
if (g_subprocess_get_exit_status (subprocess) != 0)
{
if (stderr_buf)
g_warning ("%s", (char *)g_bytes_get_data (stderr_buf, NULL));
g_clear_pointer (&stderr_buf, g_bytes_unref);
}
g_object_unref (subprocess);
g_clear_pointer (&stderr_buf, g_bytes_unref);
if (stdout_buf)
{
char *markup;
gsize len;
char *p;
GtkTextIter start;
gtk_text_buffer_set_text (buffer, "", 0);
/* highlight puts a span with font and size around its output,
* which we don't want.
*/
markup = g_bytes_unref_to_data (stdout_buf, &len);
for (p = markup + strlen ("<span "); *p != '>'; p++) *p = ' ';
gtk_text_buffer_get_start_iter (buffer, &start);
insert_markup (buffer, &start, markup, len);
}
g_object_unref (buffer);
}
void
fontify (const char *format,
GtkTextBuffer *source_buffer)
{
GSubprocess *subprocess;
char *format_arg;
GtkSettings *settings;
char *theme;
gboolean prefer_dark;
const char *style_arg;
const char *text;
GtkTextIter start, end;
GBytes *bytes;
GError *error = NULL;
settings = gtk_settings_get_default ();
g_object_get (settings,
"gtk-theme-name", &theme,
"gtk-application-prefer-dark-theme", &prefer_dark,
NULL);
if (prefer_dark || strcmp (theme, "HighContrastInverse") == 0)
style_arg = "--style=edit-vim-dark";
else
style_arg = "--style=edit-kwrite";
g_free (theme);
format_arg = g_strconcat ("--syntax=", format, NULL);
subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDIN_PIPE |
G_SUBPROCESS_FLAGS_STDOUT_PIPE |
G_SUBPROCESS_FLAGS_STDERR_PIPE,
&error,
"highlight",
format_arg,
"--out-format=pango",
style_arg,
NULL);
g_free (format_arg);
if (!subprocess)
{
if (g_error_matches (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT))
{
static gboolean warned = FALSE;
if (!warned)
{
warned = TRUE;
g_message ("For syntax highlighting, install the “highlight” program");
}
}
else
g_warning ("%s", error->message);
g_clear_error (&error);
return;
}
gtk_text_buffer_get_bounds (source_buffer, &start, &end);
text = gtk_text_buffer_get_text (source_buffer, &start, &end, TRUE);
bytes = g_bytes_new_static (text, strlen (text));
#ifdef HAVE_GIO_UNIX
/* Work around https://gitlab.gnome.org/GNOME/glib/-/issues/2182 */
if (G_IS_UNIX_OUTPUT_STREAM (g_subprocess_get_stdin_pipe (subprocess)))
{
GOutputStream *stdin_pipe = g_subprocess_get_stdin_pipe (subprocess);
int fd = g_unix_output_stream_get_fd (G_UNIX_OUTPUT_STREAM (stdin_pipe));
fcntl (fd, F_SETFL, O_NONBLOCK);
}
#endif
g_subprocess_communicate_async (subprocess,
bytes,
NULL,
fontify_finish,
g_object_ref (source_buffer));
}

6
demos/gtk-demo/fontify.h Normal file
View File

@ -0,0 +1,6 @@
#pragma once
#include <gtk/gtk.h>
void fontify (const char *format,
GtkTextBuffer *buffer);

View File

@ -9,6 +9,7 @@
#include <glib/gstdio.h> #include <glib/gstdio.h>
#include "demos.h" #include "demos.h"
#include "fontify.h"
static GtkWidget *info_view; static GtkWidget *info_view;
static GtkWidget *source_view; static GtkWidget *source_view;
@ -234,133 +235,6 @@ activate_run (GSimpleAction *action,
gtk_demo_run (demo, window); gtk_demo_run (demo, window);
} }
static GBytes *
fontify_text (const char *format,
const char *text)
{
GSubprocess *subprocess;
GBytes *stdin_buf;
GBytes *stdout_buf = NULL;
GBytes *stderr_buf = NULL;
GError *error = NULL;
char *format_arg;
GtkSettings *settings;
char *theme;
gboolean prefer_dark;
const char *style_arg;
settings = gtk_settings_get_default ();
g_object_get (settings,
"gtk-theme-name", &theme,
"gtk-application-prefer-dark-theme", &prefer_dark,
NULL);
if (prefer_dark || strcmp (theme, "HighContrastInverse") == 0)
style_arg = "--style=edit-vim-dark";
else
style_arg = "--style=edit-kwrite";
g_free (theme);
format_arg = g_strconcat ("--syntax=", format, NULL);
subprocess = g_subprocess_new (G_SUBPROCESS_FLAGS_STDIN_PIPE |
G_SUBPROCESS_FLAGS_STDOUT_PIPE |
G_SUBPROCESS_FLAGS_STDERR_PIPE,
&error,
"highlight",
format_arg,
"--out-format=pango",
style_arg,
NULL);
g_free (format_arg);
if (!subprocess)
{
if (g_error_matches (error, G_SPAWN_ERROR, G_SPAWN_ERROR_NOENT))
{
static gboolean warned = FALSE;
if (!warned)
{
warned = TRUE;
g_message ("For syntax highlighting, install the “highlight” program");
}
}
else
g_warning ("%s", error->message);
g_clear_error (&error);
return NULL;
}
stdin_buf = g_bytes_new_static (text, strlen (text));
if (!g_subprocess_communicate (subprocess,
stdin_buf,
NULL,
&stdout_buf,
&stderr_buf,
&error))
{
g_clear_pointer (&stdin_buf, g_bytes_unref);
g_clear_pointer (&stdout_buf, g_bytes_unref);
g_clear_pointer (&stderr_buf, g_bytes_unref);
g_warning ("%s", error->message);
g_clear_error (&error);
return NULL;
}
g_bytes_unref (stdin_buf);
if (g_subprocess_get_exit_status (subprocess) != 0)
{
if (stderr_buf)
g_warning ("%s", (char *)g_bytes_get_data (stderr_buf, NULL));
g_clear_pointer (&stdout_buf, g_bytes_unref);
}
g_clear_pointer (&stderr_buf, g_bytes_unref);
g_object_unref (subprocess);
return stdout_buf;
}
void
fontify (const char *format,
GtkTextBuffer *source_buffer)
{
GtkTextIter start, end;
char *text;
GBytes *bytes;
gtk_text_buffer_get_bounds (source_buffer, &start, &end);
text = gtk_text_buffer_get_text (source_buffer, &start, &end, TRUE);
bytes = fontify_text (format, text);
if (bytes)
{
char *markup;
gsize len;
char *p;
markup = g_bytes_unref_to_data (bytes, &len);
/* highlight puts a span with font and size around its output,
* which we don't want.
*/
for (p = markup + strlen ("<span "); *p != '>'; p++) *p = ' ';
gtk_text_buffer_delete (source_buffer, &start, &end);
gtk_text_buffer_insert_markup (source_buffer, &start, markup, len);
g_free (markup);
}
g_free (text);
}
static GtkWidget * static GtkWidget *
display_image (const char *format, display_image (const char *format,
const char *resource, const char *resource,
@ -432,6 +306,8 @@ display_text (const char *format,
GtkTextBuffer *buffer; GtkTextBuffer *buffer;
GtkWidget *textview, *sw; GtkWidget *textview, *sw;
GBytes *bytes; GBytes *bytes;
const char *text;
gsize len;
bytes = g_resources_lookup_data (resource, 0, NULL); bytes = g_resources_lookup_data (resource, 0, NULL);
g_assert (bytes); g_assert (bytes);
@ -454,15 +330,15 @@ display_text (const char *format,
gtk_text_view_set_monospace (GTK_TEXT_VIEW (textview), TRUE); gtk_text_view_set_monospace (GTK_TEXT_VIEW (textview), TRUE);
buffer = gtk_text_buffer_new (NULL); buffer = gtk_text_buffer_new (NULL);
gtk_text_buffer_set_text (buffer, g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
text = g_bytes_unref_to_data (bytes, &len);
gtk_text_buffer_set_text (buffer, text, len);
if (format) if (format)
fontify (format, buffer); fontify (format, buffer);
gtk_text_view_set_buffer (GTK_TEXT_VIEW (textview), buffer); gtk_text_view_set_buffer (GTK_TEXT_VIEW (textview), buffer);
g_bytes_unref (bytes);
sw = gtk_scrolled_window_new (); sw = gtk_scrolled_window_new ();
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC,

View File

@ -93,6 +93,7 @@ demos = files([
gtkdemo_deps = [ libgtk_dep, ] gtkdemo_deps = [ libgtk_dep, ]
extra_demo_sources = files(['main.c', extra_demo_sources = files(['main.c',
'fontify.c',
'gtkfishbowl.c', 'gtkfishbowl.c',
'fontplane.c', 'fontplane.c',
'gtkgears.c', 'gtkgears.c',

View File

@ -1859,28 +1859,6 @@ gtk_text_buffer_insert_with_tags (GtkTextBuffer *buffer,
va_end (args); va_end (args);
} }
static void
gtk_text_buffer_insert_with_tagsv (GtkTextBuffer *buffer,
GtkTextIter *iter,
const char *text,
int len,
GtkTextTag **tags,
int n_tags)
{
int start_offset;
GtkTextIter start;
int i;
start_offset = gtk_text_iter_get_offset (iter);
gtk_text_buffer_insert (buffer, iter, text, len);
gtk_text_buffer_get_iter_at_offset (buffer, &start, start_offset);
for (i = 0; i < n_tags; i++)
gtk_text_buffer_apply_tag (buffer, tags[i], &start, iter);
}
/** /**
* gtk_text_buffer_insert_with_tags_by_name: * gtk_text_buffer_insert_with_tags_by_name:
* @buffer: a #GtkTextBuffer * @buffer: a #GtkTextBuffer
@ -4402,21 +4380,24 @@ _gtk_text_buffer_spew (GtkTextBuffer *buffer)
} }
static void static void
get_tags_for_attributes (PangoAttrIterator *iter, insert_tags_for_attributes (GtkTextBuffer *buffer,
GtkTextTagTable *table, PangoAttrIterator *iter,
GPtrArray *tags) GtkTextIter *start,
GtkTextIter *end)
{ {
GtkTextTagTable *table;
PangoAttribute *attr; PangoAttribute *attr;
GtkTextTag *tag; GtkTextTag *tag;
char name[256]; char name[256];
table = gtk_text_buffer_get_tag_table (buffer);
#define STRING_ATTR(pango_attr_name, attr_name) \ #define STRING_ATTR(pango_attr_name, attr_name) \
attr = pango_attr_iterator_get (iter, pango_attr_name); \ attr = pango_attr_iterator_get (iter, pango_attr_name); \
if (attr) \ if (attr) \
{ \ { \
const char *string = ((PangoAttrString*)attr)->value; \ const char *string = ((PangoAttrString*)attr)->value; \
g_snprintf (name, 256, "%s=%s", #attr_name, string); \ g_snprintf (name, 256, #attr_name "=%s", string); \
tag = gtk_text_tag_table_lookup (table, name); \ tag = gtk_text_tag_table_lookup (table, name); \
if (!tag) \ if (!tag) \
{ \ { \
@ -4425,7 +4406,7 @@ get_tags_for_attributes (PangoAttrIterator *iter,
gtk_text_tag_table_add (table, tag); \ gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \ g_object_unref (tag); \
} \ } \
g_ptr_array_add (tags, tag); \ gtk_text_buffer_apply_tag (buffer, tag, start, end); \
} }
#define INT_ATTR(pango_attr_name, attr_name) \ #define INT_ATTR(pango_attr_name, attr_name) \
@ -4433,7 +4414,7 @@ get_tags_for_attributes (PangoAttrIterator *iter,
if (attr) \ if (attr) \
{ \ { \
int value = ((PangoAttrInt*)attr)->value; \ int value = ((PangoAttrInt*)attr)->value; \
g_snprintf (name, 256, "%s=%d", #attr_name, value); \ g_snprintf (name, 256, #attr_name "=%d", value); \
tag = gtk_text_tag_table_lookup (table, name); \ tag = gtk_text_tag_table_lookup (table, name); \
if (!tag) \ if (!tag) \
{ \ { \
@ -4442,7 +4423,7 @@ get_tags_for_attributes (PangoAttrIterator *iter,
gtk_text_tag_table_add (table, tag); \ gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \ g_object_unref (tag); \
} \ } \
g_ptr_array_add (tags, tag); \ gtk_text_buffer_apply_tag (buffer, tag, start, end); \
} }
#define FLOAT_ATTR(pango_attr_name, attr_name) \ #define FLOAT_ATTR(pango_attr_name, attr_name) \
@ -4450,7 +4431,7 @@ get_tags_for_attributes (PangoAttrIterator *iter,
if (attr) \ if (attr) \
{ \ { \
float value = ((PangoAttrFloat*)attr)->value; \ float value = ((PangoAttrFloat*)attr)->value; \
g_snprintf (name, 256, "%s=%g", #attr_name, value); \ g_snprintf (name, 256, #attr_name "=%g", value); \
tag = gtk_text_tag_table_lookup (table, name); \ tag = gtk_text_tag_table_lookup (table, name); \
if (!tag) \ if (!tag) \
{ \ { \
@ -4459,7 +4440,7 @@ get_tags_for_attributes (PangoAttrIterator *iter,
gtk_text_tag_table_add (table, tag); \ gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \ g_object_unref (tag); \
} \ } \
g_ptr_array_add (tags, tag); \ gtk_text_buffer_apply_tag (buffer, tag, start, end); \
} }
#define RGBA_ATTR(pango_attr_name, attr_name) \ #define RGBA_ATTR(pango_attr_name, attr_name) \
@ -4474,7 +4455,7 @@ get_tags_for_attributes (PangoAttrIterator *iter,
rgba.blue = color->blue / 65535.; \ rgba.blue = color->blue / 65535.; \
rgba.alpha = 1.; \ rgba.alpha = 1.; \
char *str = gdk_rgba_to_string (&rgba); \ char *str = gdk_rgba_to_string (&rgba); \
g_snprintf (name, 256, "%s=%s", #attr_name, str); \ g_snprintf (name, 256, #attr_name "=%s", str); \
g_free (str); \ g_free (str); \
tag = gtk_text_tag_table_lookup (table, name); \ tag = gtk_text_tag_table_lookup (table, name); \
if (!tag) \ if (!tag) \
@ -4484,10 +4465,9 @@ get_tags_for_attributes (PangoAttrIterator *iter,
gtk_text_tag_table_add (table, tag); \ gtk_text_tag_table_add (table, tag); \
g_object_unref (tag); \ g_object_unref (tag); \
} \ } \
g_ptr_array_add (tags, tag); \ gtk_text_buffer_apply_tag (buffer, tag, start, end); \
} }
attr = pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND);
attr = pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE); attr = pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE);
if (attr) if (attr)
{ {
@ -4501,15 +4481,15 @@ get_tags_for_attributes (PangoAttrIterator *iter,
gtk_text_tag_table_add (table, tag); gtk_text_tag_table_add (table, tag);
g_object_unref (tag); g_object_unref (tag);
} }
g_ptr_array_add (tags, tag); gtk_text_buffer_apply_tag (buffer, tag, start, end);
} }
STRING_ATTR(PANGO_ATTR_FAMILY, family) STRING_ATTR (PANGO_ATTR_FAMILY, family)
INT_ATTR(PANGO_ATTR_STYLE, style) INT_ATTR (PANGO_ATTR_STYLE, style)
INT_ATTR(PANGO_ATTR_WEIGHT, weight) INT_ATTR (PANGO_ATTR_WEIGHT, weight)
INT_ATTR(PANGO_ATTR_VARIANT, variant) INT_ATTR (PANGO_ATTR_VARIANT, variant)
INT_ATTR(PANGO_ATTR_STRETCH, stretch) INT_ATTR (PANGO_ATTR_STRETCH, stretch)
INT_ATTR(PANGO_ATTR_SIZE, size) INT_ATTR (PANGO_ATTR_SIZE, size)
attr = pango_attr_iterator_get (iter, PANGO_ATTR_FONT_DESC); attr = pango_attr_iterator_get (iter, PANGO_ATTR_FONT_DESC);
if (attr) if (attr)
@ -4526,25 +4506,25 @@ get_tags_for_attributes (PangoAttrIterator *iter,
gtk_text_tag_table_add (table, tag); gtk_text_tag_table_add (table, tag);
g_object_unref (tag); g_object_unref (tag);
} }
g_ptr_array_add (tags, tag); gtk_text_buffer_apply_tag (buffer, tag, start, end);
} }
RGBA_ATTR(PANGO_ATTR_FOREGROUND, foreground_rgba) RGBA_ATTR (PANGO_ATTR_FOREGROUND, foreground_rgba)
RGBA_ATTR(PANGO_ATTR_BACKGROUND, background_rgba) RGBA_ATTR (PANGO_ATTR_BACKGROUND, background_rgba)
INT_ATTR(PANGO_ATTR_UNDERLINE, underline) INT_ATTR (PANGO_ATTR_UNDERLINE, underline)
RGBA_ATTR(PANGO_ATTR_UNDERLINE_COLOR, underline_rgba) RGBA_ATTR (PANGO_ATTR_UNDERLINE_COLOR, underline_rgba)
INT_ATTR(PANGO_ATTR_OVERLINE, overline) INT_ATTR (PANGO_ATTR_OVERLINE, overline)
RGBA_ATTR(PANGO_ATTR_OVERLINE_COLOR, overline_rgba) RGBA_ATTR (PANGO_ATTR_OVERLINE_COLOR, overline_rgba)
INT_ATTR(PANGO_ATTR_STRIKETHROUGH, strikethrough) INT_ATTR (PANGO_ATTR_STRIKETHROUGH, strikethrough)
RGBA_ATTR(PANGO_ATTR_STRIKETHROUGH_COLOR, strikethrough_rgba) RGBA_ATTR (PANGO_ATTR_STRIKETHROUGH_COLOR, strikethrough_rgba)
INT_ATTR(PANGO_ATTR_RISE, rise) INT_ATTR (PANGO_ATTR_RISE, rise)
FLOAT_ATTR(PANGO_ATTR_SCALE, scale) FLOAT_ATTR (PANGO_ATTR_SCALE, scale)
INT_ATTR(PANGO_ATTR_FALLBACK, fallback) INT_ATTR (PANGO_ATTR_FALLBACK, fallback)
INT_ATTR(PANGO_ATTR_LETTER_SPACING, letter_spacing) INT_ATTR (PANGO_ATTR_LETTER_SPACING, letter_spacing)
STRING_ATTR(PANGO_ATTR_FONT_FEATURES, font_features) STRING_ATTR (PANGO_ATTR_FONT_FEATURES, font_features)
INT_ATTR(PANGO_ATTR_ALLOW_BREAKS, allow_breaks) INT_ATTR (PANGO_ATTR_ALLOW_BREAKS, allow_breaks)
INT_ATTR(PANGO_ATTR_SHOW, show_spaces) INT_ATTR (PANGO_ATTR_SHOW, show_spaces)
INT_ATTR(PANGO_ATTR_INSERT_HYPHENS, insert_hyphens) INT_ATTR (PANGO_ATTR_INSERT_HYPHENS, insert_hyphens)
} }
static void static void
@ -4555,8 +4535,6 @@ gtk_text_buffer_insert_with_attributes (GtkTextBuffer *buffer,
{ {
GtkTextMark *mark; GtkTextMark *mark;
PangoAttrIterator *attr; PangoAttrIterator *attr;
GtkTextTagTable *table;
GPtrArray *tags;
g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer)); g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
@ -4569,33 +4547,28 @@ gtk_text_buffer_insert_with_attributes (GtkTextBuffer *buffer,
/* create mark with right gravity */ /* create mark with right gravity */
mark = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE); mark = gtk_text_buffer_create_mark (buffer, NULL, iter, FALSE);
attr = pango_attr_list_get_iterator (attributes); attr = pango_attr_list_get_iterator (attributes);
table = gtk_text_buffer_get_tag_table (buffer);
tags = g_ptr_array_new ();
do do
{ {
int start, end; int start, end;
int start_offset;
GtkTextIter start_iter;
pango_attr_iterator_range (attr, &start, &end); pango_attr_iterator_range (attr, &start, &end);
if (end == G_MAXINT) /* last chunk */ if (end == G_MAXINT) /* last chunk */
end = start - 1; /* resulting in -1 to be passed to _insert */ end = start - 1; /* resulting in -1 to be passed to _insert */
get_tags_for_attributes (attr, table, tags); start_offset = gtk_text_iter_get_offset (iter);
gtk_text_buffer_insert (buffer, iter, text + start, end - start);
gtk_text_buffer_get_iter_at_offset (buffer, &start_iter, start_offset);
gtk_text_buffer_insert_with_tagsv (buffer, insert_tags_for_attributes (buffer, attr, &start_iter, iter);
iter,
text + start, end - start,
(GtkTextTag **)tags->pdata, tags->len);
g_ptr_array_set_size (tags, 0);
gtk_text_buffer_get_iter_at_mark (buffer, iter, mark); gtk_text_buffer_get_iter_at_mark (buffer, iter, mark);
} }
while (pango_attr_iterator_next (attr)); while (pango_attr_iterator_next (attr));
g_ptr_array_unref (tags);
gtk_text_buffer_delete_mark (buffer, mark); gtk_text_buffer_delete_mark (buffer, mark);
pango_attr_iterator_destroy (attr); pango_attr_iterator_destroy (attr);
} }
@ -4632,7 +4605,6 @@ gtk_text_buffer_insert_markup (GtkTextBuffer *buffer,
gtk_text_buffer_insert_with_attributes (buffer, iter, text, attributes); gtk_text_buffer_insert_with_attributes (buffer, iter, text, attributes);
g_print ("created %d tags\n", gtk_text_tag_table_get_size (gtk_text_buffer_get_tag_table (buffer)));
pango_attr_list_unref (attributes); pango_attr_list_unref (attributes);
g_free (text); g_free (text);
} }