1 /* GIMP - The GNU Image Manipulation Program
2  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
3  *
4  * GimpTextBuffer
5  * Copyright (C) 2010  Michael Natterer <mitch@gimp.org>
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  */
20 
21 #include "config.h"
22 
23 #include <stdlib.h>
24 
25 #include <gegl.h>
26 #include <gtk/gtk.h>
27 
28 #include "libgimpbase/gimpbase.h"
29 #include "libgimpcolor/gimpcolor.h"
30 
31 #include "widgets-types.h"
32 
33 #include "gimptextbuffer.h"
34 #include "gimptextbuffer-serialize.h"
35 #include "gimptexttag.h"
36 #include "gimpwidgets-utils.h"
37 
38 #include "gimp-intl.h"
39 
40 
41 enum
42 {
43   COLOR_APPLIED,
44   LAST_SIGNAL
45 };
46 
47 
48 /*  local function prototypes  */
49 
50 static void   gimp_text_buffer_constructed (GObject           *object);
51 static void   gimp_text_buffer_dispose     (GObject           *object);
52 static void   gimp_text_buffer_finalize    (GObject           *object);
53 
54 static void   gimp_text_buffer_mark_set    (GtkTextBuffer     *buffer,
55                                             const GtkTextIter *location,
56                                             GtkTextMark       *mark);
57 
58 
59 G_DEFINE_TYPE (GimpTextBuffer, gimp_text_buffer, GTK_TYPE_TEXT_BUFFER)
60 
61 #define parent_class gimp_text_buffer_parent_class
62 
63 static guint buffer_signals[LAST_SIGNAL] = { 0, };
64 
65 
66 static void
gimp_text_buffer_class_init(GimpTextBufferClass * klass)67 gimp_text_buffer_class_init (GimpTextBufferClass *klass)
68 {
69   GObjectClass       *object_class = G_OBJECT_CLASS (klass);
70   GtkTextBufferClass *buffer_class = GTK_TEXT_BUFFER_CLASS (klass);
71 
72   object_class->constructed = gimp_text_buffer_constructed;
73   object_class->dispose     = gimp_text_buffer_dispose;
74   object_class->finalize    = gimp_text_buffer_finalize;
75 
76   buffer_class->mark_set    = gimp_text_buffer_mark_set;
77 
78   buffer_signals[COLOR_APPLIED] =
79     g_signal_new ("color-applied",
80                   G_TYPE_FROM_CLASS (klass),
81                   G_SIGNAL_RUN_FIRST,
82                   G_STRUCT_OFFSET (GimpTextBufferClass, color_applied),
83                   NULL, NULL,
84                   g_cclosure_marshal_VOID__BOXED,
85                   G_TYPE_NONE, 1,
86                   GIMP_TYPE_RGB);
87 }
88 
89 static void
gimp_text_buffer_init(GimpTextBuffer * buffer)90 gimp_text_buffer_init (GimpTextBuffer *buffer)
91 {
92   buffer->markup_atom =
93     gtk_text_buffer_register_serialize_format (GTK_TEXT_BUFFER (buffer),
94                                                "application/x-gimp-pango-markup",
95                                                gimp_text_buffer_serialize,
96                                                NULL, NULL);
97 
98   gtk_text_buffer_register_deserialize_format (GTK_TEXT_BUFFER (buffer),
99                                                "application/x-gimp-pango-markup",
100                                                gimp_text_buffer_deserialize,
101                                                NULL, NULL);
102 }
103 
104 static void
gimp_text_buffer_constructed(GObject * object)105 gimp_text_buffer_constructed (GObject *object)
106 {
107   GimpTextBuffer *buffer = GIMP_TEXT_BUFFER (object);
108 
109   G_OBJECT_CLASS (parent_class)->constructed (object);
110 
111   gtk_text_buffer_set_text (GTK_TEXT_BUFFER (buffer), "", -1);
112 
113   buffer->bold_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
114                                                  "bold",
115                                                  "weight", PANGO_WEIGHT_BOLD,
116                                                  NULL);
117 
118   buffer->italic_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
119                                                    "italic",
120                                                    "style", PANGO_STYLE_ITALIC,
121                                                    NULL);
122 
123   buffer->underline_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
124                                                       "underline",
125                                                       "underline", PANGO_UNDERLINE_SINGLE,
126                                                       NULL);
127 
128   buffer->preedit_underline_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
129                                                               "preedit-underline",
130                                                               "underline", PANGO_UNDERLINE_SINGLE,
131                                                               NULL);
132 
133   buffer->strikethrough_tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
134                                                           "strikethrough",
135                                                           "strikethrough", TRUE,
136                                                           NULL);
137 }
138 
139 static void
gimp_text_buffer_dispose(GObject * object)140 gimp_text_buffer_dispose (GObject *object)
141 {
142   /* GimpTextBuffer *buffer = GIMP_TEXT_BUFFER (object); */
143 
144   G_OBJECT_CLASS (parent_class)->dispose (object);
145 }
146 
147 static void
gimp_text_buffer_finalize(GObject * object)148 gimp_text_buffer_finalize (GObject *object)
149 {
150   GimpTextBuffer *buffer = GIMP_TEXT_BUFFER (object);
151 
152   if (buffer->size_tags)
153     {
154       g_list_free (buffer->size_tags);
155       buffer->size_tags = NULL;
156     }
157 
158   if (buffer->baseline_tags)
159     {
160       g_list_free (buffer->baseline_tags);
161       buffer->baseline_tags = NULL;
162     }
163 
164   if (buffer->kerning_tags)
165     {
166       g_list_free (buffer->kerning_tags);
167       buffer->kerning_tags = NULL;
168     }
169 
170   if (buffer->font_tags)
171     {
172       g_list_free (buffer->font_tags);
173       buffer->font_tags = NULL;
174     }
175 
176   if (buffer->color_tags)
177     {
178       g_list_free (buffer->color_tags);
179       buffer->color_tags = NULL;
180     }
181 
182   if (buffer->preedit_color_tags)
183     {
184       g_list_free (buffer->preedit_color_tags);
185       buffer->preedit_color_tags = NULL;
186     }
187 
188   if (buffer->preedit_bg_color_tags)
189     {
190       g_list_free (buffer->preedit_bg_color_tags);
191       buffer->preedit_bg_color_tags = NULL;
192     }
193 
194   gimp_text_buffer_clear_insert_tags (buffer);
195 
196   G_OBJECT_CLASS (parent_class)->finalize (object);
197 }
198 
199 static void
gimp_text_buffer_mark_set(GtkTextBuffer * buffer,const GtkTextIter * location,GtkTextMark * mark)200 gimp_text_buffer_mark_set (GtkTextBuffer     *buffer,
201                            const GtkTextIter *location,
202                            GtkTextMark       *mark)
203 {
204   gimp_text_buffer_clear_insert_tags (GIMP_TEXT_BUFFER (buffer));
205 
206   GTK_TEXT_BUFFER_CLASS (parent_class)->mark_set (buffer, location, mark);
207 }
208 
209 
210 /*  public functions  */
211 
212 GimpTextBuffer *
gimp_text_buffer_new(void)213 gimp_text_buffer_new (void)
214 {
215   return g_object_new (GIMP_TYPE_TEXT_BUFFER, NULL);
216 }
217 
218 void
gimp_text_buffer_set_text(GimpTextBuffer * buffer,const gchar * text)219 gimp_text_buffer_set_text (GimpTextBuffer *buffer,
220                            const gchar    *text)
221 {
222   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
223 
224   if (text == NULL)
225     text = "";
226 
227   gtk_text_buffer_set_text (GTK_TEXT_BUFFER (buffer), text, -1);
228 
229   gimp_text_buffer_clear_insert_tags (buffer);
230 }
231 
232 gchar *
gimp_text_buffer_get_text(GimpTextBuffer * buffer)233 gimp_text_buffer_get_text (GimpTextBuffer *buffer)
234 {
235   GtkTextIter start, end;
236 
237   g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), NULL);
238 
239   gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
240 
241   return gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer),
242                                    &start, &end, TRUE);
243 }
244 
245 void
gimp_text_buffer_set_markup(GimpTextBuffer * buffer,const gchar * markup)246 gimp_text_buffer_set_markup (GimpTextBuffer *buffer,
247                              const gchar    *markup)
248 {
249   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
250 
251   gimp_text_buffer_set_text (buffer, NULL);
252 
253   if (markup)
254     {
255       GtkTextTagTable *tag_table;
256       GtkTextBuffer   *content;
257       GtkTextIter      insert;
258       GError          *error = NULL;
259 
260       tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer));
261       content = gtk_text_buffer_new (tag_table);
262 
263       gtk_text_buffer_get_start_iter (content, &insert);
264 
265       if (! gtk_text_buffer_deserialize (GTK_TEXT_BUFFER (buffer),
266                                          content,
267                                          buffer->markup_atom,
268                                          &insert,
269                                          (const guint8 *) markup, -1,
270                                          &error))
271         {
272           g_printerr ("EEK: %s\n", error->message);
273           g_clear_error (&error);
274         }
275       else
276         {
277           GtkTextIter start, end;
278 
279           gimp_text_buffer_post_deserialize (buffer, content);
280 
281           gtk_text_buffer_get_bounds (content, &start, &end);
282           gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &insert);
283 
284           gtk_text_buffer_insert_range (GTK_TEXT_BUFFER (buffer),
285                                         &insert, &start, &end);
286         }
287 
288       g_object_unref (content);
289     }
290 
291   gimp_text_buffer_clear_insert_tags (buffer);
292 }
293 
294 gchar *
gimp_text_buffer_get_markup(GimpTextBuffer * buffer)295 gimp_text_buffer_get_markup (GimpTextBuffer *buffer)
296 {
297   GtkTextTagTable *tag_table;
298   GtkTextBuffer   *content;
299   GtkTextIter      insert;
300   GtkTextIter      start, end;
301   gchar           *markup;
302   gsize            length;
303 
304   g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), NULL);
305 
306   tag_table = gtk_text_buffer_get_tag_table (GTK_TEXT_BUFFER (buffer));
307   content = gtk_text_buffer_new (tag_table);
308 
309   gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
310   gtk_text_buffer_get_start_iter (content, &insert);
311 
312   gtk_text_buffer_insert_range (content, &insert, &start, &end);
313 
314   gimp_text_buffer_pre_serialize (buffer, content);
315 
316   gtk_text_buffer_get_bounds (content, &start, &end);
317 
318   markup = (gchar *) gtk_text_buffer_serialize (GTK_TEXT_BUFFER (buffer),
319                                                 content,
320                                                 buffer->markup_atom,
321                                                 &start, &end,
322                                                 &length);
323 
324   g_object_unref (content);
325 
326   return markup;
327 }
328 
329 gboolean
gimp_text_buffer_has_markup(GimpTextBuffer * buffer)330 gimp_text_buffer_has_markup (GimpTextBuffer *buffer)
331 {
332   GtkTextIter iter;
333 
334   g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), FALSE);
335 
336   gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &iter);
337 
338   do
339     {
340       GSList *tags = gtk_text_iter_get_tags (&iter);
341 
342       if (tags)
343         {
344           g_slist_free (tags);
345           return TRUE;
346         }
347     }
348   while (gtk_text_iter_forward_char (&iter));
349 
350   return FALSE;
351 }
352 
353 GtkTextTag *
gimp_text_buffer_get_iter_size(GimpTextBuffer * buffer,const GtkTextIter * iter,gint * size)354 gimp_text_buffer_get_iter_size (GimpTextBuffer    *buffer,
355                                 const GtkTextIter *iter,
356                                 gint              *size)
357 {
358   GList *list;
359 
360   for (list = buffer->size_tags; list; list = g_list_next (list))
361     {
362       GtkTextTag *tag = list->data;
363 
364       if (gtk_text_iter_has_tag (iter, tag))
365         {
366           if (size)
367             *size = gimp_text_tag_get_size (tag);
368 
369           return tag;
370         }
371     }
372 
373   if (size)
374     *size = 0;
375 
376   return NULL;
377 }
378 
379 GtkTextTag *
gimp_text_buffer_get_size_tag(GimpTextBuffer * buffer,gint size)380 gimp_text_buffer_get_size_tag (GimpTextBuffer *buffer,
381                                gint            size)
382 {
383   GList      *list;
384   GtkTextTag *tag;
385   gchar       name[32];
386 
387   for (list = buffer->size_tags; list; list = g_list_next (list))
388     {
389       tag = list->data;
390 
391       if (size == gimp_text_tag_get_size (tag))
392         return tag;
393     }
394 
395   g_snprintf (name, sizeof (name), "size-%d", size);
396 
397   tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
398                                     name,
399                                     GIMP_TEXT_PROP_NAME_SIZE, size,
400                                     NULL);
401 
402   buffer->size_tags = g_list_prepend (buffer->size_tags, tag);
403 
404   return tag;
405 }
406 
407 void
gimp_text_buffer_set_size(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gint size)408 gimp_text_buffer_set_size (GimpTextBuffer    *buffer,
409                            const GtkTextIter *start,
410                            const GtkTextIter *end,
411                            gint               size)
412 {
413   GList *list;
414 
415   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
416   g_return_if_fail (start != NULL);
417   g_return_if_fail (end != NULL);
418 
419   if (gtk_text_iter_equal (start, end))
420     return;
421 
422   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
423 
424   for (list = buffer->size_tags; list; list = g_list_next (list))
425     {
426       gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
427                                   start, end);
428     }
429 
430   if (size != 0)
431     {
432       GtkTextTag *tag;
433 
434       tag = gimp_text_buffer_get_size_tag (buffer, size);
435 
436       gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
437                                  start, end);
438     }
439 
440   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
441 }
442 
443 void
gimp_text_buffer_change_size(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gint count)444 gimp_text_buffer_change_size (GimpTextBuffer    *buffer,
445                               const GtkTextIter *start,
446                               const GtkTextIter *end,
447                               gint               count)
448 {
449   GtkTextIter  iter;
450   GtkTextIter  span_start;
451   GtkTextIter  span_end;
452   GtkTextTag  *span_tag;
453   gint         span_size;
454 
455   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
456   g_return_if_fail (start != NULL);
457   g_return_if_fail (end != NULL);
458 
459   if (gtk_text_iter_equal (start, end))
460     return;
461 
462   iter       = *start;
463   span_start = *start;
464   span_tag   = gimp_text_buffer_get_iter_size (buffer, &iter,
465                                                &span_size);
466 
467   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
468 
469   do
470     {
471       GtkTextTag *iter_tag;
472       gint        iter_size;
473 
474       gtk_text_iter_forward_char (&iter);
475 
476       iter_tag = gimp_text_buffer_get_iter_size (buffer, &iter,
477                                                  &iter_size);
478 
479       span_end = iter;
480 
481       if (iter_size != span_size ||
482           gtk_text_iter_compare (&iter, end) >= 0)
483         {
484           if (span_size != 0)
485             {
486               gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), span_tag,
487                                           &span_start, &span_end);
488             }
489 
490           if ((span_size + count) > 0)
491             {
492               span_tag = gimp_text_buffer_get_size_tag (buffer,
493                                                         span_size + count);
494 
495               gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), span_tag,
496                                          &span_start, &span_end);
497             }
498 
499           span_start = iter;
500           span_size  = iter_size;
501           span_tag   = iter_tag;
502         }
503 
504       /* We might have moved too far */
505       if (gtk_text_iter_compare (&iter, end) > 0)
506         iter = *end;
507     }
508   while (! gtk_text_iter_equal (&iter, end));
509 
510   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
511 }
512 
513 GtkTextTag *
gimp_text_buffer_get_iter_baseline(GimpTextBuffer * buffer,const GtkTextIter * iter,gint * baseline)514 gimp_text_buffer_get_iter_baseline (GimpTextBuffer    *buffer,
515                                     const GtkTextIter *iter,
516                                     gint              *baseline)
517 {
518   GList *list;
519 
520   for (list = buffer->baseline_tags; list; list = g_list_next (list))
521     {
522       GtkTextTag *tag = list->data;
523 
524       if (gtk_text_iter_has_tag (iter, tag))
525         {
526           if (baseline)
527             *baseline = gimp_text_tag_get_baseline (tag);
528 
529           return tag;
530         }
531     }
532 
533   if (baseline)
534     *baseline = 0;
535 
536   return NULL;
537 }
538 
539 static GtkTextTag *
gimp_text_buffer_get_baseline_tag(GimpTextBuffer * buffer,gint baseline)540 gimp_text_buffer_get_baseline_tag (GimpTextBuffer *buffer,
541                                    gint            baseline)
542 {
543   GList      *list;
544   GtkTextTag *tag;
545   gchar       name[32];
546 
547   for (list = buffer->baseline_tags; list; list = g_list_next (list))
548     {
549       tag = list->data;
550 
551       if (baseline == gimp_text_tag_get_baseline (tag))
552         return tag;
553     }
554 
555   g_snprintf (name, sizeof (name), "baseline-%d", baseline);
556 
557   tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
558                                     name,
559                                     GIMP_TEXT_PROP_NAME_BASELINE, baseline,
560                                     NULL);
561 
562   buffer->baseline_tags = g_list_prepend (buffer->baseline_tags, tag);
563 
564   return tag;
565 }
566 
567 void
gimp_text_buffer_set_baseline(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gint baseline)568 gimp_text_buffer_set_baseline (GimpTextBuffer    *buffer,
569                                const GtkTextIter *start,
570                                const GtkTextIter *end,
571                                gint               baseline)
572 {
573   GList *list;
574 
575   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
576   g_return_if_fail (start != NULL);
577   g_return_if_fail (end != NULL);
578 
579   if (gtk_text_iter_equal (start, end))
580     return;
581 
582   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
583 
584   for (list = buffer->baseline_tags; list; list = g_list_next (list))
585     {
586       gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
587                                   start, end);
588     }
589 
590   if (baseline != 0)
591     {
592       GtkTextTag *tag;
593 
594       tag = gimp_text_buffer_get_baseline_tag (buffer, baseline);
595 
596       gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
597                                  start, end);
598     }
599 
600   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
601 }
602 
603 void
gimp_text_buffer_change_baseline(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gint count)604 gimp_text_buffer_change_baseline (GimpTextBuffer    *buffer,
605                                   const GtkTextIter *start,
606                                   const GtkTextIter *end,
607                                   gint               count)
608 {
609   GtkTextIter  iter;
610   GtkTextIter  span_start;
611   GtkTextIter  span_end;
612   GtkTextTag  *span_tag;
613   gint         span_baseline;
614 
615   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
616   g_return_if_fail (start != NULL);
617   g_return_if_fail (end != NULL);
618 
619   if (gtk_text_iter_equal (start, end))
620     return;
621 
622   iter       = *start;
623   span_start = *start;
624   span_tag   = gimp_text_buffer_get_iter_baseline (buffer, &iter,
625                                                    &span_baseline);
626 
627   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
628 
629   do
630     {
631       GtkTextTag *iter_tag;
632       gint        iter_baseline;
633 
634       gtk_text_iter_forward_char (&iter);
635 
636       iter_tag = gimp_text_buffer_get_iter_baseline (buffer, &iter,
637                                                      &iter_baseline);
638 
639       span_end = iter;
640 
641       if (iter_baseline != span_baseline ||
642           gtk_text_iter_compare (&iter, end) >= 0)
643         {
644           if (span_baseline != 0)
645             {
646               gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), span_tag,
647                                           &span_start, &span_end);
648             }
649 
650           if (span_baseline + count != 0)
651             {
652               span_tag = gimp_text_buffer_get_baseline_tag (buffer,
653                                                             span_baseline + count);
654 
655               gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), span_tag,
656                                          &span_start, &span_end);
657             }
658 
659           span_start    = iter;
660           span_baseline = iter_baseline;
661           span_tag      = iter_tag;
662         }
663 
664       /* We might have moved too far */
665       if (gtk_text_iter_compare (&iter, end) > 0)
666         iter = *end;
667     }
668   while (! gtk_text_iter_equal (&iter, end));
669 
670   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
671 }
672 
673 GtkTextTag *
gimp_text_buffer_get_iter_kerning(GimpTextBuffer * buffer,const GtkTextIter * iter,gint * kerning)674 gimp_text_buffer_get_iter_kerning (GimpTextBuffer    *buffer,
675                                    const GtkTextIter *iter,
676                                    gint              *kerning)
677 {
678   GList *list;
679 
680   for (list = buffer->kerning_tags; list; list = g_list_next (list))
681     {
682       GtkTextTag *tag = list->data;
683 
684       if (gtk_text_iter_has_tag (iter, tag))
685         {
686           if (kerning)
687             *kerning = gimp_text_tag_get_kerning (tag);
688 
689           return tag;
690         }
691     }
692 
693   if (kerning)
694     *kerning = 0;
695 
696   return NULL;
697 }
698 
699 static GtkTextTag *
gimp_text_buffer_get_kerning_tag(GimpTextBuffer * buffer,gint kerning)700 gimp_text_buffer_get_kerning_tag (GimpTextBuffer *buffer,
701                                   gint            kerning)
702 {
703   GList      *list;
704   GtkTextTag *tag;
705   gchar       name[32];
706 
707   for (list = buffer->kerning_tags; list; list = g_list_next (list))
708     {
709       tag = list->data;
710 
711       if (kerning == gimp_text_tag_get_kerning (tag))
712         return tag;
713     }
714 
715   g_snprintf (name, sizeof (name), "kerning-%d", kerning);
716 
717   tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
718                                     name,
719                                     GIMP_TEXT_PROP_NAME_KERNING, kerning,
720                                     NULL);
721 
722   buffer->kerning_tags = g_list_prepend (buffer->kerning_tags, tag);
723 
724   return tag;
725 }
726 
727 void
gimp_text_buffer_set_kerning(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gint kerning)728 gimp_text_buffer_set_kerning (GimpTextBuffer    *buffer,
729                               const GtkTextIter *start,
730                               const GtkTextIter *end,
731                               gint               kerning)
732 {
733   GList *list;
734 
735   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
736   g_return_if_fail (start != NULL);
737   g_return_if_fail (end != NULL);
738 
739   if (gtk_text_iter_equal (start, end))
740     return;
741 
742   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
743 
744   for (list = buffer->kerning_tags; list; list = g_list_next (list))
745     {
746       gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
747                                   start, end);
748     }
749 
750   if (kerning != 0)
751     {
752       GtkTextTag *tag;
753 
754       tag = gimp_text_buffer_get_kerning_tag (buffer, kerning);
755 
756       gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
757                                  start, end);
758     }
759 
760   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
761 }
762 
763 void
gimp_text_buffer_change_kerning(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,gint count)764 gimp_text_buffer_change_kerning (GimpTextBuffer    *buffer,
765                                  const GtkTextIter *start,
766                                  const GtkTextIter *end,
767                                  gint               count)
768 {
769   GtkTextIter  iter;
770   GtkTextIter  span_start;
771   GtkTextIter  span_end;
772   GtkTextTag  *span_tag;
773   gint         span_kerning;
774 
775   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
776   g_return_if_fail (start != NULL);
777   g_return_if_fail (end != NULL);
778 
779   if (gtk_text_iter_equal (start, end))
780     return;
781 
782   iter       = *start;
783   span_start = *start;
784   span_tag   = gimp_text_buffer_get_iter_kerning (buffer, &iter,
785                                                   &span_kerning);
786 
787   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
788 
789   do
790     {
791       GtkTextTag *iter_tag;
792       gint        iter_kerning;
793 
794       gtk_text_iter_forward_char (&iter);
795 
796       iter_tag = gimp_text_buffer_get_iter_kerning (buffer, &iter,
797                                                     &iter_kerning);
798 
799       span_end = iter;
800 
801       if (iter_kerning != span_kerning ||
802           gtk_text_iter_compare (&iter, end) >= 0)
803         {
804           if (span_kerning != 0)
805             {
806               gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), span_tag,
807                                           &span_start, &span_end);
808             }
809 
810           if (span_kerning + count != 0)
811             {
812               span_tag = gimp_text_buffer_get_kerning_tag (buffer,
813                                                            span_kerning + count);
814 
815               gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), span_tag,
816                                          &span_start, &span_end);
817             }
818 
819           span_start   = iter;
820           span_kerning = iter_kerning;
821           span_tag     = iter_tag;
822         }
823 
824       /* We might have moved too far */
825       if (gtk_text_iter_compare (&iter, end) > 0)
826         iter = *end;
827     }
828   while (! gtk_text_iter_equal (&iter, end));
829 
830   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
831 }
832 
833 GtkTextTag *
gimp_text_buffer_get_iter_font(GimpTextBuffer * buffer,const GtkTextIter * iter,gchar ** font)834 gimp_text_buffer_get_iter_font (GimpTextBuffer     *buffer,
835                                 const GtkTextIter  *iter,
836                                 gchar             **font)
837 {
838   GList *list;
839 
840   for (list = buffer->font_tags; list; list = g_list_next (list))
841     {
842       GtkTextTag *tag = list->data;
843 
844       if (gtk_text_iter_has_tag (iter, tag))
845         {
846           if (font)
847             *font = gimp_text_tag_get_font (tag);
848 
849           return tag;
850         }
851     }
852 
853   if (font)
854     *font = NULL;
855 
856   return NULL;
857 }
858 
859 GtkTextTag *
gimp_text_buffer_get_font_tag(GimpTextBuffer * buffer,const gchar * font)860 gimp_text_buffer_get_font_tag (GimpTextBuffer *buffer,
861                                const gchar    *font)
862 {
863   GList      *list;
864   GtkTextTag *tag;
865   gchar       name[256];
866   PangoFontDescription *pfd = pango_font_description_from_string (font);
867   char *description = pango_font_description_to_string (pfd);
868 
869   pango_font_description_free (pfd);
870 
871   for (list = buffer->font_tags; list; list = g_list_next (list))
872     {
873       gchar *tag_font;
874 
875       tag = list->data;
876 
877       tag_font = gimp_text_tag_get_font (tag);
878 
879       if (! strcmp (description, tag_font))
880         {
881           g_free (tag_font);
882           g_free (description);
883           return tag;
884         }
885 
886       g_free (tag_font);
887     }
888 
889   g_snprintf (name, sizeof (name), "font-%s", description);
890 
891   tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
892                                     name,
893                                     "font", description,
894                                     NULL);
895   gtk_text_tag_set_priority (tag, 0);
896   g_free (description);
897   buffer->font_tags = g_list_prepend (buffer->font_tags, tag);
898 
899   return tag;
900 }
901 
902 void
gimp_text_buffer_set_font(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,const gchar * font)903 gimp_text_buffer_set_font (GimpTextBuffer    *buffer,
904                            const GtkTextIter *start,
905                            const GtkTextIter *end,
906                            const gchar       *font)
907 {
908   GList *list;
909 
910   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
911   g_return_if_fail (start != NULL);
912   g_return_if_fail (end != NULL);
913 
914   if (gtk_text_iter_equal (start, end))
915     return;
916 
917   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
918 
919   for (list = buffer->font_tags; list; list = g_list_next (list))
920     {
921       gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
922                                   start, end);
923     }
924 
925   if (font)
926     {
927       GtkTextTag *tag = gimp_text_buffer_get_font_tag (buffer, font);
928 
929       gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
930                                  start, end);
931     }
932 
933   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
934 }
935 
936 GtkTextTag *
gimp_text_buffer_get_iter_color(GimpTextBuffer * buffer,const GtkTextIter * iter,GimpRGB * color)937 gimp_text_buffer_get_iter_color (GimpTextBuffer    *buffer,
938                                  const GtkTextIter *iter,
939                                  GimpRGB           *color)
940 {
941   GList *list;
942 
943   for (list = buffer->color_tags; list; list = g_list_next (list))
944     {
945       GtkTextTag *tag = list->data;
946 
947       if (gtk_text_iter_has_tag (iter, tag))
948         {
949           if (color)
950             gimp_text_tag_get_fg_color (tag, color);
951 
952           return tag;
953         }
954     }
955 
956   return NULL;
957 }
958 
959 GtkTextTag *
gimp_text_buffer_get_color_tag(GimpTextBuffer * buffer,const GimpRGB * color)960 gimp_text_buffer_get_color_tag (GimpTextBuffer *buffer,
961                                 const GimpRGB  *color)
962 {
963   GList      *list;
964   GtkTextTag *tag;
965   gchar       name[256];
966   GdkColor    gdk_color;
967   guchar      r, g, b;
968 
969   gimp_rgb_get_uchar (color, &r, &g, &b);
970 
971   for (list = buffer->color_tags; list; list = g_list_next (list))
972     {
973       GimpRGB tag_color;
974       guchar  tag_r, tag_g, tag_b;
975 
976       tag = list->data;
977 
978       gimp_text_tag_get_fg_color (tag, &tag_color);
979 
980       gimp_rgb_get_uchar (&tag_color, &tag_r, &tag_g, &tag_b);
981 
982       /* Do not compare the alpha channel, since it's unused */
983       if (tag_r == r &&
984           tag_g == g &&
985           tag_b == b)
986         {
987           return tag;
988         }
989     }
990 
991   g_snprintf (name, sizeof (name), "color-#%02x%02x%02x",
992               r, g, b);
993 
994   gimp_rgb_get_gdk_color (color, &gdk_color);
995 
996   tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
997                                     name,
998                                     "foreground-gdk", &gdk_color,
999                                     "foreground-set", TRUE,
1000                                     NULL);
1001 
1002   buffer->color_tags = g_list_prepend (buffer->color_tags, tag);
1003 
1004   return tag;
1005 }
1006 
1007 void
gimp_text_buffer_set_color(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,const GimpRGB * color)1008 gimp_text_buffer_set_color (GimpTextBuffer    *buffer,
1009                             const GtkTextIter *start,
1010                             const GtkTextIter *end,
1011                             const GimpRGB     *color)
1012 {
1013   GList *list;
1014 
1015   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
1016   g_return_if_fail (start != NULL);
1017   g_return_if_fail (end != NULL);
1018 
1019   if (gtk_text_iter_equal (start, end))
1020     return;
1021 
1022   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
1023 
1024   for (list = buffer->color_tags; list; list = g_list_next (list))
1025     {
1026       gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
1027                                   start, end);
1028     }
1029 
1030   if (color)
1031     {
1032       GtkTextTag *tag = gimp_text_buffer_get_color_tag (buffer, color);
1033 
1034       gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
1035                                  start, end);
1036 
1037       g_signal_emit (buffer, buffer_signals[COLOR_APPLIED], 0, color);
1038     }
1039 
1040   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
1041 }
1042 
1043 GtkTextTag *
gimp_text_buffer_get_preedit_color_tag(GimpTextBuffer * buffer,const GimpRGB * color)1044 gimp_text_buffer_get_preedit_color_tag (GimpTextBuffer *buffer,
1045                                         const GimpRGB  *color)
1046 {
1047   GList      *list;
1048   GtkTextTag *tag;
1049   gchar       name[256];
1050   GdkColor    gdk_color;
1051   guchar      r, g, b;
1052 
1053   gimp_rgb_get_uchar (color, &r, &g, &b);
1054 
1055   for (list = buffer->preedit_color_tags; list; list = g_list_next (list))
1056     {
1057       GimpRGB tag_color;
1058       guchar  tag_r, tag_g, tag_b;
1059 
1060       tag = list->data;
1061 
1062       gimp_text_tag_get_fg_color (tag, &tag_color);
1063 
1064       gimp_rgb_get_uchar (&tag_color, &tag_r, &tag_g, &tag_b);
1065 
1066       /* Do not compare the alpha channel, since it's unused */
1067       if (tag_r == r &&
1068           tag_g == g &&
1069           tag_b == b)
1070         {
1071           return tag;
1072         }
1073     }
1074 
1075   g_snprintf (name, sizeof (name), "preedit-color-#%02x%02x%02x",
1076               r, g, b);
1077 
1078   gimp_rgb_get_gdk_color (color, &gdk_color);
1079 
1080   tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
1081                                     name,
1082                                     "foreground-gdk", &gdk_color,
1083                                     "foreground-set", TRUE,
1084                                     NULL);
1085 
1086   buffer->preedit_color_tags = g_list_prepend (buffer->preedit_color_tags, tag);
1087 
1088   return tag;
1089 }
1090 
1091 void
gimp_text_buffer_set_preedit_color(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,const GimpRGB * color)1092 gimp_text_buffer_set_preedit_color (GimpTextBuffer    *buffer,
1093                                     const GtkTextIter *start,
1094                                     const GtkTextIter *end,
1095                                     const GimpRGB     *color)
1096 {
1097   GList *list;
1098 
1099   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
1100   g_return_if_fail (start != NULL);
1101   g_return_if_fail (end != NULL);
1102 
1103   if (gtk_text_iter_equal (start, end))
1104     return;
1105 
1106   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
1107 
1108   for (list = buffer->preedit_color_tags; list; list = g_list_next (list))
1109     {
1110       gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
1111                                   start, end);
1112     }
1113 
1114   if (color)
1115     {
1116       GtkTextTag *tag = gimp_text_buffer_get_preedit_color_tag (buffer, color);
1117 
1118       gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
1119                                  start, end);
1120     }
1121 
1122   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
1123 }
1124 
1125 GtkTextTag *
gimp_text_buffer_get_preedit_bg_color_tag(GimpTextBuffer * buffer,const GimpRGB * color)1126 gimp_text_buffer_get_preedit_bg_color_tag (GimpTextBuffer *buffer,
1127                                            const GimpRGB  *color)
1128 {
1129   GList      *list;
1130   GtkTextTag *tag;
1131   gchar       name[256];
1132   GdkColor    gdk_color;
1133   guchar      r, g, b;
1134 
1135   gimp_rgb_get_uchar (color, &r, &g, &b);
1136 
1137   for (list = buffer->preedit_bg_color_tags; list; list = g_list_next (list))
1138     {
1139       GimpRGB tag_color;
1140       guchar  tag_r, tag_g, tag_b;
1141 
1142       tag = list->data;
1143 
1144       gimp_text_tag_get_bg_color (tag, &tag_color);
1145 
1146       gimp_rgb_get_uchar (&tag_color, &tag_r, &tag_g, &tag_b);
1147 
1148       /* Do not compare the alpha channel, since it's unused */
1149       if (tag_r == r &&
1150           tag_g == g &&
1151           tag_b == b)
1152         {
1153           return tag;
1154         }
1155     }
1156 
1157   g_snprintf (name, sizeof (name), "bg-color-#%02x%02x%02x",
1158               r, g, b);
1159 
1160   gimp_rgb_get_gdk_color (color, &gdk_color);
1161 
1162   tag = gtk_text_buffer_create_tag (GTK_TEXT_BUFFER (buffer),
1163                                     name,
1164                                     "background-gdk", &gdk_color,
1165                                     "background-set", TRUE,
1166                                     NULL);
1167 
1168   buffer->preedit_bg_color_tags = g_list_prepend (buffer->preedit_bg_color_tags, tag);
1169 
1170   return tag;
1171 }
1172 
1173 void
gimp_text_buffer_set_preedit_bg_color(GimpTextBuffer * buffer,const GtkTextIter * start,const GtkTextIter * end,const GimpRGB * color)1174 gimp_text_buffer_set_preedit_bg_color (GimpTextBuffer    *buffer,
1175                                        const GtkTextIter *start,
1176                                        const GtkTextIter *end,
1177                                        const GimpRGB     *color)
1178 {
1179   GList *list;
1180 
1181   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
1182   g_return_if_fail (start != NULL);
1183   g_return_if_fail (end != NULL);
1184 
1185   if (gtk_text_iter_equal (start, end))
1186     return;
1187 
1188   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
1189 
1190   for (list = buffer->preedit_bg_color_tags; list; list = g_list_next (list))
1191     {
1192       gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), list->data,
1193                                   start, end);
1194     }
1195 
1196   if (color)
1197     {
1198       GtkTextTag *tag = gimp_text_buffer_get_preedit_bg_color_tag (buffer, color);
1199 
1200       gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
1201                                  start, end);
1202     }
1203 
1204   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
1205 }
1206 
1207 /*  Pango markup attribute names  */
1208 
1209 #define GIMP_TEXT_ATTR_NAME_SIZE      "size"
1210 #define GIMP_TEXT_ATTR_NAME_BASELINE  "rise"
1211 #define GIMP_TEXT_ATTR_NAME_KERNING   "letter_spacing"
1212 #define GIMP_TEXT_ATTR_NAME_FONT      "font"
1213 #define GIMP_TEXT_ATTR_NAME_STYLE     "style"
1214 #define GIMP_TEXT_ATTR_NAME_COLOR     "foreground"
1215 #define GIMP_TEXT_ATTR_NAME_FG_COLOR  "fgcolor"
1216 #define GIMP_TEXT_ATTR_NAME_BG_COLOR  "background"
1217 #define GIMP_TEXT_ATTR_NAME_UNDERLINE "underline"
1218 
1219 const gchar *
gimp_text_buffer_tag_to_name(GimpTextBuffer * buffer,GtkTextTag * tag,const gchar ** attribute,gchar ** value)1220 gimp_text_buffer_tag_to_name (GimpTextBuffer  *buffer,
1221                               GtkTextTag      *tag,
1222                               const gchar    **attribute,
1223                               gchar          **value)
1224 {
1225   g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), NULL);
1226   g_return_val_if_fail (GTK_IS_TEXT_TAG (tag), NULL);
1227 
1228   if (attribute)
1229     *attribute = NULL;
1230 
1231   if (value)
1232     *value = NULL;
1233 
1234   if (tag == buffer->bold_tag)
1235     {
1236       return "b";
1237     }
1238   else if (tag == buffer->italic_tag)
1239     {
1240       return "i";
1241     }
1242   else if (tag == buffer->underline_tag)
1243     {
1244       return "u";
1245     }
1246   else if (tag == buffer->strikethrough_tag)
1247     {
1248       return "s";
1249     }
1250   else if (g_list_find (buffer->size_tags, tag))
1251     {
1252       if (attribute)
1253         *attribute = GIMP_TEXT_ATTR_NAME_SIZE;
1254 
1255       if (value)
1256         *value = g_strdup_printf ("%d", gimp_text_tag_get_size (tag));
1257 
1258       return "span";
1259     }
1260   else if (g_list_find (buffer->baseline_tags, tag))
1261     {
1262       if (attribute)
1263         *attribute = GIMP_TEXT_ATTR_NAME_BASELINE;
1264 
1265       if (value)
1266         *value = g_strdup_printf ("%d", gimp_text_tag_get_baseline (tag));
1267 
1268       return "span";
1269     }
1270   else if (g_list_find (buffer->kerning_tags, tag))
1271     {
1272       if (attribute)
1273         *attribute = GIMP_TEXT_ATTR_NAME_KERNING;
1274 
1275       if (value)
1276         *value = g_strdup_printf ("%d", gimp_text_tag_get_kerning (tag));
1277 
1278       return "span";
1279     }
1280   else if (g_list_find (buffer->font_tags, tag))
1281     {
1282       if (attribute)
1283         *attribute = GIMP_TEXT_ATTR_NAME_FONT;
1284 
1285       if (value)
1286         *value = gimp_text_tag_get_font (tag);
1287 
1288       return "span";
1289     }
1290   else if (g_list_find (buffer->color_tags, tag))
1291     {
1292       if (attribute)
1293         *attribute = GIMP_TEXT_ATTR_NAME_COLOR;
1294 
1295       if (value)
1296         {
1297           GimpRGB color;
1298           guchar  r, g, b;
1299 
1300           gimp_text_tag_get_fg_color (tag, &color);
1301           gimp_rgb_get_uchar (&color, &r, &g, &b);
1302 
1303           *value = g_strdup_printf ("#%02x%02x%02x", r, g, b);
1304         }
1305 
1306       return "span";
1307     }
1308   else if (g_list_find (buffer->preedit_color_tags, tag))
1309     {
1310       /* "foreground" and "fgcolor" attributes are similar, but I use
1311        * one or the other as a trick to differentiate the color chosen
1312        * from the user and a display color for preedit. */
1313       if (attribute)
1314         *attribute = GIMP_TEXT_ATTR_NAME_FG_COLOR;
1315 
1316       if (value)
1317         {
1318           GimpRGB color;
1319           guchar  r, g, b;
1320 
1321           gimp_text_tag_get_fg_color (tag, &color);
1322           gimp_rgb_get_uchar (&color, &r, &g, &b);
1323 
1324           *value = g_strdup_printf ("#%02x%02x%02x", r, g, b);
1325         }
1326 
1327       return "span";
1328     }
1329   else if (g_list_find (buffer->preedit_bg_color_tags, tag))
1330     {
1331       if (attribute)
1332         *attribute = GIMP_TEXT_ATTR_NAME_BG_COLOR;
1333 
1334       if (value)
1335         {
1336           GimpRGB color;
1337           guchar  r, g, b;
1338 
1339           gimp_text_tag_get_bg_color (tag, &color);
1340           gimp_rgb_get_uchar (&color, &r, &g, &b);
1341 
1342           *value = g_strdup_printf ("#%02x%02x%02x", r, g, b);
1343         }
1344 
1345       return "span";
1346     }
1347   else if (tag == buffer->preedit_underline_tag)
1348     {
1349       if (attribute)
1350         *attribute = GIMP_TEXT_ATTR_NAME_UNDERLINE;
1351 
1352       if (value)
1353         *value = g_strdup ("single");
1354 
1355       return "span";
1356     }
1357 
1358   return NULL;
1359 }
1360 
1361 GtkTextTag *
gimp_text_buffer_name_to_tag(GimpTextBuffer * buffer,const gchar * name,const gchar * attribute,const gchar * value)1362 gimp_text_buffer_name_to_tag (GimpTextBuffer *buffer,
1363                               const gchar    *name,
1364                               const gchar    *attribute,
1365                               const gchar    *value)
1366 {
1367   g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), NULL);
1368   g_return_val_if_fail (name != NULL, NULL);
1369 
1370   if (! strcmp (name, "b"))
1371     {
1372       return buffer->bold_tag;
1373     }
1374   else if (! strcmp (name, "i"))
1375     {
1376       return buffer->italic_tag;
1377     }
1378   else if (! strcmp (name, "u"))
1379     {
1380       return buffer->underline_tag;
1381     }
1382   else if (! strcmp (name, "s"))
1383     {
1384       return buffer->strikethrough_tag;
1385     }
1386   else if (! strcmp (name, "span") &&
1387            attribute != NULL       &&
1388            value     != NULL)
1389     {
1390       if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_SIZE))
1391         {
1392           return gimp_text_buffer_get_size_tag (buffer, atoi (value));
1393         }
1394       else if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_BASELINE))
1395         {
1396           return gimp_text_buffer_get_baseline_tag (buffer, atoi (value));
1397         }
1398       else if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_KERNING))
1399         {
1400           return gimp_text_buffer_get_kerning_tag (buffer, atoi (value));
1401         }
1402       else if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_FONT))
1403         {
1404           return gimp_text_buffer_get_font_tag (buffer, value);
1405         }
1406       else if (! strcmp (attribute, GIMP_TEXT_ATTR_NAME_COLOR))
1407         {
1408           GimpRGB color;
1409           guint   r, g, b;
1410 
1411           sscanf (value, "#%02x%02x%02x", &r, &g, &b);
1412 
1413           gimp_rgb_set_uchar (&color, r, g, b);
1414 
1415           return gimp_text_buffer_get_color_tag (buffer, &color);
1416         }
1417     }
1418 
1419   return NULL;
1420 }
1421 
1422 void
gimp_text_buffer_set_insert_tags(GimpTextBuffer * buffer,GList * insert_tags,GList * remove_tags)1423 gimp_text_buffer_set_insert_tags (GimpTextBuffer *buffer,
1424                                   GList          *insert_tags,
1425                                   GList          *remove_tags)
1426 {
1427   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
1428 
1429   buffer->insert_tags_set = TRUE;
1430 
1431   g_list_free (buffer->insert_tags);
1432   g_list_free (buffer->remove_tags);
1433   buffer->insert_tags = insert_tags;
1434   buffer->remove_tags = remove_tags;
1435 }
1436 
1437 void
gimp_text_buffer_clear_insert_tags(GimpTextBuffer * buffer)1438 gimp_text_buffer_clear_insert_tags (GimpTextBuffer *buffer)
1439 {
1440   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
1441 
1442   buffer->insert_tags_set = FALSE;
1443 
1444   g_list_free (buffer->insert_tags);
1445   g_list_free (buffer->remove_tags);
1446   buffer->insert_tags = NULL;
1447   buffer->remove_tags = NULL;
1448 }
1449 
1450 void
gimp_text_buffer_insert(GimpTextBuffer * buffer,const gchar * text)1451 gimp_text_buffer_insert (GimpTextBuffer *buffer,
1452                          const gchar    *text)
1453 {
1454   GtkTextIter  iter, start;
1455   gint         start_offset;
1456   gboolean     insert_tags_set;
1457   GList       *insert_tags;
1458   GList       *remove_tags;
1459   GSList      *tags_off = NULL;
1460   GimpRGB      color;
1461 
1462   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
1463 
1464   gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (buffer), &iter,
1465                                     gtk_text_buffer_get_insert (GTK_TEXT_BUFFER (buffer)));
1466 
1467   start_offset = gtk_text_iter_get_offset (&iter);
1468 
1469   insert_tags_set = buffer->insert_tags_set;
1470   insert_tags     = buffer->insert_tags;
1471   remove_tags     = buffer->remove_tags;
1472 
1473   buffer->insert_tags_set = FALSE;
1474   buffer->insert_tags     = NULL;
1475   buffer->remove_tags     = NULL;
1476 
1477   tags_off = gtk_text_iter_get_toggled_tags (&iter, FALSE);
1478 
1479   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
1480 
1481   gtk_text_buffer_insert (GTK_TEXT_BUFFER (buffer), &iter, text, -1);
1482 
1483   gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (buffer), &start,
1484                                       start_offset);
1485 
1486   if (insert_tags_set)
1487     {
1488       GList *list;
1489 
1490       for (list = remove_tags; list; list = g_list_next (list))
1491         {
1492           GtkTextTag *tag = list->data;
1493 
1494           gtk_text_buffer_remove_tag (GTK_TEXT_BUFFER (buffer), tag,
1495                                       &start, &iter);
1496         }
1497 
1498       for (list = insert_tags; list; list = g_list_next (list))
1499         {
1500           GtkTextTag *tag = list->data;
1501 
1502           gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
1503                                      &start, &iter);
1504         }
1505     }
1506 
1507   if (tags_off)
1508     {
1509       GSList *slist;
1510 
1511       for (slist = tags_off; slist; slist = g_slist_next (slist))
1512         {
1513           GtkTextTag *tag = slist->data;
1514 
1515           if (! g_list_find (remove_tags, tag) &&
1516               ! g_list_find (buffer->kerning_tags, tag))
1517             {
1518               gtk_text_buffer_apply_tag (GTK_TEXT_BUFFER (buffer), tag,
1519                                          &start, &iter);
1520             }
1521         }
1522 
1523       g_slist_free (tags_off);
1524     }
1525 
1526   g_list_free (remove_tags);
1527   g_list_free (insert_tags);
1528 
1529   if (gimp_text_buffer_get_iter_color (buffer, &start, &color))
1530     {
1531       g_signal_emit (buffer, buffer_signals[COLOR_APPLIED], 0, &color);
1532     }
1533 
1534   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
1535 }
1536 
1537 gint
gimp_text_buffer_get_iter_index(GimpTextBuffer * buffer,GtkTextIter * iter,gboolean layout_index)1538 gimp_text_buffer_get_iter_index (GimpTextBuffer *buffer,
1539                                  GtkTextIter    *iter,
1540                                  gboolean        layout_index)
1541 {
1542   GtkTextIter  start;
1543   gchar       *string;
1544   gint         index;
1545 
1546   g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), 0);
1547 
1548   gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (buffer), &start);
1549 
1550   string = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer),
1551                                      &start, iter, TRUE);
1552   index = strlen (string);
1553   g_free (string);
1554 
1555   if (layout_index)
1556     {
1557       do
1558         {
1559           GSList *tags = gtk_text_iter_get_tags (&start);
1560           GSList *list;
1561 
1562           for (list = tags; list; list = g_slist_next (list))
1563             {
1564               GtkTextTag *tag = list->data;
1565 
1566               if (g_list_find (buffer->kerning_tags, tag))
1567                 {
1568                   index += WORD_JOINER_LENGTH;
1569 
1570                   break;
1571                 }
1572             }
1573 
1574           g_slist_free (tags);
1575 
1576           gtk_text_iter_forward_char (&start);
1577 
1578           /* We might have moved too far */
1579           if (gtk_text_iter_compare (&start, iter) > 0)
1580             start = *iter;
1581         }
1582       while (! gtk_text_iter_equal (&start, iter));
1583     }
1584 
1585   return index;
1586 }
1587 
1588 void
gimp_text_buffer_get_iter_at_index(GimpTextBuffer * buffer,GtkTextIter * iter,gint index,gboolean layout_index)1589 gimp_text_buffer_get_iter_at_index (GimpTextBuffer *buffer,
1590                                     GtkTextIter    *iter,
1591                                     gint            index,
1592                                     gboolean        layout_index)
1593 {
1594   GtkTextIter  start;
1595   GtkTextIter  end;
1596   gchar       *string;
1597 
1598   g_return_if_fail (GIMP_IS_TEXT_BUFFER (buffer));
1599 
1600   gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer), &start, &end);
1601 
1602   string = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer),
1603                                      &start, &end, TRUE);
1604 
1605   if (layout_index)
1606     {
1607       gchar *my_string = string;
1608       gint   my_index  = 0;
1609       gchar *tmp;
1610 
1611       do
1612         {
1613           GSList *tags = gtk_text_iter_get_tags (&start);
1614           GSList *list;
1615 
1616           tmp = g_utf8_next_char (my_string);
1617           my_index += (tmp - my_string);
1618           my_string = tmp;
1619 
1620           for (list = tags; list; list = g_slist_next (list))
1621             {
1622               GtkTextTag *tag = list->data;
1623 
1624               if (g_list_find (buffer->kerning_tags, tag))
1625                 {
1626                   index = MAX (0, index - WORD_JOINER_LENGTH);
1627 
1628                   break;
1629                 }
1630             }
1631 
1632           g_slist_free (tags);
1633 
1634           gtk_text_iter_forward_char (&start);
1635 
1636           /* We might have moved too far */
1637           if (gtk_text_iter_compare (&start, &end) > 0)
1638             start = end;
1639         }
1640       while (my_index < index &&
1641              ! gtk_text_iter_equal (&start, &end));
1642     }
1643 
1644   string[index] = '\0';
1645 
1646   gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (buffer), iter,
1647                                       g_utf8_strlen (string, -1));
1648 
1649   g_free (string);
1650 }
1651 
1652 gboolean
gimp_text_buffer_load(GimpTextBuffer * buffer,GFile * file,GError ** error)1653 gimp_text_buffer_load (GimpTextBuffer *buffer,
1654                        GFile          *file,
1655                        GError        **error)
1656 {
1657   GInputStream *input;
1658   gchar         buf[2048];
1659   gint          to_read;
1660   gsize         bytes_read;
1661   gsize         total_read = 0;
1662   gint          remaining  = 0;
1663   GtkTextIter   iter;
1664   GError       *my_error = NULL;
1665 
1666   g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), FALSE);
1667   g_return_val_if_fail (G_IS_FILE (file), FALSE);
1668   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1669 
1670   input = G_INPUT_STREAM (g_file_read (file, NULL, &my_error));
1671   if (! input)
1672     {
1673       g_set_error (error, my_error->domain, my_error->code,
1674                    _("Could not open '%s' for reading: %s"),
1675                    gimp_file_get_utf8_name (file), my_error->message);
1676       g_clear_error (&my_error);
1677       return FALSE;
1678     }
1679 
1680   gtk_text_buffer_begin_user_action (GTK_TEXT_BUFFER (buffer));
1681 
1682   gimp_text_buffer_set_text (buffer, NULL);
1683   gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &iter);
1684 
1685   do
1686     {
1687       gboolean    success;
1688       const char *leftover;
1689 
1690       to_read = sizeof (buf) - remaining - 1;
1691 
1692       success = g_input_stream_read_all (input, buf + remaining, to_read,
1693                                          &bytes_read, NULL, &my_error);
1694 
1695       total_read += bytes_read;
1696       buf[bytes_read + remaining] = '\0';
1697 
1698       g_utf8_validate (buf, bytes_read + remaining, &leftover);
1699 
1700       gtk_text_buffer_insert (GTK_TEXT_BUFFER (buffer), &iter,
1701                               buf, leftover - buf);
1702       gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (buffer), &iter);
1703 
1704       remaining = (buf + remaining + bytes_read) - leftover;
1705       memmove (buf, leftover, remaining);
1706 
1707       if (! success)
1708         {
1709           if (total_read > 0)
1710             {
1711               g_message (_("Input file '%s' appears truncated: %s"),
1712                          gimp_file_get_utf8_name (file),
1713                          my_error->message);
1714               g_clear_error (&my_error);
1715               break;
1716             }
1717 
1718           gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
1719           g_object_unref (input);
1720 
1721           g_propagate_error (error, my_error);
1722 
1723           return FALSE;
1724         }
1725     }
1726   while (remaining <= 6 && bytes_read == to_read);
1727 
1728   if (remaining)
1729     g_message (_("Invalid UTF-8 data in file '%s'."),
1730                gimp_file_get_utf8_name (file));
1731 
1732   gtk_text_buffer_end_user_action (GTK_TEXT_BUFFER (buffer));
1733   g_object_unref (input);
1734 
1735   return TRUE;
1736 }
1737 
1738 gboolean
gimp_text_buffer_save(GimpTextBuffer * buffer,GFile * file,gboolean selection_only,GError ** error)1739 gimp_text_buffer_save (GimpTextBuffer *buffer,
1740                        GFile          *file,
1741                        gboolean        selection_only,
1742                        GError        **error)
1743 {
1744   GOutputStream *output;
1745   GtkTextIter    start_iter;
1746   GtkTextIter    end_iter;
1747   gchar         *text_contents;
1748   GError        *my_error = NULL;
1749 
1750   g_return_val_if_fail (GIMP_IS_TEXT_BUFFER (buffer), FALSE);
1751   g_return_val_if_fail (G_IS_FILE (file), FALSE);
1752   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
1753 
1754   output = G_OUTPUT_STREAM (g_file_replace (file,
1755                                             NULL, FALSE, G_FILE_CREATE_NONE,
1756                                             NULL, error));
1757   if (! output)
1758     return FALSE;
1759 
1760   if (selection_only)
1761     gtk_text_buffer_get_selection_bounds (GTK_TEXT_BUFFER (buffer),
1762                                           &start_iter, &end_iter);
1763   else
1764     gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (buffer),
1765                                 &start_iter, &end_iter);
1766 
1767   text_contents = gtk_text_buffer_get_text (GTK_TEXT_BUFFER (buffer),
1768                                             &start_iter, &end_iter, TRUE);
1769 
1770   if (text_contents)
1771     {
1772       gint text_length = strlen (text_contents);
1773 
1774       if (! g_output_stream_write_all (output, text_contents, text_length,
1775                                        NULL, NULL, &my_error))
1776         {
1777           GCancellable *cancellable = g_cancellable_new ();
1778 
1779           g_set_error (error, my_error->domain, my_error->code,
1780                        _("Writing text file '%s' failed: %s"),
1781                        gimp_file_get_utf8_name (file), my_error->message);
1782           g_clear_error (&my_error);
1783           g_free (text_contents);
1784 
1785           /* Cancel the overwrite initiated by g_file_replace(). */
1786           g_cancellable_cancel (cancellable);
1787           g_output_stream_close (output, cancellable, NULL);
1788           g_object_unref (cancellable);
1789           g_object_unref (output);
1790 
1791           return FALSE;
1792         }
1793 
1794       g_free (text_contents);
1795     }
1796 
1797   g_object_unref (output);
1798 
1799   return TRUE;
1800 }
1801