mirror of https://github.com/GNOME/gimp.git
1527 lines
40 KiB
C
1527 lines
40 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 void gimp_text_buffer_constructed (GObject *object);
|
|
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->constructed = gimp_text_buffer_constructed;
|
|
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 void
|
|
gimp_text_buffer_constructed (GObject *object)
|
|
{
|
|
GimpTextBuffer *buffer = GIMP_TEXT_BUFFER (object);
|
|
|
|
if (G_OBJECT_CLASS (parent_class)->constructed)
|
|
G_OBJECT_CLASS (parent_class)->constructed (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);
|
|
}
|
|
|
|
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;
|
|
}
|