Change the generated tag names to use #rrggbb notation and compare the colors on an 8 bit basis to make sure the comparison and the tag names exist in the same set of values (otherwise the text buffer gets into an inconsistent state that can even lead to crashes).
1534 lines
41 KiB
C
1534 lines
41 KiB
C
/* GIMP - The GNU Image Manipulation Program
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
|
*
|
|
* GimpTextBuffer
|
|
* Copyright (C) 2010 Michael Natterer <mitch@gimp.org>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <glib/gstdio.h>
|
|
|
|
#include <glib.h>
|
|
|
|
#ifdef G_OS_WIN32
|
|
#include "libgimpbase/gimpwin32-io.h"
|
|
#endif
|
|
|
|
#include <gegl.h>
|
|
#include <gtk/gtk.h>
|
|
|
|
#include "libgimpbase/gimpbase.h"
|
|
#include "libgimpcolor/gimpcolor.h"
|
|
|
|
#include "widgets-types.h"
|
|
|
|
#include "gimptextbuffer.h"
|
|
#include "gimptextbuffer-serialize.h"
|
|
#include "gimptexttag.h"
|
|
#include "gimpwidgets-utils.h"
|
|
|
|
#include "gimp-intl.h"
|
|
|
|
|
|
/* local function prototypes */
|
|
|
|
static GObject * gimp_text_buffer_constructor (GType type,
|
|
guint n_params,
|
|
GObjectConstructParam *params);
|
|
static void gimp_text_buffer_dispose (GObject *object);
|
|
static void gimp_text_buffer_finalize (GObject *object);
|
|
|
|
static void gimp_text_buffer_mark_set (GtkTextBuffer *buffer,
|
|
const GtkTextIter *location,
|
|
GtkTextMark *mark);
|
|
|
|
|
|
G_DEFINE_TYPE (GimpTextBuffer, gimp_text_buffer, GTK_TYPE_TEXT_BUFFER)
|
|
|
|
#define parent_class gimp_text_buffer_parent_class
|
|
|
|
|
|
static void
|
|
gimp_text_buffer_class_init (GimpTextBufferClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
GtkTextBufferClass *buffer_class = GTK_TEXT_BUFFER_CLASS (klass);
|
|
|
|
object_class->constructor = gimp_text_buffer_constructor;
|
|
object_class->dispose = gimp_text_buffer_dispose;
|
|
object_class->finalize = gimp_text_buffer_finalize;
|
|
|
|
buffer_class->mark_set = gimp_text_buffer_mark_set;
|
|
}
|
|
|
|
static void
|
|
gimp_text_buffer_init (GimpTextBuffer *buffer)
|
|
{
|
|
buffer->markup_atom =
|
|
gtk_text_buffer_register_serialize_format (GTK_TEXT_BUFFER (buffer),
|
|
"application/x-gimp-pango-markup",
|
|
gimp_text_buffer_serialize,
|
|
NULL, NULL);
|
|
|
|
gtk_text_buffer_register_deserialize_format (GTK_TEXT_BUFFER (buffer),
|
|
"application/x-gimp-pango-markup",
|
|
gimp_text_buffer_deserialize,
|
|
NULL, NULL);
|
|
}
|
|
|
|
static GObject *
|
|
gimp_text_buffer_constructor (GType type,
|
|
guint n_params,
|
|
GObjectConstructParam *params)
|
|
{
|
|
GObject *object;
|
|
GimpTextBuffer *buffer;
|
|
|
|
object = G_OBJECT_CLASS (parent_class)->constructor (type, n_params, params);
|
|
|
|
buffer = GIMP_TEXT_BUFFER (object);
|
|
|
|
gtk_text_buffer_set_text (GTK_TEXT_BUFFER (buffer), "", -1);
|
|
|
|
buffer->bold_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
"bold",
|
|
"weight", PANGO_WEIGHT_BOLD,
|
|
NULL);
|
|
|
|
buffer->italic_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
"italic",
|
|
"style", PANGO_STYLE_ITALIC,
|
|
NULL);
|
|
|
|
buffer->underline_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
"underline",
|
|
"underline", PANGO_UNDERLINE_SINGLE,
|
|
NULL);
|
|
|
|
buffer->strikethrough_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
"strikethrough",
|
|
"strikethrough", TRUE,
|
|
NULL);
|
|
|
|
return object;
|
|
}
|
|
|
|
static void
|
|
gimp_text_buffer_dispose (GObject *object)
|
|
{
|
|
/* GimpTextBuffer *buffer = GIMP_TEXT_BUFFER (object); */
|
|
|
|
G_OBJECT_CLASS (parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gimp_text_buffer_finalize (GObject *object)
|
|
{
|
|
GimpTextBuffer *buffer = GIMP_TEXT_BUFFER (object);
|
|
|
|
if (buffer->size_tags)
|
|
{
|
|
g_list_free (buffer->size_tags);
|
|
buffer->size_tags = NULL;
|
|
}
|
|
|
|
if (buffer->baseline_tags)
|
|
{
|
|
g_list_free (buffer->baseline_tags);
|
|
buffer->baseline_tags = NULL;
|
|
}
|
|
|
|
if (buffer->kerning_tags)
|
|
{
|
|
g_list_free (buffer->kerning_tags);
|
|
buffer->kerning_tags = NULL;
|
|
}
|
|
|
|
if (buffer->font_tags)
|
|
{
|
|
g_list_free (buffer->font_tags);
|
|
buffer->font_tags = NULL;
|
|
}
|
|
|
|
if (buffer->color_tags)
|
|
{
|
|
g_list_free (buffer->color_tags);
|
|
buffer->color_tags = NULL;
|
|
}
|
|
|
|
gimp_text_buffer_clear_insert_tags (buffer);
|
|
|
|
G_OBJECT_CLASS (parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gimp_text_buffer_mark_set (GtkTextBuffer *buffer,
|
|
const GtkTextIter *location,
|
|
GtkTextMark *mark)
|
|
{
|
|
gimp_text_buffer_clear_insert_tags (GIMP_TEXT_BUFFER (buffer));
|
|
|
|
GTK_TEXT_BUFFER_CLASS (parent_class)->mark_set (buffer, location, mark);
|
|
}
|
|
|
|
|
|
/* public functions */
|
|
|
|
GimpTextBuffer *
|
|
gimp_text_buffer_new (void)
|
|
{
|
|
return g_object_new (GIMP_TYPE_TEXT_BUFFER, NULL);
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_set_text (GimpTextBuffer *buffer,
|
|
const gchar *text)
|
|
{
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
|
|
if (text == NULL)
|
|
text = "";
|
|
|
|
gtk_text_buffer_set_text (GTK_TEXT_BUFFER (buffer), text, -1);
|
|
|
|
gimp_text_buffer_clear_insert_tags (buffer);
|
|
}
|
|
|
|
gchar *
|
|
gimp_text_buffer_get_text (GimpTextBuffer *buffer)
|
|
{
|
|
GtkTextIter start, end;
|
|
|
|
g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), NULL);
|
|
|
|
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
|
|
|
|
return gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer),
|
|
&start, &end, TRUE);
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_set_markup (GimpTextBuffer *buffer,
|
|
const gchar *markup)
|
|
{
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
|
|
gimp_text_buffer_set_text (buffer, NULL);
|
|
|
|
if (markup)
|
|
{
|
|
GtkTextTagTable *tag_table;
|
|
GtkTextBuffer *content;
|
|
GtkTextIter insert;
|
|
GError *error = NULL;
|
|
|
|
tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer));
|
|
content = gtk_text_buffer_new (tag_table);
|
|
|
|
gtk_text_buffer_get_start_iter (content, &insert);
|
|
|
|
if (! gtk_text_buffer_deserialize (GTK_TEXT_BUFFER (buffer),
|
|
content,
|
|
buffer->markup_atom,
|
|
&insert,
|
|
(const guint8 *) markup, -1,
|
|
&error))
|
|
{
|
|
g_printerr ("EEK: %s\n", error->message);
|
|
g_clear_error (&error);
|
|
}
|
|
else
|
|
{
|
|
GtkTextIter start, end;
|
|
|
|
gimp_text_buffer_post_deserialize (buffer, content);
|
|
|
|
gtk_text_buffer_get_bounds (content, &start, &end);
|
|
gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &insert);
|
|
|
|
gtk_text_buffer_insert_range (GTK_TEXT_BUFFER (buffer),
|
|
&insert, &start, &end);
|
|
}
|
|
|
|
g_object_unref (content);
|
|
}
|
|
|
|
gimp_text_buffer_clear_insert_tags (buffer);
|
|
}
|
|
|
|
gchar *
|
|
gimp_text_buffer_get_markup (GimpTextBuffer *buffer)
|
|
{
|
|
GtkTextTagTable *tag_table;
|
|
GtkTextBuffer *content;
|
|
GtkTextIter insert;
|
|
GtkTextIter start, end;
|
|
gchar *markup;
|
|
gsize length;
|
|
|
|
g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), NULL);
|
|
|
|
tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer));
|
|
content = gtk_text_buffer_new (tag_table);
|
|
|
|
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
|
|
gtk_text_buffer_get_start_iter (content, &insert);
|
|
|
|
gtk_text_buffer_insert_range (content, &insert, &start, &end);
|
|
|
|
gimp_text_buffer_pre_serialize (buffer, content);
|
|
|
|
gtk_text_buffer_get_bounds (content, &start, &end);
|
|
|
|
markup = (gchar *) gtk_text_buffer_serialize (GTK_TEXT_BUFFER (buffer),
|
|
content,
|
|
buffer->markup_atom,
|
|
&start, &end,
|
|
&length);
|
|
|
|
g_object_unref (content);
|
|
|
|
return markup;
|
|
}
|
|
|
|
gboolean
|
|
gimp_text_buffer_has_markup (GimpTextBuffer *buffer)
|
|
{
|
|
GtkTextIter iter;
|
|
|
|
g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), FALSE);
|
|
|
|
gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
|
|
|
|
do
|
|
{
|
|
GSList *tags = gtk_text_iter_get_tags (&iter);
|
|
|
|
if (tags)
|
|
{
|
|
g_slist_free (tags);
|
|
return TRUE;
|
|
}
|
|
}
|
|
while (gtk_text_iter_forward_char (&iter));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
GtkTextTag *
|
|
gimp_text_buffer_get_iter_size (GimpTextBuffer *buffer,
|
|
const GtkTextIter *iter,
|
|
gint *size)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = buffer->size_tags; list; list = g_list_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
if (gtk_text_iter_has_tag (iter, tag))
|
|
{
|
|
if (size)
|
|
*size = gimp_text_tag_get_size (tag);
|
|
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
if (size)
|
|
*size = 0;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GtkTextTag *
|
|
gimp_text_buffer_get_size_tag (GimpTextBuffer *buffer,
|
|
gint size)
|
|
{
|
|
GList *list;
|
|
GtkTextTag *tag;
|
|
gchar name[32];
|
|
|
|
for (list = buffer->size_tags; list; list = g_list_next (list))
|
|
{
|
|
tag = list->data;
|
|
|
|
if (size == gimp_text_tag_get_size (tag))
|
|
return tag;
|
|
}
|
|
|
|
g_snprintf (name, sizeof (name), "size-%d", size);
|
|
|
|
tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
name,
|
|
GIMP_TEXT_PROP_NAME_SIZE, size,
|
|
NULL);
|
|
|
|
buffer->size_tags = g_list_prepend (buffer->size_tags, tag);
|
|
|
|
return tag;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_set_size (GimpTextBuffer *buffer,
|
|
const GtkTextIter *start,
|
|
const GtkTextIter *end,
|
|
gint size)
|
|
{
|
|
GList *list;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
g_return_if_fail (start != NULL);
|
|
g_return_if_fail (end != NULL);
|
|
|
|
if (gtk_text_iter_equal (start, end))
|
|
return;
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
for (list = buffer->size_tags; list; list = g_list_next (list))
|
|
{
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
|
|
start, end);
|
|
}
|
|
|
|
if (size != 0)
|
|
{
|
|
GtkTextTag *tag;
|
|
|
|
tag = gimp_text_buffer_get_size_tag (buffer, size);
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
|
|
start, end);
|
|
}
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_change_size (GimpTextBuffer *buffer,
|
|
const GtkTextIter *start,
|
|
const GtkTextIter *end,
|
|
gint count)
|
|
{
|
|
GtkTextIter iter;
|
|
GtkTextIter span_start;
|
|
GtkTextIter span_end;
|
|
GtkTextTag *span_tag;
|
|
gint span_size;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
g_return_if_fail (start != NULL);
|
|
g_return_if_fail (end != NULL);
|
|
|
|
if (gtk_text_iter_equal (start, end))
|
|
return;
|
|
|
|
iter = *start;
|
|
span_start = *start;
|
|
span_tag = gimp_text_buffer_get_iter_size (buffer, &iter,
|
|
&span_size);
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
do
|
|
{
|
|
GtkTextTag *iter_tag;
|
|
gint iter_size;
|
|
|
|
gtk_text_iter_forward_char (&iter);
|
|
|
|
iter_tag = gimp_text_buffer_get_iter_size (buffer, &iter,
|
|
&iter_size);
|
|
|
|
span_end = iter;
|
|
|
|
if (iter_size != span_size ||
|
|
gtk_text_iter_compare (&iter, end) >= 0)
|
|
{
|
|
if (span_size != 0)
|
|
{
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), span_tag,
|
|
&span_start, &span_end);
|
|
}
|
|
|
|
if ((span_size + count) > 0)
|
|
{
|
|
span_tag = gimp_text_buffer_get_size_tag (buffer,
|
|
span_size + count);
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), span_tag,
|
|
&span_start, &span_end);
|
|
}
|
|
|
|
span_start = iter;
|
|
span_size = iter_size;
|
|
span_tag = iter_tag;
|
|
}
|
|
|
|
/* We might have moved too far */
|
|
if (gtk_text_iter_compare (&iter, end) > 0)
|
|
iter = *end;
|
|
}
|
|
while (! gtk_text_iter_equal (&iter, end));
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
GtkTextTag *
|
|
gimp_text_buffer_get_iter_baseline (GimpTextBuffer *buffer,
|
|
const GtkTextIter *iter,
|
|
gint *baseline)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = buffer->baseline_tags; list; list = g_list_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
if (gtk_text_iter_has_tag (iter, tag))
|
|
{
|
|
if (baseline)
|
|
*baseline = gimp_text_tag_get_baseline (tag);
|
|
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
if (baseline)
|
|
*baseline = 0;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GtkTextTag *
|
|
gimp_text_buffer_get_baseline_tag (GimpTextBuffer *buffer,
|
|
gint baseline)
|
|
{
|
|
GList *list;
|
|
GtkTextTag *tag;
|
|
gchar name[32];
|
|
|
|
for (list = buffer->baseline_tags; list; list = g_list_next (list))
|
|
{
|
|
tag = list->data;
|
|
|
|
if (baseline == gimp_text_tag_get_baseline (tag))
|
|
return tag;
|
|
}
|
|
|
|
g_snprintf (name, sizeof (name), "baseline-%d", baseline);
|
|
|
|
tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
name,
|
|
GIMP_TEXT_PROP_NAME_BASELINE, baseline,
|
|
NULL);
|
|
|
|
buffer->baseline_tags = g_list_prepend (buffer->baseline_tags, tag);
|
|
|
|
return tag;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_set_baseline (GimpTextBuffer *buffer,
|
|
const GtkTextIter *start,
|
|
const GtkTextIter *end,
|
|
gint baseline)
|
|
{
|
|
GList *list;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
g_return_if_fail (start != NULL);
|
|
g_return_if_fail (end != NULL);
|
|
|
|
if (gtk_text_iter_equal (start, end))
|
|
return;
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
for (list = buffer->baseline_tags; list; list = g_list_next (list))
|
|
{
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
|
|
start, end);
|
|
}
|
|
|
|
if (baseline != 0)
|
|
{
|
|
GtkTextTag *tag;
|
|
|
|
tag = gimp_text_buffer_get_baseline_tag (buffer, baseline);
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
|
|
start, end);
|
|
}
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_change_baseline (GimpTextBuffer *buffer,
|
|
const GtkTextIter *start,
|
|
const GtkTextIter *end,
|
|
gint count)
|
|
{
|
|
GtkTextIter iter;
|
|
GtkTextIter span_start;
|
|
GtkTextIter span_end;
|
|
GtkTextTag *span_tag;
|
|
gint span_baseline;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
g_return_if_fail (start != NULL);
|
|
g_return_if_fail (end != NULL);
|
|
|
|
if (gtk_text_iter_equal (start, end))
|
|
return;
|
|
|
|
iter = *start;
|
|
span_start = *start;
|
|
span_tag = gimp_text_buffer_get_iter_baseline (buffer, &iter,
|
|
&span_baseline);
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
do
|
|
{
|
|
GtkTextTag *iter_tag;
|
|
gint iter_baseline;
|
|
|
|
gtk_text_iter_forward_char (&iter);
|
|
|
|
iter_tag = gimp_text_buffer_get_iter_baseline (buffer, &iter,
|
|
&iter_baseline);
|
|
|
|
span_end = iter;
|
|
|
|
if (iter_baseline != span_baseline ||
|
|
gtk_text_iter_compare (&iter, end) >= 0)
|
|
{
|
|
if (span_baseline != 0)
|
|
{
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), span_tag,
|
|
&span_start, &span_end);
|
|
}
|
|
|
|
if (span_baseline + count != 0)
|
|
{
|
|
span_tag = gimp_text_buffer_get_baseline_tag (buffer,
|
|
span_baseline + count);
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), span_tag,
|
|
&span_start, &span_end);
|
|
}
|
|
|
|
span_start = iter;
|
|
span_baseline = iter_baseline;
|
|
span_tag = iter_tag;
|
|
}
|
|
|
|
/* We might have moved too far */
|
|
if (gtk_text_iter_compare (&iter, end) > 0)
|
|
iter = *end;
|
|
}
|
|
while (! gtk_text_iter_equal (&iter, end));
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
GtkTextTag *
|
|
gimp_text_buffer_get_iter_kerning (GimpTextBuffer *buffer,
|
|
const GtkTextIter *iter,
|
|
gint *kerning)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = buffer->kerning_tags; list; list = g_list_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
if (gtk_text_iter_has_tag (iter, tag))
|
|
{
|
|
if (kerning)
|
|
*kerning = gimp_text_tag_get_kerning (tag);
|
|
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
if (kerning)
|
|
*kerning = 0;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GtkTextTag *
|
|
gimp_text_buffer_get_kerning_tag (GimpTextBuffer *buffer,
|
|
gint kerning)
|
|
{
|
|
GList *list;
|
|
GtkTextTag *tag;
|
|
gchar name[32];
|
|
|
|
for (list = buffer->kerning_tags; list; list = g_list_next (list))
|
|
{
|
|
tag = list->data;
|
|
|
|
if (kerning == gimp_text_tag_get_kerning (tag))
|
|
return tag;
|
|
}
|
|
|
|
g_snprintf (name, sizeof (name), "kerning-%d", kerning);
|
|
|
|
tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
name,
|
|
GIMP_TEXT_PROP_NAME_KERNING, kerning,
|
|
NULL);
|
|
|
|
buffer->kerning_tags = g_list_prepend (buffer->kerning_tags, tag);
|
|
|
|
return tag;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_set_kerning (GimpTextBuffer *buffer,
|
|
const GtkTextIter *start,
|
|
const GtkTextIter *end,
|
|
gint kerning)
|
|
{
|
|
GList *list;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
g_return_if_fail (start != NULL);
|
|
g_return_if_fail (end != NULL);
|
|
|
|
if (gtk_text_iter_equal (start, end))
|
|
return;
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
for (list = buffer->kerning_tags; list; list = g_list_next (list))
|
|
{
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
|
|
start, end);
|
|
}
|
|
|
|
if (kerning != 0)
|
|
{
|
|
GtkTextTag *tag;
|
|
|
|
tag = gimp_text_buffer_get_kerning_tag (buffer, kerning);
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
|
|
start, end);
|
|
}
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_change_kerning (GimpTextBuffer *buffer,
|
|
const GtkTextIter *start,
|
|
const GtkTextIter *end,
|
|
gint count)
|
|
{
|
|
GtkTextIter iter;
|
|
GtkTextIter span_start;
|
|
GtkTextIter span_end;
|
|
GtkTextTag *span_tag;
|
|
gint span_kerning;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
g_return_if_fail (start != NULL);
|
|
g_return_if_fail (end != NULL);
|
|
|
|
if (gtk_text_iter_equal (start, end))
|
|
return;
|
|
|
|
iter = *start;
|
|
span_start = *start;
|
|
span_tag = gimp_text_buffer_get_iter_kerning (buffer, &iter,
|
|
&span_kerning);
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
do
|
|
{
|
|
GtkTextTag *iter_tag;
|
|
gint iter_kerning;
|
|
|
|
gtk_text_iter_forward_char (&iter);
|
|
|
|
iter_tag = gimp_text_buffer_get_iter_kerning (buffer, &iter,
|
|
&iter_kerning);
|
|
|
|
span_end = iter;
|
|
|
|
if (iter_kerning != span_kerning ||
|
|
gtk_text_iter_compare (&iter, end) >= 0)
|
|
{
|
|
if (span_kerning != 0)
|
|
{
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), span_tag,
|
|
&span_start, &span_end);
|
|
}
|
|
|
|
if (span_kerning + count != 0)
|
|
{
|
|
span_tag = gimp_text_buffer_get_kerning_tag (buffer,
|
|
span_kerning + count);
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), span_tag,
|
|
&span_start, &span_end);
|
|
}
|
|
|
|
span_start = iter;
|
|
span_kerning = iter_kerning;
|
|
span_tag = iter_tag;
|
|
}
|
|
|
|
/* We might have moved too far */
|
|
if (gtk_text_iter_compare (&iter, end) > 0)
|
|
iter = *end;
|
|
}
|
|
while (! gtk_text_iter_equal (&iter, end));
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
GtkTextTag *
|
|
gimp_text_buffer_get_iter_font (GimpTextBuffer *buffer,
|
|
const GtkTextIter *iter,
|
|
gchar **font)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = buffer->font_tags; list; list = g_list_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
if (gtk_text_iter_has_tag (iter, tag))
|
|
{
|
|
if (font)
|
|
*font = gimp_text_tag_get_font (tag);
|
|
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
if (font)
|
|
*font = NULL;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GtkTextTag *
|
|
gimp_text_buffer_get_font_tag (GimpTextBuffer *buffer,
|
|
const gchar *font)
|
|
{
|
|
GList *list;
|
|
GtkTextTag *tag;
|
|
gchar name[256];
|
|
|
|
for (list = buffer->font_tags; list; list = g_list_next (list))
|
|
{
|
|
gchar *tag_font;
|
|
|
|
tag = list->data;
|
|
|
|
tag_font = gimp_text_tag_get_font (tag);
|
|
|
|
if (! strcmp (font, tag_font))
|
|
{
|
|
g_free (tag_font);
|
|
return tag;
|
|
}
|
|
|
|
g_free (tag_font);
|
|
}
|
|
|
|
g_snprintf (name, sizeof (name), "font-%s", font);
|
|
|
|
tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
name,
|
|
"font", font,
|
|
NULL);
|
|
|
|
buffer->font_tags = g_list_prepend (buffer->font_tags, tag);
|
|
|
|
return tag;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_set_font (GimpTextBuffer *buffer,
|
|
const GtkTextIter *start,
|
|
const GtkTextIter *end,
|
|
const gchar *font)
|
|
{
|
|
GList *list;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
g_return_if_fail (start != NULL);
|
|
g_return_if_fail (end != NULL);
|
|
|
|
if (gtk_text_iter_equal (start, end))
|
|
return;
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
for (list = buffer->font_tags; list; list = g_list_next (list))
|
|
{
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
|
|
start, end);
|
|
}
|
|
|
|
if (font)
|
|
{
|
|
GtkTextTag *tag = gimp_text_buffer_get_font_tag (buffer, font);
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
|
|
start, end);
|
|
}
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
GtkTextTag *
|
|
gimp_text_buffer_get_iter_color (GimpTextBuffer *buffer,
|
|
const GtkTextIter *iter,
|
|
GimpRGB *color)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = buffer->color_tags; list; list = g_list_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
if (gtk_text_iter_has_tag (iter, tag))
|
|
{
|
|
if (color)
|
|
gimp_text_tag_get_color (tag, color);
|
|
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static GtkTextTag *
|
|
gimp_text_buffer_get_color_tag (GimpTextBuffer *buffer,
|
|
const GimpRGB *color)
|
|
{
|
|
GList *list;
|
|
GtkTextTag *tag;
|
|
gchar name[256];
|
|
GdkColor gdk_color;
|
|
guchar r, g, b;
|
|
|
|
gimp_rgb_get_uchar (color, &r, &g, &b);
|
|
|
|
for (list = buffer->color_tags; list; list = g_list_next (list))
|
|
{
|
|
GimpRGB tag_color;
|
|
guchar tag_r, tag_g, tag_b;
|
|
|
|
tag = list->data;
|
|
|
|
gimp_text_tag_get_color (tag, &tag_color);
|
|
|
|
gimp_rgb_get_uchar (&tag_color, &tag_r, &tag_g, &tag_b);
|
|
|
|
/* Do not compare the alpha channel, since it's unused */
|
|
if (tag_r == r &&
|
|
tag_g == g &&
|
|
tag_b == b)
|
|
{
|
|
return tag;
|
|
}
|
|
}
|
|
|
|
g_snprintf (name, sizeof (name), "color-#%02x%02x%02x",
|
|
r, g, b);
|
|
|
|
gimp_rgb_get_gdk_color (color, &gdk_color);
|
|
|
|
tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
|
|
name,
|
|
"foreground-gdk", &gdk_color,
|
|
"foreground-set", TRUE,
|
|
NULL);
|
|
|
|
buffer->color_tags = g_list_prepend (buffer->color_tags, tag);
|
|
|
|
return tag;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_set_color (GimpTextBuffer *buffer,
|
|
const GtkTextIter *start,
|
|
const GtkTextIter *end,
|
|
const GimpRGB *color)
|
|
{
|
|
GList *list;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
g_return_if_fail (start != NULL);
|
|
g_return_if_fail (end != NULL);
|
|
|
|
if (gtk_text_iter_equal (start, end))
|
|
return;
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
for (list = buffer->color_tags; list; list = g_list_next (list))
|
|
{
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
|
|
start, end);
|
|
}
|
|
|
|
if (color)
|
|
{
|
|
GtkTextTag *tag = gimp_text_buffer_get_color_tag (buffer, color);
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
|
|
start, end);
|
|
}
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
/* Pango markup attribute names */
|
|
|
|
#define GIMP_TEXT_ATTR_NAME_SIZE "size"
|
|
#define GIMP_TEXT_ATTR_NAME_BASELINE "rise"
|
|
#define GIMP_TEXT_ATTR_NAME_KERNING "letter_spacing"
|
|
#define GIMP_TEXT_ATTR_NAME_FONT "font"
|
|
#define GIMP_TEXT_ATTR_NAME_COLOR "foreground"
|
|
|
|
const gchar *
|
|
gimp_text_buffer_tag_to_name (GimpTextBuffer *buffer,
|
|
GtkTextTag *tag,
|
|
const gchar **attribute,
|
|
gchar **value)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), NULL);
|
|
g_return_val_if_fail (GTK_IS_TEXT_TAG (tag), NULL);
|
|
|
|
if (attribute)
|
|
*attribute = NULL;
|
|
|
|
if (value)
|
|
*value = NULL;
|
|
|
|
if (tag == buffer->bold_tag)
|
|
{
|
|
return "b";
|
|
}
|
|
else if (tag == buffer->italic_tag)
|
|
{
|
|
return "i";
|
|
}
|
|
else if (tag == buffer->underline_tag)
|
|
{
|
|
return "u";
|
|
}
|
|
else if (tag == buffer->strikethrough_tag)
|
|
{
|
|
return "s";
|
|
}
|
|
else if (g_list_find (buffer->size_tags, tag))
|
|
{
|
|
if (attribute)
|
|
*attribute = GIMP_TEXT_ATTR_NAME_SIZE;
|
|
|
|
if (value)
|
|
*value = g_strdup_printf ("%d", gimp_text_tag_get_size (tag));
|
|
|
|
return "span";
|
|
}
|
|
else if (g_list_find (buffer->baseline_tags, tag))
|
|
{
|
|
if (attribute)
|
|
*attribute = GIMP_TEXT_ATTR_NAME_BASELINE;
|
|
|
|
if (value)
|
|
*value = g_strdup_printf ("%d", gimp_text_tag_get_baseline (tag));
|
|
|
|
return "span";
|
|
}
|
|
else if (g_list_find (buffer->kerning_tags, tag))
|
|
{
|
|
if (attribute)
|
|
*attribute = GIMP_TEXT_ATTR_NAME_KERNING;
|
|
|
|
if (value)
|
|
*value = g_strdup_printf ("%d", gimp_text_tag_get_kerning (tag));
|
|
|
|
return "span";
|
|
}
|
|
else if (g_list_find (buffer->font_tags, tag))
|
|
{
|
|
if (attribute)
|
|
*attribute = GIMP_TEXT_ATTR_NAME_FONT;
|
|
|
|
if (value)
|
|
*value = gimp_text_tag_get_font (tag);
|
|
|
|
return "span";
|
|
}
|
|
else if (g_list_find (buffer->color_tags, tag))
|
|
{
|
|
if (attribute)
|
|
*attribute = GIMP_TEXT_ATTR_NAME_COLOR;
|
|
|
|
if (value)
|
|
{
|
|
GimpRGB color;
|
|
guchar r, g, b;
|
|
|
|
gimp_text_tag_get_color (tag, &color);
|
|
gimp_rgb_get_uchar (&color, &r, &g, &b);
|
|
|
|
*value = g_strdup_printf ("#%02x%02x%02x", r, g, b);
|
|
}
|
|
|
|
return "span";
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
GtkTextTag *
|
|
gimp_text_buffer_name_to_tag (GimpTextBuffer *buffer,
|
|
const gchar *name,
|
|
const gchar *attribute,
|
|
const gchar *value)
|
|
{
|
|
g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), NULL);
|
|
g_return_val_if_fail (name != NULL, NULL);
|
|
|
|
if (! strcmp (name, "b"))
|
|
{
|
|
return buffer->bold_tag;
|
|
}
|
|
else if (! strcmp (name, "i"))
|
|
{
|
|
return buffer->italic_tag;
|
|
}
|
|
else if (! strcmp (name, "u"))
|
|
{
|
|
return buffer->underline_tag;
|
|
}
|
|
else if (! strcmp (name, "s"))
|
|
{
|
|
return buffer->strikethrough_tag;
|
|
}
|
|
else if (! strcmp (name, "span") &&
|
|
attribute != NULL &&
|
|
value != NULL)
|
|
{
|
|
if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_SIZE))
|
|
{
|
|
return gimp_text_buffer_get_size_tag (buffer, atoi (value));
|
|
}
|
|
else if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_BASELINE))
|
|
{
|
|
return gimp_text_buffer_get_baseline_tag (buffer, atoi (value));
|
|
}
|
|
else if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_KERNING))
|
|
{
|
|
return gimp_text_buffer_get_kerning_tag (buffer, atoi (value));
|
|
}
|
|
else if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_FONT))
|
|
{
|
|
return gimp_text_buffer_get_font_tag (buffer, value);
|
|
}
|
|
else if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_COLOR))
|
|
{
|
|
GimpRGB color;
|
|
guint r, g, b;
|
|
|
|
sscanf (value, "#%02x%02x%02x", &r, &g, &b);
|
|
|
|
gimp_rgb_set_uchar (&color, r, g, b);
|
|
|
|
return gimp_text_buffer_get_color_tag (buffer, &color);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_set_insert_tags (GimpTextBuffer *buffer,
|
|
GList *insert_tags,
|
|
GList *remove_tags)
|
|
{
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
|
|
buffer->insert_tags_set = TRUE;
|
|
|
|
g_list_free (buffer->insert_tags);
|
|
g_list_free (buffer->remove_tags);
|
|
buffer->insert_tags = insert_tags;
|
|
buffer->remove_tags = remove_tags;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_clear_insert_tags (GimpTextBuffer *buffer)
|
|
{
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
|
|
buffer->insert_tags_set = FALSE;
|
|
|
|
g_list_free (buffer->insert_tags);
|
|
g_list_free (buffer->remove_tags);
|
|
buffer->insert_tags = NULL;
|
|
buffer->remove_tags = NULL;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_insert (GimpTextBuffer *buffer,
|
|
const gchar *text)
|
|
{
|
|
GtkTextIter iter, start;
|
|
gint start_offset;
|
|
gboolean insert_tags_set;
|
|
GList *insert_tags;
|
|
GList *remove_tags;
|
|
GSList *tags_off = NULL;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
|
|
gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter,
|
|
gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer)));
|
|
|
|
start_offset = gtk_text_iter_get_offset (&iter);
|
|
|
|
insert_tags_set = buffer->insert_tags_set;
|
|
insert_tags = buffer->insert_tags;
|
|
remove_tags = buffer->remove_tags;
|
|
|
|
buffer->insert_tags_set = FALSE;
|
|
buffer->insert_tags = NULL;
|
|
buffer->remove_tags = NULL;
|
|
|
|
tags_off = gtk_text_iter_get_toggled_tags (&iter, FALSE);
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
gtk_text_buffer_insert (GTK_TEXT_BUFFER (buffer), &iter, text, -1);
|
|
|
|
gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (buffer), &start,
|
|
start_offset);
|
|
|
|
if (insert_tags_set)
|
|
{
|
|
GList *list;
|
|
|
|
for (list = remove_tags; list; list = g_list_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), tag,
|
|
&start, &iter);
|
|
}
|
|
|
|
for (list = insert_tags; list; list = g_list_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
|
|
&start, &iter);
|
|
}
|
|
}
|
|
|
|
if (tags_off)
|
|
{
|
|
GSList *slist;
|
|
|
|
for (slist = tags_off; slist; slist = g_slist_next (slist))
|
|
{
|
|
GtkTextTag *tag = slist->data;
|
|
|
|
if (! g_list_find (remove_tags, tag) &&
|
|
! g_list_find (buffer->kerning_tags, tag))
|
|
{
|
|
gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
|
|
&start, &iter);
|
|
}
|
|
}
|
|
|
|
g_slist_free (tags_off);
|
|
}
|
|
|
|
g_list_free (remove_tags);
|
|
g_list_free (insert_tags);
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
}
|
|
|
|
gint
|
|
gimp_text_buffer_get_iter_index (GimpTextBuffer *buffer,
|
|
GtkTextIter *iter,
|
|
gboolean layout_index)
|
|
{
|
|
GtkTextIter start;
|
|
gchar *string;
|
|
gint index;
|
|
|
|
g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), 0);
|
|
|
|
gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
|
|
|
|
string = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer),
|
|
&start, iter, TRUE);
|
|
index = strlen (string);
|
|
g_free (string);
|
|
|
|
if (layout_index)
|
|
{
|
|
do
|
|
{
|
|
GSList *tags = gtk_text_iter_get_tags (&start);
|
|
GSList *list;
|
|
|
|
for (list = tags; list; list = g_slist_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
if (g_list_find (buffer->kerning_tags, tag))
|
|
{
|
|
index += WORD_JOINER_LENGTH;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_slist_free (tags);
|
|
|
|
gtk_text_iter_forward_char (&start);
|
|
|
|
/* We might have moved too far */
|
|
if (gtk_text_iter_compare (&start, iter) > 0)
|
|
start = *iter;
|
|
}
|
|
while (! gtk_text_iter_equal (&start, iter));
|
|
}
|
|
|
|
return index;
|
|
}
|
|
|
|
void
|
|
gimp_text_buffer_get_iter_at_index (GimpTextBuffer *buffer,
|
|
GtkTextIter *iter,
|
|
gint index,
|
|
gboolean layout_index)
|
|
{
|
|
GtkTextIter start;
|
|
GtkTextIter end;
|
|
gchar *string;
|
|
|
|
g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
|
|
|
|
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
|
|
|
|
string = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer),
|
|
&start, &end, TRUE);
|
|
|
|
if (layout_index)
|
|
{
|
|
gchar *my_string = string;
|
|
gint my_index = 0;
|
|
gchar *tmp;
|
|
|
|
do
|
|
{
|
|
GSList *tags = gtk_text_iter_get_tags (&start);
|
|
GSList *list;
|
|
|
|
tmp = g_utf8_next_char (my_string);
|
|
my_index += (tmp - my_string);
|
|
my_string = tmp;
|
|
|
|
for (list = tags; list; list = g_slist_next (list))
|
|
{
|
|
GtkTextTag *tag = list->data;
|
|
|
|
if (g_list_find (buffer->kerning_tags, tag))
|
|
{
|
|
index -= WORD_JOINER_LENGTH;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_slist_free (tags);
|
|
|
|
gtk_text_iter_forward_char (&start);
|
|
|
|
/* We might have moved too far */
|
|
if (gtk_text_iter_compare (&start, &end) > 0)
|
|
start = end;
|
|
}
|
|
while (my_index < index &&
|
|
! gtk_text_iter_equal (&start, &end));
|
|
}
|
|
|
|
string[index] = '\0';
|
|
|
|
gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (buffer), iter,
|
|
g_utf8_strlen (string, -1));
|
|
|
|
g_free (string);
|
|
}
|
|
|
|
gboolean
|
|
gimp_text_buffer_load (GimpTextBuffer *buffer,
|
|
const gchar *filename,
|
|
GError **error)
|
|
{
|
|
FILE *file;
|
|
gchar buf[2048];
|
|
gint remaining = 0;
|
|
GtkTextIter iter;
|
|
|
|
g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), FALSE);
|
|
g_return_val_if_fail (filename != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
file = g_fopen (filename, "r");
|
|
|
|
if (! file)
|
|
{
|
|
g_set_error_literal (error, G_FILE_ERROR,
|
|
g_file_error_from_errno (errno),
|
|
g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
gimp_text_buffer_set_text (buffer, NULL);
|
|
gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &iter);
|
|
|
|
while (! feof (file))
|
|
{
|
|
const char *leftover;
|
|
gint count;
|
|
gint to_read = sizeof (buf) - remaining - 1;
|
|
|
|
count = fread (buf + remaining, 1, to_read, file);
|
|
buf[count + remaining] = '\0';
|
|
|
|
g_utf8_validate (buf, count + remaining, &leftover);
|
|
|
|
gtk_text_buffer_insert (GTK_TEXT_BUFFER (buffer), &iter,
|
|
buf, leftover - buf);
|
|
gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &iter);
|
|
|
|
remaining = (buf + remaining + count) - leftover;
|
|
g_memmove (buf, leftover, remaining);
|
|
|
|
if (remaining > 6 || count < to_read)
|
|
break;
|
|
}
|
|
|
|
if (remaining)
|
|
g_message (_("Invalid UTF-8 data in file '%s'."),
|
|
gimp_filename_to_utf8 (filename));
|
|
|
|
fclose (file);
|
|
|
|
gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
gboolean
|
|
gimp_text_buffer_save (GimpTextBuffer *buffer,
|
|
const gchar *filename,
|
|
gboolean selection_only,
|
|
GError **error)
|
|
{
|
|
GtkTextIter start_iter;
|
|
GtkTextIter end_iter;
|
|
gint fd;
|
|
gchar *text_contents;
|
|
|
|
g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), FALSE);
|
|
g_return_val_if_fail (filename != NULL, FALSE);
|
|
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
|
|
|
|
fd = g_open (filename, O_WRONLY | O_CREAT | O_APPEND, 0666);
|
|
|
|
if (fd == -1)
|
|
{
|
|
g_set_error_literal (error, G_FILE_ERROR,
|
|
g_file_error_from_errno (errno),
|
|
g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
|
|
if (selection_only)
|
|
gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer),
|
|
&start_iter, &end_iter);
|
|
else
|
|
gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer),
|
|
&start_iter, &end_iter);
|
|
|
|
text_contents = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer),
|
|
&start_iter, &end_iter, TRUE);
|
|
|
|
if (text_contents)
|
|
{
|
|
gint text_length = strlen (text_contents);
|
|
|
|
if (text_length > 0)
|
|
{
|
|
gint bytes_written;
|
|
|
|
bytes_written = write (fd, text_contents, text_length);
|
|
|
|
if (bytes_written != text_length)
|
|
{
|
|
g_free (text_contents);
|
|
close (fd);
|
|
g_set_error_literal (error, G_FILE_ERROR,
|
|
g_file_error_from_errno (errno),
|
|
g_strerror (errno));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
g_free (text_contents);
|
|
}
|
|
|
|
close (fd);
|
|
|
|
return TRUE;
|
|
}
|