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