Git init
[external/pango1.0.git] / pango / pango-attributes.c
1 /* Pango
2  * pango-attributes.c: Attributed text
3  *
4  * Copyright (C) 2000-2002 Red Hat Software
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23 #include <string.h>
24
25 #include "pango-attributes.h"
26 #include "pango-impl-utils.h"
27
28 struct _PangoAttrList
29 {
30   guint ref_count;
31   GSList *attributes;
32   GSList *attributes_tail;
33 };
34
35 struct _PangoAttrIterator
36 {
37   GSList *next_attribute;
38   GList *attribute_stack;
39   guint start_index;
40   guint end_index;
41 };
42
43 static PangoAttribute *pango_attr_color_new         (const PangoAttrClass *klass,
44                                                      guint16               red,
45                                                      guint16               green,
46                                                      guint16               blue);
47 static PangoAttribute *pango_attr_string_new        (const PangoAttrClass *klass,
48                                                      const char           *str);
49 static PangoAttribute *pango_attr_int_new           (const PangoAttrClass *klass,
50                                                      int                   value);
51 static PangoAttribute *pango_attr_float_new         (const PangoAttrClass *klass,
52                                                      double                value);
53 static PangoAttribute *pango_attr_size_new_internal (int                   size,
54                                                      gboolean              absolute);
55
56
57 static GHashTable *name_map = NULL;
58
59 /**
60  * pango_attr_type_register:
61  * @name: an identifier for the type
62  *
63  * Allocate a new attribute type ID.  The attribute type name can be accessed
64  * later by using pango_attr_type_get_name().
65  *
66  * Return value: the new type ID.
67  **/
68 PangoAttrType
69 pango_attr_type_register (const gchar *name)
70 {
71   static guint current_type = 0x1000000;
72   guint type = current_type++;
73
74   if (name)
75     {
76       if (G_UNLIKELY (!name_map))
77         name_map = g_hash_table_new (NULL, NULL);
78
79       g_hash_table_insert (name_map, GUINT_TO_POINTER (type), (gpointer) g_intern_string (name));
80     }
81
82   return type;
83 }
84
85 /**
86  * pango_attr_type_get_name:
87  * @type: an attribute type ID to fetch the name for
88  *
89  * Fetches the attribute type name passed in when registering the type using
90  * pango_attr_type_register().
91  *
92  * The returned value is an interned string (see g_intern_string() for what
93  * that means) that should not be modified or freed.
94  *
95  * Return value: the type ID name (which may be %NULL), or %NULL if @type is
96  * a built-in Pango attribute type or invalid. 
97  *
98  * Since: 1.22
99  **/
100 G_CONST_RETURN char *
101 pango_attr_type_get_name (PangoAttrType type)
102 {
103   const char *result = NULL;
104
105   if (name_map)
106     result = g_hash_table_lookup (name_map, GUINT_TO_POINTER ((guint) type));
107
108   return result;
109 }
110
111 /**
112  * pango_attribute_init:
113  * @attr: a #PangoAttribute
114  * @klass: a #PangoAttributeClass
115  *
116  * Initializes @attr's klass to @klass,
117  * it's start_index to %PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING
118  * and end_index to %PANGO_ATTR_INDEX_TO_TEXT_END
119  * such that the attribute applies
120  * to the entire text by default.
121  *
122  * Since: 1.20
123  **/
124 void
125 pango_attribute_init (PangoAttribute       *attr,
126                       const PangoAttrClass *klass)
127 {
128   g_return_if_fail (attr != NULL);
129   g_return_if_fail (klass != NULL);
130
131   attr->klass = klass;
132   attr->start_index = PANGO_ATTR_INDEX_FROM_TEXT_BEGINNING;
133   attr->end_index   = PANGO_ATTR_INDEX_TO_TEXT_END;
134 }
135
136 /**
137  * pango_attribute_copy:
138  * @attr: a #PangoAttribute
139  *
140  * Make a copy of an attribute.
141  *
142  * Return value: the newly allocated #PangoAttribute, which should be
143  *               freed with pango_attribute_destroy().
144  **/
145 PangoAttribute *
146 pango_attribute_copy (const PangoAttribute *attr)
147 {
148   PangoAttribute *result;
149
150   g_return_val_if_fail (attr != NULL, NULL);
151
152   result = attr->klass->copy (attr);
153   result->start_index = attr->start_index;
154   result->end_index = attr->end_index;
155
156   return result;
157 }
158
159 /**
160  * pango_attribute_destroy:
161  * @attr: a #PangoAttribute.
162  *
163  * Destroy a #PangoAttribute and free all associated memory.
164  **/
165 void
166 pango_attribute_destroy (PangoAttribute *attr)
167 {
168   g_return_if_fail (attr != NULL);
169
170   attr->klass->destroy (attr);
171 }
172
173 /**
174  * pango_attribute_equal:
175  * @attr1: a #PangoAttribute
176  * @attr2: another #PangoAttribute
177  *
178  * Compare two attributes for equality. This compares only the
179  * actual value of the two attributes and not the ranges that the
180  * attributes apply to.
181  *
182  * Return value: %TRUE if the two attributes have the same value.
183  **/
184 gboolean
185 pango_attribute_equal (const PangoAttribute *attr1,
186                        const PangoAttribute *attr2)
187 {
188   g_return_val_if_fail (attr1 != NULL, FALSE);
189   g_return_val_if_fail (attr2 != NULL, FALSE);
190
191   if (attr1->klass->type != attr2->klass->type)
192     return FALSE;
193
194   return attr1->klass->equal (attr1, attr2);
195 }
196
197 static PangoAttribute *
198 pango_attr_string_copy (const PangoAttribute *attr)
199 {
200   return pango_attr_string_new (attr->klass, ((PangoAttrString *)attr)->value);
201 }
202
203 static void
204 pango_attr_string_destroy (PangoAttribute *attr)
205 {
206   PangoAttrString *sattr = (PangoAttrString *)attr;
207
208   g_free (sattr->value);
209   g_slice_free (PangoAttrString, sattr);
210 }
211
212 static gboolean
213 pango_attr_string_equal (const PangoAttribute *attr1,
214                          const PangoAttribute *attr2)
215 {
216   return strcmp (((PangoAttrString *)attr1)->value, ((PangoAttrString *)attr2)->value) == 0;
217 }
218
219 static PangoAttribute *
220 pango_attr_string_new (const PangoAttrClass *klass,
221                        const char           *str)
222 {
223   PangoAttrString *result = g_slice_new (PangoAttrString);
224   pango_attribute_init (&result->attr, klass);
225   result->value = g_strdup (str);
226
227   return (PangoAttribute *)result;
228 }
229
230 /**
231  * pango_attr_family_new:
232  * @family: the family or comma separated list of families
233  *
234  * Create a new font family attribute.
235  *
236  * Return value: the newly allocated #PangoAttribute, which should be
237  *               freed with pango_attribute_destroy().
238  **/
239 PangoAttribute *
240 pango_attr_family_new (const char *family)
241 {
242   static const PangoAttrClass klass = {
243     PANGO_ATTR_FAMILY,
244     pango_attr_string_copy,
245     pango_attr_string_destroy,
246     pango_attr_string_equal
247   };
248
249   g_return_val_if_fail (family != NULL, NULL);
250
251   return pango_attr_string_new (&klass, family);
252 }
253
254 static PangoAttribute *
255 pango_attr_language_copy (const PangoAttribute *attr)
256 {
257   return pango_attr_language_new (((PangoAttrLanguage *)attr)->value);
258 }
259
260 static void
261 pango_attr_language_destroy (PangoAttribute *attr)
262 {
263   PangoAttrLanguage *lattr = (PangoAttrLanguage *)attr;
264
265   g_slice_free (PangoAttrLanguage, lattr);
266 }
267
268 static gboolean
269 pango_attr_language_equal (const PangoAttribute *attr1,
270                            const PangoAttribute *attr2)
271 {
272   return ((PangoAttrLanguage *)attr1)->value == ((PangoAttrLanguage *)attr2)->value;
273 }
274
275 /**
276  * pango_attr_language_new:
277  * @language: language tag
278  *
279  * Create a new language tag attribute.
280  *
281  * Return value: the newly allocated #PangoAttribute, which should be
282  *               freed with pango_attribute_destroy().
283  **/
284 PangoAttribute *
285 pango_attr_language_new (PangoLanguage *language)
286 {
287   PangoAttrLanguage *result;
288
289   static const PangoAttrClass klass = {
290     PANGO_ATTR_LANGUAGE,
291     pango_attr_language_copy,
292     pango_attr_language_destroy,
293     pango_attr_language_equal
294   };
295
296   result = g_slice_new (PangoAttrLanguage);
297   pango_attribute_init (&result->attr, &klass);
298   result->value = language;
299
300   return (PangoAttribute *)result;
301 }
302
303 static PangoAttribute *
304 pango_attr_color_copy (const PangoAttribute *attr)
305 {
306   const PangoAttrColor *color_attr = (PangoAttrColor *)attr;
307
308   return pango_attr_color_new (attr->klass,
309                                color_attr->color.red,
310                                color_attr->color.green,
311                                color_attr->color.blue);
312 }
313
314 static void
315 pango_attr_color_destroy (PangoAttribute *attr)
316 {
317   PangoAttrColor *cattr = (PangoAttrColor *)attr;
318
319   g_slice_free (PangoAttrColor, cattr);
320 }
321
322 static gboolean
323 pango_attr_color_equal (const PangoAttribute *attr1,
324                         const PangoAttribute *attr2)
325 {
326   const PangoAttrColor *color_attr1 = (const PangoAttrColor *)attr1;
327   const PangoAttrColor *color_attr2 = (const PangoAttrColor *)attr2;
328
329   return (color_attr1->color.red == color_attr2->color.red &&
330           color_attr1->color.blue == color_attr2->color.blue &&
331           color_attr1->color.green == color_attr2->color.green);
332 }
333
334 static PangoAttribute *
335 pango_attr_color_new (const PangoAttrClass *klass,
336                       guint16               red,
337                       guint16               green,
338                       guint16               blue)
339 {
340   PangoAttrColor *result = g_slice_new (PangoAttrColor);
341   pango_attribute_init (&result->attr, klass);
342   result->color.red = red;
343   result->color.green = green;
344   result->color.blue = blue;
345
346   return (PangoAttribute *)result;
347 }
348
349 /**
350  * pango_attr_foreground_new:
351  * @red: the red value (ranging from 0 to 65535)
352  * @green: the green value
353  * @blue: the blue value
354  *
355  * Create a new foreground color attribute.
356  *
357  * Return value: the newly allocated #PangoAttribute, which should be
358  *               freed with pango_attribute_destroy().
359  **/
360 PangoAttribute *
361 pango_attr_foreground_new (guint16 red,
362                            guint16 green,
363                            guint16 blue)
364 {
365   static const PangoAttrClass klass = {
366     PANGO_ATTR_FOREGROUND,
367     pango_attr_color_copy,
368     pango_attr_color_destroy,
369     pango_attr_color_equal
370   };
371
372   return pango_attr_color_new (&klass, red, green, blue);
373 }
374
375 /**
376  * pango_attr_background_new:
377  * @red: the red value (ranging from 0 to 65535)
378  * @green: the green value
379  * @blue: the blue value
380  *
381  * Create a new background color attribute.
382  *
383  * Return value: the newly allocated #PangoAttribute, which should be
384  *               freed with pango_attribute_destroy().
385  **/
386 PangoAttribute *
387 pango_attr_background_new (guint16 red,
388                            guint16 green,
389                            guint16 blue)
390 {
391   static const PangoAttrClass klass = {
392     PANGO_ATTR_BACKGROUND,
393     pango_attr_color_copy,
394     pango_attr_color_destroy,
395     pango_attr_color_equal
396   };
397
398   return pango_attr_color_new (&klass, red, green, blue);
399 }
400
401 static PangoAttribute *
402 pango_attr_int_copy (const PangoAttribute *attr)
403 {
404   const PangoAttrInt *int_attr = (PangoAttrInt *)attr;
405
406   return pango_attr_int_new (attr->klass, int_attr->value);
407 }
408
409 static void
410 pango_attr_int_destroy (PangoAttribute *attr)
411 {
412   PangoAttrInt *iattr = (PangoAttrInt *)attr;
413
414   g_slice_free (PangoAttrInt, iattr);
415 }
416
417 static gboolean
418 pango_attr_int_equal (const PangoAttribute *attr1,
419                       const PangoAttribute *attr2)
420 {
421   const PangoAttrInt *int_attr1 = (const PangoAttrInt *)attr1;
422   const PangoAttrInt *int_attr2 = (const PangoAttrInt *)attr2;
423
424   return (int_attr1->value == int_attr2->value);
425 }
426
427 static PangoAttribute *
428 pango_attr_int_new (const PangoAttrClass *klass,
429                     int                   value)
430 {
431   PangoAttrInt *result = g_slice_new (PangoAttrInt);
432   pango_attribute_init (&result->attr, klass);
433   result->value = value;
434
435   return (PangoAttribute *)result;
436 }
437
438 static PangoAttribute *
439 pango_attr_float_copy (const PangoAttribute *attr)
440 {
441   const PangoAttrFloat *float_attr = (PangoAttrFloat *)attr;
442
443   return pango_attr_float_new (attr->klass, float_attr->value);
444 }
445
446 static void
447 pango_attr_float_destroy (PangoAttribute *attr)
448 {
449   PangoAttrFloat *fattr = (PangoAttrFloat *)attr;
450
451   g_slice_free (PangoAttrFloat, fattr);
452 }
453
454 static gboolean
455 pango_attr_float_equal (const PangoAttribute *attr1,
456                         const PangoAttribute *attr2)
457 {
458   const PangoAttrFloat *float_attr1 = (const PangoAttrFloat *)attr1;
459   const PangoAttrFloat *float_attr2 = (const PangoAttrFloat *)attr2;
460
461   return (float_attr1->value == float_attr2->value);
462 }
463
464 static PangoAttribute*
465 pango_attr_float_new  (const PangoAttrClass *klass,
466                        double                value)
467 {
468   PangoAttrFloat *result = g_slice_new (PangoAttrFloat);
469   pango_attribute_init (&result->attr, klass);
470   result->value = value;
471
472   return (PangoAttribute *)result;
473 }
474
475 static PangoAttribute *
476 pango_attr_size_copy (const PangoAttribute *attr)
477 {
478   const PangoAttrSize *size_attr = (PangoAttrSize *)attr;
479
480   if (attr->klass->type == PANGO_ATTR_ABSOLUTE_SIZE)
481     return pango_attr_size_new_absolute (size_attr->size);
482   else
483     return pango_attr_size_new (size_attr->size);
484 }
485
486 static void
487 pango_attr_size_destroy (PangoAttribute *attr)
488 {
489   PangoAttrSize *sattr = (PangoAttrSize *)attr;
490
491   g_slice_free (PangoAttrSize, sattr);
492 }
493
494 static gboolean
495 pango_attr_size_equal (const PangoAttribute *attr1,
496                        const PangoAttribute *attr2)
497 {
498   const PangoAttrSize *size_attr1 = (const PangoAttrSize *)attr1;
499   const PangoAttrSize *size_attr2 = (const PangoAttrSize *)attr2;
500
501   return size_attr1->size == size_attr2->size;
502 }
503
504 static PangoAttribute *
505 pango_attr_size_new_internal (int size,
506                               gboolean absolute)
507 {
508   PangoAttrSize *result;
509
510   static const PangoAttrClass klass = {
511     PANGO_ATTR_SIZE,
512     pango_attr_size_copy,
513     pango_attr_size_destroy,
514     pango_attr_size_equal
515   };
516   static const PangoAttrClass absolute_klass = {
517     PANGO_ATTR_ABSOLUTE_SIZE,
518     pango_attr_size_copy,
519     pango_attr_size_destroy,
520     pango_attr_size_equal
521   };
522
523   result = g_slice_new (PangoAttrSize);
524   pango_attribute_init (&result->attr, absolute ? &absolute_klass : &klass);
525   result->size = size;
526   result->absolute = absolute;
527
528   return (PangoAttribute *)result;
529 }
530
531 /**
532  * pango_attr_size_new:
533  * @size: the font size, in %PANGO_SCALE<!-- -->ths of a point.
534  *
535  * Create a new font-size attribute in fractional points.
536  *
537  * Return value: the newly allocated #PangoAttribute, which should be
538  *               freed with pango_attribute_destroy().
539  **/
540 PangoAttribute *
541 pango_attr_size_new (int size)
542 {
543   return pango_attr_size_new_internal (size, FALSE);
544 }
545
546 /**
547  * pango_attr_size_new_absolute:
548  * @size: the font size, in %PANGO_SCALE<!-- -->ths of a device unit.
549  *
550  * Create a new font-size attribute in device units.
551  *
552  * Return value: the newly allocated #PangoAttribute, which should be
553  *               freed with pango_attribute_destroy().
554  *
555  * Since: 1.8
556  **/
557 PangoAttribute *
558 pango_attr_size_new_absolute (int size)
559 {
560   return pango_attr_size_new_internal (size, TRUE);
561 }
562
563 /**
564  * pango_attr_style_new:
565  * @style: the slant style
566  *
567  * Create a new font slant style attribute.
568  *
569  * Return value: the newly allocated #PangoAttribute, which should be
570  *               freed with pango_attribute_destroy().
571  **/
572 PangoAttribute *
573 pango_attr_style_new (PangoStyle style)
574 {
575   static const PangoAttrClass klass = {
576     PANGO_ATTR_STYLE,
577     pango_attr_int_copy,
578     pango_attr_int_destroy,
579     pango_attr_int_equal
580   };
581
582   return pango_attr_int_new (&klass, (int)style);
583 }
584
585 /**
586  * pango_attr_weight_new:
587  * @weight: the weight
588  *
589  * Create a new font weight attribute.
590  *
591  * Return value: the newly allocated #PangoAttribute, which should be
592  *               freed with pango_attribute_destroy().
593  **/
594 PangoAttribute *
595 pango_attr_weight_new (PangoWeight weight)
596 {
597   static const PangoAttrClass klass = {
598     PANGO_ATTR_WEIGHT,
599     pango_attr_int_copy,
600     pango_attr_int_destroy,
601     pango_attr_int_equal
602   };
603
604   return pango_attr_int_new (&klass, (int)weight);
605 }
606
607 /**
608  * pango_attr_variant_new:
609  * @variant: the variant
610  *
611  * Create a new font variant attribute (normal or small caps)
612  *
613  * Return value: the newly allocated #PangoAttribute, which should be
614  *               freed with pango_attribute_destroy().
615  **/
616 PangoAttribute *
617 pango_attr_variant_new (PangoVariant variant)
618 {
619   static const PangoAttrClass klass = {
620     PANGO_ATTR_VARIANT,
621     pango_attr_int_copy,
622     pango_attr_int_destroy,
623     pango_attr_int_equal
624   };
625
626   return pango_attr_int_new (&klass, (int)variant);
627 }
628
629 /**
630  * pango_attr_stretch_new:
631  * @stretch: the stretch
632  *
633  * Create a new font stretch attribute
634  *
635  * Return value: the newly allocated #PangoAttribute, which should be
636  *               freed with pango_attribute_destroy().
637  **/
638 PangoAttribute *
639 pango_attr_stretch_new (PangoStretch  stretch)
640 {
641   static const PangoAttrClass klass = {
642     PANGO_ATTR_STRETCH,
643     pango_attr_int_copy,
644     pango_attr_int_destroy,
645     pango_attr_int_equal
646   };
647
648   return pango_attr_int_new (&klass, (int)stretch);
649 }
650
651 static PangoAttribute *
652 pango_attr_font_desc_copy (const PangoAttribute *attr)
653 {
654   const PangoAttrFontDesc *desc_attr = (const PangoAttrFontDesc *)attr;
655
656   return pango_attr_font_desc_new (desc_attr->desc);
657 }
658
659 static void
660 pango_attr_font_desc_destroy (PangoAttribute *attr)
661 {
662   PangoAttrFontDesc *desc_attr = (PangoAttrFontDesc *)attr;
663
664   pango_font_description_free (desc_attr->desc);
665   g_slice_free (PangoAttrFontDesc, desc_attr);
666 }
667
668 static gboolean
669 pango_attr_font_desc_equal (const PangoAttribute *attr1,
670                             const PangoAttribute *attr2)
671 {
672   const PangoAttrFontDesc *desc_attr1 = (const PangoAttrFontDesc *)attr1;
673   const PangoAttrFontDesc *desc_attr2 = (const PangoAttrFontDesc *)attr2;
674
675   return pango_font_description_get_set_fields (desc_attr1->desc) ==
676          pango_font_description_get_set_fields (desc_attr2->desc) &&
677          pango_font_description_equal (desc_attr1->desc, desc_attr2->desc);
678 }
679
680 /**
681  * pango_attr_font_desc_new:
682  * @desc: the font description
683  *
684  * Create a new font description attribute. This attribute
685  * allows setting family, style, weight, variant, stretch,
686  * and size simultaneously.
687  *
688  * Return value:  the newly allocated #PangoAttribute, which should be
689  *               freed with pango_attribute_destroy().
690  **/
691 PangoAttribute *
692 pango_attr_font_desc_new (const PangoFontDescription *desc)
693 {
694   static const PangoAttrClass klass = {
695     PANGO_ATTR_FONT_DESC,
696     pango_attr_font_desc_copy,
697     pango_attr_font_desc_destroy,
698     pango_attr_font_desc_equal
699   };
700
701   PangoAttrFontDesc *result = g_slice_new (PangoAttrFontDesc);
702   pango_attribute_init (&result->attr, &klass);
703   result->desc = pango_font_description_copy (desc);
704
705   return (PangoAttribute *)result;
706 }
707
708
709 /**
710  * pango_attr_underline_new:
711  * @underline: the underline style.
712  *
713  * Create a new underline-style attribute.
714  *
715  * Return value: the newly allocated #PangoAttribute, which should be
716  *               freed with pango_attribute_destroy().
717  **/
718 PangoAttribute *
719 pango_attr_underline_new (PangoUnderline underline)
720 {
721   static const PangoAttrClass klass = {
722     PANGO_ATTR_UNDERLINE,
723     pango_attr_int_copy,
724     pango_attr_int_destroy,
725     pango_attr_int_equal
726   };
727
728   return pango_attr_int_new (&klass, (int)underline);
729 }
730
731 /**
732  * pango_attr_underline_color_new:
733  * @red: the red value (ranging from 0 to 65535)
734  * @green: the green value
735  * @blue: the blue value
736  *
737  * Create a new underline color attribute. This attribute
738  * modifies the color of underlines. If not set, underlines
739  * will use the foreground color.
740  *
741  * Return value: the newly allocated #PangoAttribute, which should be
742  *               freed with pango_attribute_destroy().
743  *
744  * Since: 1.8
745  **/
746 PangoAttribute *
747 pango_attr_underline_color_new (guint16 red,
748                                 guint16 green,
749                                 guint16 blue)
750 {
751   static const PangoAttrClass klass = {
752     PANGO_ATTR_UNDERLINE_COLOR,
753     pango_attr_color_copy,
754     pango_attr_color_destroy,
755     pango_attr_color_equal
756   };
757
758   return pango_attr_color_new (&klass, red, green, blue);
759 }
760
761 /**
762  * pango_attr_strikethrough_new:
763  * @strikethrough: %TRUE if the text should be struck-through.
764  *
765  * Create a new strike-through attribute.
766  *
767  * Return value: the newly allocated #PangoAttribute, which should be
768  *               freed with pango_attribute_destroy().
769  **/
770 PangoAttribute *
771 pango_attr_strikethrough_new (gboolean strikethrough)
772 {
773   static const PangoAttrClass klass = {
774     PANGO_ATTR_STRIKETHROUGH,
775     pango_attr_int_copy,
776     pango_attr_int_destroy,
777     pango_attr_int_equal
778   };
779
780   return pango_attr_int_new (&klass, (int)strikethrough);
781 }
782
783 /**
784  * pango_attr_strikethrough_color_new:
785  * @red: the red value (ranging from 0 to 65535)
786  * @green: the green value
787  * @blue: the blue value
788  *
789  * Create a new strikethrough color attribute. This attribute
790  * modifies the color of strikethrough lines. If not set, strikethrough
791  * lines will use the foreground color.
792  *
793  * Return value: the newly allocated #PangoAttribute, which should be
794  *               freed with pango_attribute_destroy().
795  *
796  * Since: 1.8
797  **/
798 PangoAttribute *
799 pango_attr_strikethrough_color_new (guint16 red,
800                                     guint16 green,
801                                     guint16 blue)
802 {
803   static const PangoAttrClass klass = {
804     PANGO_ATTR_STRIKETHROUGH_COLOR,
805     pango_attr_color_copy,
806     pango_attr_color_destroy,
807     pango_attr_color_equal
808   };
809
810   return pango_attr_color_new (&klass, red, green, blue);
811 }
812
813 /**
814  * pango_attr_rise_new:
815  * @rise: the amount that the text should be displaced vertically,
816  *        in Pango units. Positive values displace the text upwards.
817  *
818  * Create a new baseline displacement attribute.
819  *
820  * Return value: the newly allocated #PangoAttribute, which should be
821  *               freed with pango_attribute_destroy().
822  **/
823 PangoAttribute *
824 pango_attr_rise_new (int rise)
825 {
826   static const PangoAttrClass klass = {
827     PANGO_ATTR_RISE,
828     pango_attr_int_copy,
829     pango_attr_int_destroy,
830     pango_attr_int_equal
831   };
832
833   return pango_attr_int_new (&klass, (int)rise);
834 }
835
836 /**
837  * pango_attr_scale_new:
838  * @scale_factor: factor to scale the font
839  *
840  * Create a new font size scale attribute. The base font for the
841  * affected text will have its size multiplied by @scale_factor.
842  *
843  * Return value: the newly allocated #PangoAttribute, which should be
844  *               freed with pango_attribute_destroy().
845  **/
846 PangoAttribute*
847 pango_attr_scale_new (double scale_factor)
848 {
849   static const PangoAttrClass klass = {
850     PANGO_ATTR_SCALE,
851     pango_attr_float_copy,
852     pango_attr_float_destroy,
853     pango_attr_float_equal
854   };
855
856   return pango_attr_float_new (&klass, scale_factor);
857 }
858
859 /**
860  * pango_attr_fallback_new:
861  * @enable_fallback: %TRUE if we should fall back on other fonts
862  *                   for characters the active font is missing.
863  *
864  * Create a new font fallback attribute.
865  *
866  * If fallback is disabled, characters will only be used from the
867  * closest matching font on the system. No fallback will be done to
868  * other fonts on the system that might contain the characters in the
869  * text.
870  *
871  * Return value: the newly allocated #PangoAttribute, which should be
872  *               freed with pango_attribute_destroy().
873  *
874  * Since: 1.4
875  **/
876 PangoAttribute *
877 pango_attr_fallback_new (gboolean enable_fallback)
878 {
879   static const PangoAttrClass klass = {
880     PANGO_ATTR_FALLBACK,
881     pango_attr_int_copy,
882     pango_attr_int_destroy,
883     pango_attr_int_equal,
884   };
885
886   return pango_attr_int_new (&klass, (int)enable_fallback);
887 }
888
889 /**
890  * pango_attr_letter_spacing_new:
891  * @letter_spacing: amount of extra space to add between graphemes
892  *   of the text, in Pango units.
893  *
894  * Create a new letter-spacing attribute.
895  *
896  * Return value: the newly allocated #PangoAttribute, which should be
897  *               freed with pango_attribute_destroy().
898  *
899  * Since: 1.6
900  **/
901 PangoAttribute *
902 pango_attr_letter_spacing_new (int letter_spacing)
903 {
904   static const PangoAttrClass klass = {
905     PANGO_ATTR_LETTER_SPACING,
906     pango_attr_int_copy,
907     pango_attr_int_destroy,
908     pango_attr_int_equal
909   };
910
911   return pango_attr_int_new (&klass, letter_spacing);
912 }
913
914 static PangoAttribute *
915 pango_attr_shape_copy (const PangoAttribute *attr)
916 {
917   const PangoAttrShape *shape_attr = (PangoAttrShape *)attr;
918   gpointer data;
919
920   if (shape_attr->copy_func)
921     data = shape_attr->copy_func (shape_attr->data);
922   else
923     data = shape_attr->data;
924
925   return pango_attr_shape_new_with_data (&shape_attr->ink_rect, &shape_attr->logical_rect,
926                                          data, shape_attr->copy_func, shape_attr->destroy_func);
927 }
928
929 static void
930 pango_attr_shape_destroy (PangoAttribute *attr)
931 {
932   PangoAttrShape *shape_attr = (PangoAttrShape *)attr;
933
934   if (shape_attr->destroy_func)
935     shape_attr->destroy_func (shape_attr->data);
936
937   g_slice_free (PangoAttrShape, shape_attr);
938 }
939
940 static gboolean
941 pango_attr_shape_equal (const PangoAttribute *attr1,
942                         const PangoAttribute *attr2)
943 {
944   const PangoAttrShape *shape_attr1 = (const PangoAttrShape *)attr1;
945   const PangoAttrShape *shape_attr2 = (const PangoAttrShape *)attr2;
946
947   return (shape_attr1->logical_rect.x == shape_attr2->logical_rect.x &&
948           shape_attr1->logical_rect.y == shape_attr2->logical_rect.y &&
949           shape_attr1->logical_rect.width == shape_attr2->logical_rect.width &&
950           shape_attr1->logical_rect.height == shape_attr2->logical_rect.height &&
951           shape_attr1->ink_rect.x == shape_attr2->ink_rect.x &&
952           shape_attr1->ink_rect.y == shape_attr2->ink_rect.y &&
953           shape_attr1->ink_rect.width == shape_attr2->ink_rect.width &&
954           shape_attr1->ink_rect.height == shape_attr2->ink_rect.height &&
955           shape_attr1->data == shape_attr2->data);
956 }
957
958 /**
959  * pango_attr_shape_new_with_data:
960  * @ink_rect:     ink rectangle to assign to each character
961  * @logical_rect: logical rectangle to assign to each character
962  * @data:         user data pointer
963  * @copy_func:    function to copy @data when the attribute
964  *                is copied. If %NULL, @data is simply copied
965  *                as a pointer.
966  * @destroy_func: function to free @data when the attribute
967  *                is freed, or %NULL
968  *
969  * Like pango_attr_shape_new(), but a user data pointer is also
970  * provided; this pointer can be accessed when later
971  * rendering the glyph.
972  *
973  * Return value: the newly allocated #PangoAttribute, which should be
974  *               freed with pango_attribute_destroy().
975  *
976  * Since: 1.8
977  **/
978 PangoAttribute *
979 pango_attr_shape_new_with_data (const PangoRectangle  *ink_rect,
980                                 const PangoRectangle  *logical_rect,
981                                 gpointer               data,
982                                 PangoAttrDataCopyFunc  copy_func,
983                                 GDestroyNotify         destroy_func)
984 {
985   static const PangoAttrClass klass = {
986     PANGO_ATTR_SHAPE,
987     pango_attr_shape_copy,
988     pango_attr_shape_destroy,
989     pango_attr_shape_equal
990   };
991
992   PangoAttrShape *result;
993
994   g_return_val_if_fail (ink_rect != NULL, NULL);
995   g_return_val_if_fail (logical_rect != NULL, NULL);
996
997   result = g_slice_new (PangoAttrShape);
998   pango_attribute_init (&result->attr, &klass);
999   result->ink_rect = *ink_rect;
1000   result->logical_rect = *logical_rect;
1001   result->data = data;
1002   result->copy_func = copy_func;
1003   result->destroy_func =  destroy_func;
1004
1005   return (PangoAttribute *)result;
1006 }
1007
1008 /**
1009  * pango_attr_shape_new:
1010  * @ink_rect:     ink rectangle to assign to each character
1011  * @logical_rect: logical rectangle to assign to each character
1012  *
1013  * Create a new shape attribute. A shape is used to impose a
1014  * particular ink and logical rectangle on the result of shaping a
1015  * particular glyph. This might be used, for instance, for
1016  * embedding a picture or a widget inside a #PangoLayout.
1017  *
1018  * Return value: the newly allocated #PangoAttribute, which should be
1019  *               freed with pango_attribute_destroy().
1020  **/
1021 PangoAttribute *
1022 pango_attr_shape_new (const PangoRectangle *ink_rect,
1023                       const PangoRectangle *logical_rect)
1024 {
1025   g_return_val_if_fail (ink_rect != NULL, NULL);
1026   g_return_val_if_fail (logical_rect != NULL, NULL);
1027
1028   return pango_attr_shape_new_with_data (ink_rect, logical_rect,
1029                                          NULL, NULL, NULL);
1030 }
1031
1032 /**
1033  * pango_attr_gravity_new:
1034  * @gravity: the gravity value; should not be %PANGO_GRAVITY_AUTO.
1035  *
1036  * Create a new gravity attribute.
1037  *
1038  * Return value: the newly allocated #PangoAttribute, which should be
1039  *               freed with pango_attribute_destroy().
1040  *
1041  * Since: 1.16
1042  **/
1043 PangoAttribute *
1044 pango_attr_gravity_new (PangoGravity gravity)
1045 {
1046   static const PangoAttrClass klass = {
1047     PANGO_ATTR_GRAVITY,
1048     pango_attr_int_copy,
1049     pango_attr_int_destroy,
1050     pango_attr_int_equal
1051   };
1052
1053   g_return_val_if_fail (gravity != PANGO_GRAVITY_AUTO, NULL);
1054
1055   return pango_attr_int_new (&klass, (int)gravity);
1056 }
1057
1058 /**
1059  * pango_attr_gravity_hint_new:
1060  * @hint: the gravity hint value.
1061  *
1062  * Create a new gravity hint attribute.
1063  *
1064  * Return value: the newly allocated #PangoAttribute, which should be
1065  *               freed with pango_attribute_destroy().
1066  *
1067  * Since: 1.16
1068  **/
1069 PangoAttribute *
1070 pango_attr_gravity_hint_new (PangoGravityHint hint)
1071 {
1072   static const PangoAttrClass klass = {
1073     PANGO_ATTR_GRAVITY_HINT,
1074     pango_attr_int_copy,
1075     pango_attr_int_destroy,
1076     pango_attr_int_equal
1077   };
1078
1079   return pango_attr_int_new (&klass, (int)hint);
1080 }
1081
1082
1083 /*
1084  * Attribute List
1085  */
1086
1087 GType
1088 pango_attr_list_get_type (void)
1089 {
1090   static GType our_type = 0;
1091
1092   if (G_UNLIKELY (our_type == 0))
1093     our_type = g_boxed_type_register_static (I_("PangoAttrList"),
1094                                              (GBoxedCopyFunc) pango_attr_list_copy,
1095                                              (GBoxedFreeFunc) pango_attr_list_unref);
1096
1097   return our_type;
1098 }
1099
1100 /**
1101  * pango_attr_list_new:
1102  *
1103  * Create a new empty attribute list with a reference count of one.
1104  *
1105  * Return value: the newly allocated #PangoAttrList, which should
1106  *               be freed with pango_attr_list_unref().
1107  **/
1108 PangoAttrList *
1109 pango_attr_list_new (void)
1110 {
1111   PangoAttrList *list = g_slice_new (PangoAttrList);
1112
1113   list->ref_count = 1;
1114   list->attributes = NULL;
1115   list->attributes_tail = NULL;
1116
1117   return list;
1118 }
1119
1120 /**
1121  * pango_attr_list_ref:
1122  * @list: a #PangoAttrList, may be %NULL
1123  *
1124  * Increase the reference count of the given attribute list by one.
1125  *
1126  * Return value: The attribute list passed in
1127  *
1128  * Since: 1.10
1129  **/
1130 PangoAttrList *
1131 pango_attr_list_ref (PangoAttrList *list)
1132 {
1133   if (list == NULL)
1134     return NULL;
1135
1136   g_atomic_int_inc ((int *) &list->ref_count);
1137
1138   return list;
1139 }
1140
1141 /**
1142  * pango_attr_list_unref:
1143  * @list: a #PangoAttrList, may be %NULL
1144  *
1145  * Decrease the reference count of the given attribute list by one.
1146  * If the result is zero, free the attribute list and the attributes
1147  * it contains.
1148  **/
1149 void
1150 pango_attr_list_unref (PangoAttrList *list)
1151 {
1152   GSList *tmp_list;
1153
1154   if (list == NULL)
1155     return;
1156
1157   g_return_if_fail (list->ref_count > 0);
1158
1159   if (g_atomic_int_dec_and_test ((int *) &list->ref_count))
1160     {
1161       tmp_list = list->attributes;
1162       while (tmp_list)
1163         {
1164           PangoAttribute *attr = tmp_list->data;
1165           tmp_list = tmp_list->next;
1166
1167           attr->klass->destroy (attr);
1168         }
1169
1170       g_slist_free (list->attributes);
1171
1172       g_slice_free (PangoAttrList, list);
1173     }
1174 }
1175
1176 /**
1177  * pango_attr_list_copy:
1178  * @list: a #PangoAttrList, may be %NULL
1179  *
1180  * Copy @list and return an identical new list.
1181  *
1182  * Return value: the newly allocated #PangoAttrList, with a
1183  *               reference count of one, which should
1184  *               be freed with pango_attr_list_unref().
1185  *               Returns %NULL if @list was %NULL.
1186  **/
1187 PangoAttrList *
1188 pango_attr_list_copy (PangoAttrList *list)
1189 {
1190   PangoAttrList *new;
1191   GSList *iter;
1192   GSList *new_attrs;
1193
1194   if (list == NULL)
1195     return NULL;
1196
1197   new = pango_attr_list_new ();
1198
1199   iter = list->attributes;
1200   new_attrs = NULL;
1201   while (iter != NULL)
1202     {
1203       new_attrs = g_slist_prepend (new_attrs,
1204                                    pango_attribute_copy (iter->data));
1205
1206       iter = g_slist_next (iter);
1207     }
1208
1209   /* we're going to reverse the nodes, so head becomes tail */
1210   new->attributes_tail = new_attrs;
1211   new->attributes = g_slist_reverse (new_attrs);
1212
1213   return new;
1214 }
1215
1216 static void
1217 pango_attr_list_insert_internal (PangoAttrList  *list,
1218                                  PangoAttribute *attr,
1219                                  gboolean        before)
1220 {
1221   GSList *tmp_list, *prev, *link;
1222   guint start_index = attr->start_index;
1223
1224   if (!list->attributes)
1225     {
1226       list->attributes = g_slist_prepend (NULL, attr);
1227       list->attributes_tail = list->attributes;
1228     }
1229   else if (((PangoAttribute *)list->attributes_tail->data)->start_index < start_index ||
1230            (!before && ((PangoAttribute *)list->attributes_tail->data)->start_index == start_index))
1231     {
1232       list->attributes_tail = g_slist_append (list->attributes_tail, attr);
1233       list->attributes_tail = list->attributes_tail->next;
1234       g_assert (list->attributes_tail);
1235     }
1236   else
1237     {
1238       prev = NULL;
1239       tmp_list = list->attributes;
1240       while (1)
1241         {
1242           PangoAttribute *tmp_attr = tmp_list->data;
1243
1244           if (tmp_attr->start_index > start_index ||
1245               (before && tmp_attr->start_index == start_index))
1246             {
1247               link = g_slist_alloc ();
1248               link->next = tmp_list;
1249               link->data = attr;
1250
1251               if (prev)
1252                 prev->next = link;
1253               else
1254                 list->attributes = link;
1255
1256               if (!tmp_list)
1257                 list->attributes_tail = link;
1258
1259               break;
1260             }
1261
1262           prev = tmp_list;
1263           tmp_list = tmp_list->next;
1264         }
1265     }
1266 }
1267
1268 /**
1269  * pango_attr_list_insert:
1270  * @list: a #PangoAttrList
1271  * @attr: the attribute to insert. Ownership of this value is
1272  *        assumed by the list.
1273  *
1274  * Insert the given attribute into the #PangoAttrList. It will
1275  * be inserted after all other attributes with a matching
1276  * @start_index.
1277  **/
1278 void
1279 pango_attr_list_insert (PangoAttrList  *list,
1280                         PangoAttribute *attr)
1281 {
1282   g_return_if_fail (list != NULL);
1283   g_return_if_fail (attr != NULL);
1284
1285   pango_attr_list_insert_internal (list, attr, FALSE);
1286 }
1287
1288 /**
1289  * pango_attr_list_insert_before:
1290  * @list: a #PangoAttrList
1291  * @attr: the attribute to insert. Ownership of this value is
1292  *        assumed by the list.
1293  *
1294  * Insert the given attribute into the #PangoAttrList. It will
1295  * be inserted before all other attributes with a matching
1296  * @start_index.
1297  **/
1298 void
1299 pango_attr_list_insert_before (PangoAttrList  *list,
1300                                PangoAttribute *attr)
1301 {
1302   g_return_if_fail (list != NULL);
1303   g_return_if_fail (attr != NULL);
1304
1305   pango_attr_list_insert_internal (list, attr, TRUE);
1306 }
1307
1308 /**
1309  * pango_attr_list_change:
1310  * @list: a #PangoAttrList
1311  * @attr: the attribute to insert. Ownership of this value is
1312  *        assumed by the list.
1313  *
1314  * Insert the given attribute into the #PangoAttrList. It will
1315  * replace any attributes of the same type on that segment
1316  * and be merged with any adjoining attributes that are identical.
1317  *
1318  * This function is slower than pango_attr_list_insert() for
1319  * creating a attribute list in order (potentially much slower
1320  * for large lists). However, pango_attr_list_insert() is not
1321  * suitable for continually changing a set of attributes
1322  * since it never removes or combines existing attributes.
1323  **/
1324 void
1325 pango_attr_list_change (PangoAttrList  *list,
1326                         PangoAttribute *attr)
1327 {
1328   GSList *tmp_list, *prev, *link;
1329   guint start_index = attr->start_index;
1330   guint end_index = attr->end_index;
1331
1332   g_return_if_fail (list != NULL);
1333
1334   if (start_index == end_index) /* empty, nothing to do */
1335     {
1336       pango_attribute_destroy (attr);
1337       return;
1338     }
1339
1340   tmp_list = list->attributes;
1341   prev = NULL;
1342   while (1)
1343     {
1344       PangoAttribute *tmp_attr;
1345
1346       if (!tmp_list ||
1347           ((PangoAttribute *)tmp_list->data)->start_index > start_index)
1348         {
1349           /* We need to insert a new attribute
1350            */
1351           link = g_slist_alloc ();
1352           link->next = tmp_list;
1353           link->data = attr;
1354
1355           if (prev)
1356             prev->next = link;
1357           else
1358             list->attributes = link;
1359
1360           if (!tmp_list)
1361             list->attributes_tail = link;
1362
1363           prev = link;
1364           tmp_list = prev->next;
1365           break;
1366         }
1367
1368       tmp_attr = tmp_list->data;
1369
1370       if (tmp_attr->klass->type == attr->klass->type &&
1371           tmp_attr->end_index >= start_index)
1372         {
1373           /* We overlap with an existing attribute */
1374           if (pango_attribute_equal (tmp_attr, attr))
1375             {
1376               /* We can merge the new attribute with this attribute
1377                */
1378               if (tmp_attr->end_index >= end_index)
1379                 {
1380                   /* We are totally overlapping the previous attribute.
1381                    * No action is needed.
1382                    */
1383                   pango_attribute_destroy (attr);
1384                   return;
1385                 }
1386               tmp_attr->end_index = end_index;
1387               pango_attribute_destroy (attr);
1388
1389               attr = tmp_attr;
1390
1391               prev = tmp_list;
1392               tmp_list = tmp_list->next;
1393
1394               break;
1395             }
1396           else
1397             {
1398               /* Split, truncate, or remove the old attribute
1399                */
1400               if (tmp_attr->end_index > attr->end_index)
1401                 {
1402                   PangoAttribute *end_attr = pango_attribute_copy (tmp_attr);
1403
1404                   end_attr->start_index = attr->end_index;
1405                   pango_attr_list_insert (list, end_attr);
1406                 }
1407
1408               if (tmp_attr->start_index == attr->start_index)
1409                 {
1410                   pango_attribute_destroy (tmp_attr);
1411                   tmp_list->data = attr;
1412
1413                   prev = tmp_list;
1414                   tmp_list = tmp_list->next;
1415                   break;
1416                 }
1417               else
1418                 {
1419                   tmp_attr->end_index = attr->start_index;
1420                 }
1421             }
1422         }
1423       prev = tmp_list;
1424       tmp_list = tmp_list->next;
1425     }
1426   /* At this point, prev points to the list node with attr in it,
1427    * tmp_list points to prev->next.
1428    */
1429
1430   g_assert (prev->data == attr);
1431   g_assert (prev->next == tmp_list);
1432
1433   /* We now have the range inserted into the list one way or the
1434    * other. Fix up the remainder
1435    */
1436   while (tmp_list)
1437     {
1438       PangoAttribute *tmp_attr = tmp_list->data;
1439
1440       if (tmp_attr->start_index > end_index)
1441         break;
1442       else if (tmp_attr->klass->type == attr->klass->type)
1443         {
1444           if (tmp_attr->end_index <= attr->end_index ||
1445               pango_attribute_equal (tmp_attr, attr))
1446             {
1447               /* We can merge the new attribute with this attribute.
1448                */
1449               attr->end_index = MAX (end_index, tmp_attr->end_index);
1450
1451               pango_attribute_destroy (tmp_attr);
1452               prev->next = tmp_list->next;
1453
1454               if (!prev->next)
1455                 list->attributes_tail = prev;
1456
1457               g_slist_free_1 (tmp_list);
1458               tmp_list = prev->next;
1459
1460               continue;
1461             }
1462           else
1463             {
1464               /* Trim the start of this attribute that it begins at the end
1465                * of the new attribute. This may involve moving
1466                * it in the list to maintain the required non-decreasing
1467                * order of start indices
1468                */
1469               GSList *tmp_list2;
1470               GSList *prev2;
1471
1472               tmp_attr->start_index = attr->end_index;
1473
1474               tmp_list2 = tmp_list->next;
1475               prev2 = tmp_list;
1476
1477               while (tmp_list2)
1478                 {
1479                   PangoAttribute *tmp_attr2 = tmp_list2->data;
1480
1481                   if (tmp_attr2->start_index >= tmp_attr->start_index)
1482                     break;
1483
1484                   prev2 = tmp_list2;
1485                   tmp_list2 = tmp_list2->next;
1486                 }
1487
1488               /* Now remove and insert before tmp_list2. We'll
1489                * hit this attribute again later, but that's harmless.
1490                */
1491               if (prev2 != tmp_list)
1492                 {
1493                   GSList *old_next = tmp_list->next;
1494
1495                   prev->next = old_next;
1496                   prev2->next = tmp_list;
1497                   tmp_list->next = tmp_list2;
1498
1499                   if (!tmp_list->next)
1500                     list->attributes_tail = tmp_list;
1501
1502                   tmp_list = old_next;
1503
1504                   continue;
1505                 }
1506             }
1507         }
1508
1509       prev = tmp_list;
1510       tmp_list = tmp_list->next;
1511     }
1512 }
1513
1514 /**
1515  * pango_attr_list_splice:
1516  * @list: a #PangoAttrList
1517  * @other: another #PangoAttrList
1518  * @pos: the position in @list at which to insert @other
1519  * @len: the length of the spliced segment. (Note that this
1520  *       must be specified since the attributes in @other
1521  *       may only be present at some subsection of this range)
1522  *
1523  * This function opens up a hole in @list, fills it in with attributes from
1524  * the left, and then merges @other on top of the hole.
1525  *
1526  * This operation is equivalent to stretching every attribute
1527  * that applies at position @pos in @list by an amount @len,
1528  * and then calling pango_attr_list_change() with a copy
1529  * of each attribute in @other in sequence (offset in position by @pos).
1530  *
1531  * This operation proves useful for, for instance, inserting
1532  * a pre-edit string in the middle of an edit buffer.
1533  **/
1534 void
1535 pango_attr_list_splice (PangoAttrList *list,
1536                         PangoAttrList *other,
1537                         gint           pos,
1538                         gint           len)
1539 {
1540   GSList *tmp_list;
1541   guint upos, ulen;
1542
1543   g_return_if_fail (list != NULL);
1544   g_return_if_fail (other != NULL);
1545   g_return_if_fail (pos >= 0);
1546   g_return_if_fail (len >= 0);
1547
1548   upos = (guint)pos;
1549   ulen = (guint)len;
1550
1551 /* This definition only works when a and b are unsigned; overflow
1552  * isn't defined in the C standard for signed integers
1553  */
1554 #define CLAMP_ADD(a,b) (((a) + (b) < (a)) ? G_MAXUINT : (a) + (b))
1555
1556   tmp_list = list->attributes;
1557   while (tmp_list)
1558     {
1559       PangoAttribute *attr = tmp_list->data;
1560
1561       if (attr->start_index <= upos)
1562         {
1563           if (attr->end_index > upos)
1564             attr->end_index = CLAMP_ADD (attr->end_index, ulen);
1565         }
1566       else
1567         {
1568           /* This could result in a zero length attribute if it
1569            * gets squashed up against G_MAXUINT, but deleting such
1570            * an element could (in theory) suprise the caller, so
1571            * we don't delete it.
1572            */
1573           attr->start_index = CLAMP_ADD (attr->start_index, ulen);
1574           attr->end_index = CLAMP_ADD (attr->end_index, ulen);
1575         }
1576
1577       tmp_list = tmp_list->next;
1578     }
1579
1580   tmp_list = other->attributes;
1581   while (tmp_list)
1582     {
1583       PangoAttribute *attr = pango_attribute_copy (tmp_list->data);
1584       attr->start_index = CLAMP_ADD (attr->start_index, upos);
1585       attr->end_index = CLAMP_ADD (attr->end_index, upos);
1586
1587       /* Same as above, the attribute could be squashed to zero-length; here
1588        * pango_attr_list_change() will take care of deleting it.
1589        */
1590       pango_attr_list_change (list, attr);
1591
1592       tmp_list = tmp_list->next;
1593     }
1594 #undef CLAMP_ADD
1595 }
1596
1597 /**
1598  * pango_attr_list_get_iterator:
1599  * @list: a #PangoAttrList
1600  *
1601  * Create a iterator initialized to the beginning of the list.
1602  * @list must not be modified until this iterator is freed.
1603  *
1604  * Return value: the newly allocated #PangoAttrIterator, which should
1605  *               be freed with pango_attr_iterator_destroy().
1606  **/
1607 PangoAttrIterator *
1608 pango_attr_list_get_iterator (PangoAttrList  *list)
1609 {
1610   PangoAttrIterator *iterator;
1611
1612   g_return_val_if_fail (list != NULL, NULL);
1613
1614   iterator = g_slice_new (PangoAttrIterator);
1615   iterator->next_attribute = list->attributes;
1616   iterator->attribute_stack = NULL;
1617
1618   iterator->start_index = 0;
1619   iterator->end_index = 0;
1620
1621   if (!pango_attr_iterator_next (iterator))
1622     iterator->end_index = G_MAXUINT;
1623
1624   return iterator;
1625 }
1626
1627 /**
1628  * pango_attr_iterator_range:
1629  * @iterator: a #PangoAttrIterator
1630  * @start: location to store the start of the range
1631  * @end: location to store the end of the range
1632  *
1633  * Get the range of the current segment. Note that the
1634  * stored return values are signed, not unsigned like
1635  * the values in #PangoAttribute. To deal with this API
1636  * oversight, stored return values that wouldn't fit into
1637  * a signed integer are clamped to %G_MAXINT.
1638  **/
1639 void
1640 pango_attr_iterator_range (PangoAttrIterator *iterator,
1641                            gint              *start,
1642                            gint              *end)
1643 {
1644   g_return_if_fail (iterator != NULL);
1645
1646   if (start)
1647     *start = MIN (iterator->start_index, G_MAXINT);
1648   if (end)
1649     *end = MIN (iterator->end_index, G_MAXINT);
1650 }
1651
1652 /**
1653  * pango_attr_iterator_next:
1654  * @iterator: a #PangoAttrIterator
1655  *
1656  * Advance the iterator until the next change of style.
1657  *
1658  * Return value: %FALSE if the iterator is at the end of the list, otherwise %TRUE
1659  **/
1660 gboolean
1661 pango_attr_iterator_next (PangoAttrIterator *iterator)
1662 {
1663   GList *tmp_list;
1664
1665   g_return_val_if_fail (iterator != NULL, FALSE);
1666
1667   if (!iterator->next_attribute && !iterator->attribute_stack)
1668     return FALSE;
1669
1670   iterator->start_index = iterator->end_index;
1671   iterator->end_index = G_MAXUINT;
1672
1673   tmp_list = iterator->attribute_stack;
1674   while (tmp_list)
1675     {
1676       GList *next = tmp_list->next;
1677       PangoAttribute *attr = tmp_list->data;
1678
1679       if (attr->end_index == iterator->start_index)
1680         {
1681           iterator->attribute_stack = g_list_remove_link (iterator->attribute_stack, tmp_list);
1682           g_list_free_1 (tmp_list);
1683         }
1684       else
1685         {
1686           iterator->end_index = MIN (iterator->end_index, attr->end_index);
1687         }
1688
1689       tmp_list = next;
1690     }
1691
1692   while (iterator->next_attribute &&
1693          ((PangoAttribute *)iterator->next_attribute->data)->start_index == iterator->start_index)
1694     {
1695       if (((PangoAttribute *)iterator->next_attribute->data)->end_index > iterator->start_index)
1696         {
1697           iterator->attribute_stack = g_list_prepend (iterator->attribute_stack, iterator->next_attribute->data);
1698           iterator->end_index = MIN (iterator->end_index, ((PangoAttribute *)iterator->next_attribute->data)->end_index);
1699         }
1700       iterator->next_attribute = iterator->next_attribute->next;
1701     }
1702
1703   if (iterator->next_attribute)
1704     iterator->end_index = MIN (iterator->end_index, ((PangoAttribute *)iterator->next_attribute->data)->start_index);
1705
1706   return TRUE;
1707 }
1708
1709 /**
1710  * pango_attr_iterator_copy:
1711  * @iterator: a #PangoAttrIterator.
1712  *
1713  * Copy a #PangoAttrIterator
1714  *
1715  * Return value: the newly allocated #PangoAttrIterator, which should
1716  *               be freed with pango_attr_iterator_destroy().
1717  **/
1718 PangoAttrIterator *
1719 pango_attr_iterator_copy (PangoAttrIterator *iterator)
1720 {
1721   PangoAttrIterator *copy;
1722
1723   g_return_val_if_fail (iterator != NULL, NULL);
1724
1725   copy = g_slice_new (PangoAttrIterator);
1726
1727   *copy = *iterator;
1728
1729   copy->attribute_stack = g_list_copy (iterator->attribute_stack);
1730
1731   return copy;
1732 }
1733
1734 /**
1735  * pango_attr_iterator_destroy:
1736  * @iterator: a #PangoAttrIterator.
1737  *
1738  * Destroy a #PangoAttrIterator and free all associated memory.
1739  **/
1740 void
1741 pango_attr_iterator_destroy (PangoAttrIterator *iterator)
1742 {
1743   g_return_if_fail (iterator != NULL);
1744
1745   g_list_free (iterator->attribute_stack);
1746   g_slice_free (PangoAttrIterator, iterator);
1747 }
1748
1749 /**
1750  * pango_attr_iterator_get:
1751  * @iterator: a #PangoAttrIterator
1752  * @type: the type of attribute to find.
1753  *
1754  * Find the current attribute of a particular type at the iterator
1755  * location. When multiple attributes of the same type overlap,
1756  * the attribute whose range starts closest to the current location
1757  * is used.
1758  *
1759  * Return value: the current attribute of the given type, or %NULL
1760  *               if no attribute of that type applies to the current
1761  *               location.
1762  **/
1763 PangoAttribute *
1764 pango_attr_iterator_get (PangoAttrIterator *iterator,
1765                          PangoAttrType      type)
1766 {
1767   GList *tmp_list;
1768
1769   g_return_val_if_fail (iterator != NULL, NULL);
1770
1771   tmp_list = iterator->attribute_stack;
1772   while (tmp_list)
1773     {
1774       PangoAttribute *attr = tmp_list->data;
1775
1776       if (attr->klass->type == type)
1777         return attr;
1778
1779       tmp_list = tmp_list->next;
1780     }
1781
1782   return NULL;
1783 }
1784
1785 /**
1786  * pango_attr_iterator_get_font:
1787  * @iterator: a #PangoAttrIterator
1788  * @desc: a #PangoFontDescription to fill in with the current values.
1789  *        The family name in this structure will be set using
1790  *        pango_font_description_set_family_static() using values from
1791  *        an attribute in the #PangoAttrList associated with the iterator,
1792  *        so if you plan to keep it around, you must call:
1793  *        <literal>pango_font_description_set_family (desc, pango_font_description_get_family (desc))</literal>.
1794  * @language: if non-%NULL, location to store language tag for item, or %NULL
1795  *            if none is found.
1796  * @extra_attrs: (element type Pango.Attribute): (transfer full): if non-%NULL,
1797  *           location in which to store a list of non-font
1798  *           attributes at the the current position; only the highest priority
1799  *           value of each attribute will be added to this list. In order
1800  *           to free this value, you must call pango_attribute_destroy() on
1801  *           each member.
1802  *
1803  * Get the font and other attributes at the current iterator position.
1804  **/
1805 void
1806 pango_attr_iterator_get_font (PangoAttrIterator     *iterator,
1807                               PangoFontDescription  *desc,
1808                               PangoLanguage        **language,
1809                               GSList               **extra_attrs)
1810 {
1811   GList *tmp_list1;
1812   GSList *tmp_list2;
1813
1814   PangoFontMask mask = 0;
1815   gboolean have_language = FALSE;
1816   gdouble scale = 0;
1817   gboolean have_scale = FALSE;
1818
1819   g_return_if_fail (iterator != NULL);
1820   g_return_if_fail (desc != NULL);
1821
1822   if (language)
1823     *language = NULL;
1824
1825   if (extra_attrs)
1826     *extra_attrs = NULL;
1827
1828   tmp_list1 = iterator->attribute_stack;
1829   while (tmp_list1)
1830     {
1831       PangoAttribute *attr = tmp_list1->data;
1832       tmp_list1 = tmp_list1->next;
1833
1834       switch ((int) attr->klass->type)
1835         {
1836         case PANGO_ATTR_FONT_DESC:
1837           {
1838             PangoFontMask new_mask = pango_font_description_get_set_fields (((PangoAttrFontDesc *)attr)->desc) & ~mask;
1839             mask |= new_mask;
1840             pango_font_description_unset_fields (desc, new_mask);
1841             pango_font_description_merge_static (desc, ((PangoAttrFontDesc *)attr)->desc, FALSE);
1842
1843             break;
1844           }
1845         case PANGO_ATTR_FAMILY:
1846           if (!(mask & PANGO_FONT_MASK_FAMILY))
1847             {
1848               mask |= PANGO_FONT_MASK_FAMILY;
1849               pango_font_description_set_family (desc, ((PangoAttrString *)attr)->value);
1850             }
1851           break;
1852         case PANGO_ATTR_STYLE:
1853           if (!(mask & PANGO_FONT_MASK_STYLE))
1854             {
1855               mask |= PANGO_FONT_MASK_STYLE;
1856               pango_font_description_set_style (desc, ((PangoAttrInt *)attr)->value);
1857             }
1858           break;
1859         case PANGO_ATTR_VARIANT:
1860           if (!(mask & PANGO_FONT_MASK_VARIANT))
1861             {
1862               mask |= PANGO_FONT_MASK_VARIANT;
1863               pango_font_description_set_variant (desc, ((PangoAttrInt *)attr)->value);
1864             }
1865           break;
1866         case PANGO_ATTR_WEIGHT:
1867           if (!(mask & PANGO_FONT_MASK_WEIGHT))
1868             {
1869               mask |= PANGO_FONT_MASK_WEIGHT;
1870               pango_font_description_set_weight (desc, ((PangoAttrInt *)attr)->value);
1871             }
1872           break;
1873         case PANGO_ATTR_STRETCH:
1874           if (!(mask & PANGO_FONT_MASK_STRETCH))
1875             {
1876               mask |= PANGO_FONT_MASK_STRETCH;
1877               pango_font_description_set_stretch (desc, ((PangoAttrInt *)attr)->value);
1878             }
1879           break;
1880         case PANGO_ATTR_SIZE:
1881           if (!(mask & PANGO_FONT_MASK_SIZE))
1882             {
1883               mask |= PANGO_FONT_MASK_SIZE;
1884               pango_font_description_set_size (desc, ((PangoAttrSize *)attr)->size);
1885             }
1886           break;
1887         case PANGO_ATTR_ABSOLUTE_SIZE:
1888           if (!(mask & PANGO_FONT_MASK_SIZE))
1889             {
1890               mask |= PANGO_FONT_MASK_SIZE;
1891               pango_font_description_set_absolute_size (desc, ((PangoAttrSize *)attr)->size);
1892             }
1893           break;
1894         case PANGO_ATTR_SCALE:
1895           if (!have_scale)
1896             {
1897               have_scale = TRUE;
1898               scale = ((PangoAttrFloat *)attr)->value;
1899             }
1900           break;
1901         case PANGO_ATTR_LANGUAGE:
1902           if (language)
1903             {
1904               if (!have_language)
1905                 {
1906                   have_language = TRUE;
1907                   *language = ((PangoAttrLanguage *)attr)->value;
1908                 }
1909             }
1910           break;
1911         default:
1912           if (extra_attrs)
1913             {
1914               gboolean found = FALSE;
1915
1916               tmp_list2 = *extra_attrs;
1917               while (tmp_list2)
1918                 {
1919                   PangoAttribute *old_attr = tmp_list2->data;
1920                   if (attr->klass->type == old_attr->klass->type)
1921                     {
1922                       found = TRUE;
1923                       break;
1924                     }
1925
1926                   tmp_list2 = tmp_list2->next;
1927                 }
1928
1929               if (!found)
1930                 *extra_attrs = g_slist_prepend (*extra_attrs, pango_attribute_copy (attr));
1931             }
1932         }
1933     }
1934
1935   if (have_scale)
1936     pango_font_description_set_size (desc, scale * pango_font_description_get_size (desc));
1937
1938 }
1939
1940 /**
1941  * pango_attr_list_filter:
1942  * @list: a #PangoAttrList
1943  * @func: callback function; returns %TRUE if an attribute
1944  *        should be filtered out.
1945  * @data: Data to be passed to @func
1946  *
1947  * Given a #PangoAttrList and callback function, removes any elements
1948  * of @list for which @func returns %TRUE and inserts them into
1949  * a new list.
1950  *
1951  * Return value: the new #PangoAttrList or %NULL if
1952  *  no attributes of the given types were found.
1953  *
1954  * Since: 1.2
1955  **/
1956 PangoAttrList *
1957 pango_attr_list_filter (PangoAttrList       *list,
1958                         PangoAttrFilterFunc  func,
1959                         gpointer             data)
1960
1961 {
1962   PangoAttrList *new = NULL;
1963   GSList *tmp_list;
1964   GSList *prev;
1965
1966   g_return_val_if_fail (list != NULL, NULL);
1967
1968   tmp_list = list->attributes;
1969   prev = NULL;
1970   while (tmp_list)
1971     {
1972       GSList *next = tmp_list->next;
1973       PangoAttribute *tmp_attr = tmp_list->data;
1974
1975       if ((*func) (tmp_attr, data))
1976         {
1977           if (!tmp_list->next)
1978             list->attributes_tail = prev;
1979
1980           if (prev)
1981             prev->next = tmp_list->next;
1982           else
1983             list->attributes = tmp_list->next;
1984
1985           tmp_list->next = NULL;
1986
1987           if (!new)
1988             {
1989               new = pango_attr_list_new ();
1990               new->attributes = new->attributes_tail = tmp_list;
1991             }
1992           else
1993             {
1994               new->attributes_tail->next = tmp_list;
1995               new->attributes_tail = tmp_list;
1996             }
1997
1998           goto next_attr;
1999         }
2000
2001       prev = tmp_list;
2002
2003     next_attr:
2004       tmp_list = next;
2005     }
2006
2007   return new;
2008 }
2009
2010 /**
2011  * pango_attr_iterator_get_attrs:
2012  * @iterator: a #PangoAttrIterator
2013  *
2014  * Gets a list of all attributes at the current position of the
2015  * iterator.
2016  *
2017  * Return value: (element-type Pango.Attribute): (transfer full): a list of
2018  *   all attributes for the current range.
2019  *   To free this value, call pango_attribute_destroy() on
2020  *   each value and g_slist_free() on the list.
2021  *
2022  * Since: 1.2
2023  **/
2024 GSList *
2025 pango_attr_iterator_get_attrs (PangoAttrIterator *iterator)
2026 {
2027   GSList *attrs = NULL;
2028   GList *tmp_list;
2029
2030   for (tmp_list = iterator->attribute_stack; tmp_list; tmp_list = tmp_list->next)
2031     {
2032       PangoAttribute *attr = tmp_list->data;
2033       GSList *tmp_list2;
2034       gboolean found = FALSE;
2035
2036       for (tmp_list2 = attrs; tmp_list2; tmp_list2 = tmp_list2->next)
2037         {
2038           PangoAttribute *old_attr = tmp_list2->data;
2039           if (attr->klass->type == old_attr->klass->type)
2040             {
2041               found = TRUE;
2042               break;
2043             }
2044         }
2045
2046       if (!found)
2047         attrs = g_slist_prepend (attrs, pango_attribute_copy (attr));
2048     }
2049
2050   return attrs;
2051 }