]> git.sur5r.net Git - glabels/blob - glabels2/src/label-text.c
2009-08-12 Jim Evins <evins@snaught.com>
[glabels] / glabels2 / src / label-text.c
1 /*
2  *  label-text.c
3  *  Copyright (C) 2001-2009  Jim Evins <evins@snaught.com>.
4  *
5  *  This file is part of gLabels.
6  *
7  *  gLabels 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  *  gLabels 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 gLabels.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include "label-text.h"
24
25 #include <glib/gmem.h>
26 #include <glib/gstrfuncs.h>
27 #include <glib/gmessages.h>
28 #include <pango/pango.h>
29 #include <math.h>
30
31 #include "util.h"
32
33 #include "debug.h"
34
35
36 /*========================================================*/
37 /* Private macros and constants.                          */
38 /*========================================================*/
39
40 #define DEFAULT_FONT_FAMILY       "Sans"
41 #define DEFAULT_FONT_SIZE         14.0
42 #define DEFAULT_FONT_WEIGHT       PANGO_WEIGHT_NORMAL
43 #define DEFAULT_FONT_ITALIC_FLAG  FALSE
44 #define DEFAULT_ALIGN             PANGO_ALIGN_LEFT
45 #define DEFAULT_COLOR             GL_COLOR (0,0,0)
46 #define DEFAULT_TEXT_LINE_SPACING 1.0
47 #define DEFAULT_AUTO_SHRINK       FALSE
48
49 #define FONT_SCALE (72.0/96.0)
50
51
52 /*========================================================*/
53 /* Private types.                                         */
54 /*========================================================*/
55
56 struct _glLabelTextPrivate {
57         GtkTextTagTable *tag_table;
58         GtkTextBuffer   *buffer;
59
60         gchar           *font_family;
61         gdouble          font_size;
62         PangoWeight      font_weight;
63         gboolean         font_italic_flag;
64         PangoAlignment   align;
65         glColorNode     *color_node;
66         gdouble          line_spacing;
67         gboolean         auto_shrink;
68
69         gboolean         size_changed;
70         gdouble          w;
71         gdouble          h;
72 };
73
74
75 /*========================================================*/
76 /* Private globals.                                       */
77 /*========================================================*/
78
79
80 /*========================================================*/
81 /* Private function prototypes.                           */
82 /*========================================================*/
83
84 static void gl_label_text_finalize      (GObject          *object);
85
86 static void copy                        (glLabelObject    *dst_object,
87                                          glLabelObject    *src_object);
88
89 static void buffer_changed_cb           (GtkTextBuffer    *textbuffer,
90                                          glLabelText      *ltext);
91
92 static void get_size                    (glLabelObject    *object,
93                                          gdouble          *w,
94                                          gdouble          *h);
95
96 static void set_font_family             (glLabelObject    *object,
97                                          const gchar      *font_family);
98
99 static void set_font_size               (glLabelObject    *object,
100                                          gdouble           font_size);
101
102 static void set_font_weight             (glLabelObject    *object,
103                                          PangoWeight       font_weight);
104
105 static void set_font_italic_flag        (glLabelObject    *object,
106                                          gboolean          font_italic_flag);
107
108 static void set_text_alignment          (glLabelObject    *object,
109                                          PangoAlignment    text_alignment);
110
111 static void set_text_line_spacing       (glLabelObject    *object,
112                                          gdouble           text_line_spacing);
113
114 static void set_text_color              (glLabelObject    *object,
115                                          glColorNode      *text_color_node);
116
117 static gchar          *get_font_family             (glLabelObject    *object);
118
119 static gdouble         get_font_size               (glLabelObject    *object);
120
121 static PangoWeight     get_font_weight             (glLabelObject    *object);
122
123 static gboolean        get_font_italic_flag        (glLabelObject    *object);
124
125 static PangoAlignment  get_text_alignment          (glLabelObject    *object);
126
127 static gdouble         get_text_line_spacing       (glLabelObject    *object);
128
129 static glColorNode*    get_text_color              (glLabelObject    *object);
130
131 static void            draw_object                 (glLabelObject    *object,
132                                                     cairo_t          *cr,
133                                                     gboolean          screen_flag,
134                                                     glMergeRecord    *record);
135
136 static void            draw_shadow                 (glLabelObject    *object,
137                                                     cairo_t          *cr,
138                                                     gboolean          screen_flag,
139                                                     glMergeRecord    *record);
140
141 static void            draw_text_real              (glLabelObject    *object,
142                                                     cairo_t          *cr,
143                                                     gboolean          screen_flag,
144                                                     glMergeRecord    *record,
145                                                     guint             color);
146
147 static gdouble         auto_shrink_font_size       (cairo_t          *cr,
148                                                     gchar            *family,
149                                                     gdouble           size,
150                                                     PangoWeight       weight,
151                                                     PangoStyle        style,
152                                                     gchar            *text,
153                                                     gdouble           width);
154
155
156 /*****************************************************************************/
157 /* Object infrastructure.                                                    */
158 /*****************************************************************************/
159 G_DEFINE_TYPE (glLabelText, gl_label_text, GL_TYPE_LABEL_OBJECT);
160
161
162 /*****************************************************************************/
163 /* Class Init Function.                                                      */
164 /*****************************************************************************/
165 static void
166 gl_label_text_class_init (glLabelTextClass *class)
167 {
168         GObjectClass       *object_class       = G_OBJECT_CLASS (class);
169         glLabelObjectClass *label_object_class = GL_LABEL_OBJECT_CLASS (class);
170
171         gl_label_text_parent_class = g_type_class_peek_parent (class);
172
173         label_object_class->copy                  = copy;
174
175         label_object_class->get_size              = get_size;
176
177         label_object_class->set_font_family       = set_font_family;
178         label_object_class->set_font_size         = set_font_size;
179         label_object_class->set_font_weight       = set_font_weight;
180         label_object_class->set_font_italic_flag  = set_font_italic_flag;
181         label_object_class->set_text_alignment    = set_text_alignment;
182         label_object_class->set_text_line_spacing = set_text_line_spacing;
183         label_object_class->set_text_color        = set_text_color;
184         label_object_class->get_font_family       = get_font_family;
185         label_object_class->get_font_size         = get_font_size;
186         label_object_class->get_font_weight       = get_font_weight;
187         label_object_class->get_font_italic_flag  = get_font_italic_flag;
188         label_object_class->get_text_alignment    = get_text_alignment;
189         label_object_class->get_text_line_spacing = get_text_line_spacing;
190         label_object_class->get_text_color        = get_text_color;
191         label_object_class->draw_object           = draw_object;
192         label_object_class->draw_shadow           = draw_shadow;
193
194         object_class->finalize = gl_label_text_finalize;
195 }
196
197
198 /*****************************************************************************/
199 /* Object Instance Init Function.                                            */
200 /*****************************************************************************/
201 static void
202 gl_label_text_init (glLabelText *ltext)
203 {
204         ltext->priv = g_new0 (glLabelTextPrivate, 1);
205
206         ltext->priv->tag_table         = gtk_text_tag_table_new ();
207         ltext->priv->buffer            = gtk_text_buffer_new (ltext->priv->tag_table);
208
209         ltext->priv->font_family       = g_strdup(DEFAULT_FONT_FAMILY);
210         ltext->priv->font_size         = DEFAULT_FONT_SIZE;
211         ltext->priv->font_weight       = DEFAULT_FONT_WEIGHT;
212         ltext->priv->font_italic_flag  = DEFAULT_FONT_ITALIC_FLAG;
213         ltext->priv->align             = DEFAULT_ALIGN;
214         ltext->priv->color_node        = gl_color_node_new_default ();
215         ltext->priv->color_node->color = DEFAULT_COLOR;
216         ltext->priv->line_spacing      = DEFAULT_TEXT_LINE_SPACING;
217         ltext->priv->auto_shrink       = DEFAULT_AUTO_SHRINK;
218
219         ltext->priv->size_changed      = TRUE;
220
221         g_signal_connect (G_OBJECT(ltext->priv->buffer), "changed",
222                           G_CALLBACK(buffer_changed_cb), ltext);
223 }
224
225
226 /*****************************************************************************/
227 /* Finalize Method.                                                          */
228 /*****************************************************************************/
229 static void
230 gl_label_text_finalize (GObject *object)
231 {
232         glLabelText *ltext = GL_LABEL_TEXT (object);
233
234         g_return_if_fail (object && GL_IS_LABEL_TEXT (object));
235
236         g_object_unref (ltext->priv->tag_table);
237         g_object_unref (ltext->priv->buffer);
238         g_free (ltext->priv->font_family);
239         gl_color_node_free (&(ltext->priv->color_node));
240         g_free (ltext->priv);
241
242         G_OBJECT_CLASS (gl_label_text_parent_class)->finalize (object);
243 }
244
245
246 /*****************************************************************************/
247 /** New Object Generator.                                                    */
248 /*****************************************************************************/
249 GObject *
250 gl_label_text_new (glLabel *label)
251 {
252         glLabelText *ltext;
253
254         ltext = g_object_new (gl_label_text_get_type(), NULL);
255
256         gl_label_object_set_parent (GL_LABEL_OBJECT(ltext), label);
257
258         return G_OBJECT (ltext);
259 }
260
261
262 /*****************************************************************************/
263 /* Copy object contents.                                                     */
264 /*****************************************************************************/
265 static void
266 copy (glLabelObject *dst_object,
267       glLabelObject *src_object)
268 {
269         glLabelText      *ltext     = (glLabelText *)src_object;
270         glLabelText      *new_ltext = (glLabelText *)dst_object;
271         GList            *lines;
272         glColorNode      *text_color_node;
273
274         gl_debug (DEBUG_LABEL, "START");
275
276         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
277         g_return_if_fail (new_ltext && GL_IS_LABEL_TEXT (new_ltext));
278
279         lines = gl_label_text_get_lines (ltext);
280         text_color_node = get_text_color (src_object);
281         gl_label_text_set_lines (new_ltext, lines);
282
283         new_ltext->priv->font_family      = g_strdup (ltext->priv->font_family);
284         new_ltext->priv->font_size        = ltext->priv->font_size;
285         new_ltext->priv->font_weight      = ltext->priv->font_weight;
286         new_ltext->priv->font_italic_flag = ltext->priv->font_italic_flag;
287         set_text_color (dst_object, text_color_node);
288         new_ltext->priv->align            = ltext->priv->align;
289         new_ltext->priv->line_spacing     = ltext->priv->line_spacing;
290         new_ltext->priv->auto_shrink      = ltext->priv->auto_shrink;
291
292         new_ltext->priv->size_changed     = ltext->priv->size_changed;
293         new_ltext->priv->w                = ltext->priv->w;
294         new_ltext->priv->h                = ltext->priv->h;
295
296         gl_color_node_free (&text_color_node);
297         gl_text_node_lines_free (&lines);
298
299         gl_debug (DEBUG_LABEL, "END");
300 }
301
302
303 /*****************************************************************************/
304 /* Set object params.                                                        */
305 /*****************************************************************************/
306 void
307 gl_label_text_set_lines (glLabelText *ltext,
308                          GList       *lines)
309 {
310         gchar *text;
311
312         gl_debug (DEBUG_LABEL, "START");
313
314         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
315
316         text = gl_text_node_lines_expand (lines, NULL);
317         gtk_text_buffer_set_text (ltext->priv->buffer, text, -1);
318         g_free (text);
319
320         ltext->priv->size_changed = TRUE;
321
322         gl_debug (DEBUG_LABEL, "END");
323 }
324
325 /*****************************************************************************/
326 /* Get object params.                                                        */
327 /*****************************************************************************/
328 GtkTextBuffer *
329 gl_label_text_get_buffer (glLabelText *ltext)
330 {
331         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), NULL);
332
333         return ltext->priv->buffer;
334 }
335
336
337 GList *
338 gl_label_text_get_lines (glLabelText *ltext)
339 {
340         GtkTextIter  start, end;
341         gchar       *text;
342         GList       *lines;
343
344         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), NULL);
345
346         gtk_text_buffer_get_bounds (ltext->priv->buffer, &start, &end);
347         text = gtk_text_buffer_get_text (ltext->priv->buffer,
348                                          &start, &end, FALSE);
349         lines = gl_text_node_lines_new_from_text (text);
350         g_free (text);
351
352         return lines;
353 }
354
355
356 /*****************************************************************************/
357 /* Text buffer "changed" callback.                                           */
358 /*****************************************************************************/
359 static void
360 buffer_changed_cb (GtkTextBuffer *textbuffer,
361                    glLabelText   *ltext)
362 {
363         ltext->priv->size_changed = TRUE;
364
365         gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
366 }
367
368
369 /*****************************************************************************/
370 /* Get object size method.                                                   */
371 /*****************************************************************************/
372 static void
373 get_size (glLabelObject *object,
374           gdouble       *w,
375           gdouble       *h)
376 {
377         glLabelText          *ltext = (glLabelText *)object;
378         PangoFontMap         *fontmap;
379         PangoContext         *context;
380         cairo_font_options_t *options;
381         PangoStyle            style;
382         PangoLayout          *layout;
383         PangoFontDescription *desc;
384         gdouble               font_size;
385         gdouble               line_spacing;
386         GtkTextIter           start, end;
387         gchar                *text;
388         gdouble               w_parent, h_parent;
389         gint                  iw, ih;
390
391         gl_debug (DEBUG_LABEL, "START");
392
393         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
394
395         gl_label_object_get_raw_size (object, &w_parent, &h_parent);
396
397         if ( (w_parent != 0.0) || (h_parent != 0.0) ) {
398                 *w = w_parent;
399                 *h = h_parent;
400                 return;
401         }
402
403         if (!ltext->priv->size_changed)
404         {
405                 *w = ltext->priv->w;
406                 *h = ltext->priv->h;
407                 return;
408         }
409
410         font_size = GL_LABEL_TEXT (object)->priv->font_size * FONT_SCALE;
411         line_spacing = GL_LABEL_TEXT (object)->priv->line_spacing;
412
413         gtk_text_buffer_get_bounds (ltext->priv->buffer, &start, &end);
414         text = gtk_text_buffer_get_text (ltext->priv->buffer,
415                                          &start, &end, FALSE);
416
417         
418         fontmap = pango_cairo_font_map_new ();
419         context = pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fontmap));
420         options = cairo_font_options_create ();
421         cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_NONE);
422         pango_cairo_context_set_font_options (context, options);
423         cairo_font_options_destroy (options);
424
425         layout = pango_layout_new (context);
426
427         style = GL_LABEL_TEXT (object)->priv->font_italic_flag ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL;
428
429         desc = pango_font_description_new ();
430         pango_font_description_set_family (desc, GL_LABEL_TEXT (object)->priv->font_family);
431         pango_font_description_set_weight (desc, GL_LABEL_TEXT (object)->priv->font_weight);
432         pango_font_description_set_style  (desc, style);
433         pango_font_description_set_size   (desc, font_size * PANGO_SCALE);
434         pango_layout_set_font_description (layout, desc);
435         pango_font_description_free       (desc);
436
437         pango_layout_set_spacing (layout, font_size * (line_spacing-1) * PANGO_SCALE);
438         pango_layout_set_text (layout, text, -1);
439         pango_layout_get_size (layout, &iw, &ih);
440         *w = ltext->priv->w = iw / PANGO_SCALE + 2*GL_LABEL_TEXT_MARGIN;
441         *h = ltext->priv->h = ih / PANGO_SCALE;
442         ltext->priv->size_changed = FALSE;
443
444         g_object_unref (layout);
445         g_object_unref (context);
446         g_object_unref (fontmap);
447         g_free (text);
448
449         gl_debug (DEBUG_LABEL, "END");
450 }
451
452
453 /*****************************************************************************/
454 /* Set font family method.                                                   */
455 /*****************************************************************************/
456 static void
457 set_font_family (glLabelObject *object,
458                  const gchar   *font_family)
459 {
460         glLabelText    *ltext = (glLabelText *)object;
461         GList          *family_names;
462         gchar          *good_font_family;
463
464         gl_debug (DEBUG_LABEL, "START");
465
466         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
467         g_return_if_fail (font_family);
468
469         family_names = gl_util_get_font_family_list ();
470         if (g_list_find_custom (family_names, font_family, (GCompareFunc)g_utf8_collate)) {
471                 good_font_family = g_strdup (font_family);
472         } else {
473                 if (family_names != NULL) {
474                         good_font_family = g_strdup (family_names->data); /* 1st entry */
475                 } else {
476                         good_font_family = g_strdup (font_family);
477                 }
478         }
479
480         if (ltext->priv->font_family) {
481                 if (g_strcasecmp (ltext->priv->font_family, good_font_family) == 0) {
482                         g_free (good_font_family);
483                         gl_debug (DEBUG_LABEL, "END (no change)");
484                         return;
485                 }
486                 g_free (ltext->priv->font_family);
487         }
488         ltext->priv->font_family = g_strdup (good_font_family);
489         g_free (good_font_family);
490
491         gl_debug (DEBUG_LABEL, "new font family = %s", ltext->priv->font_family);
492
493         ltext->priv->size_changed = TRUE;
494
495         gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
496
497         gl_debug (DEBUG_LABEL, "END");
498 }
499
500
501 /*****************************************************************************/
502 /* Set font size method.                                                     */
503 /*****************************************************************************/
504 static void
505 set_font_size (glLabelObject *object,
506                gdouble        font_size)
507 {
508         glLabelText    *ltext = (glLabelText *)object;
509
510         gl_debug (DEBUG_LABEL, "START");
511
512         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
513
514         if (ltext->priv->font_size != font_size) {
515
516                 ltext->priv->size_changed = TRUE;
517
518                 ltext->priv->font_size = font_size;
519                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
520
521         }
522
523         gl_debug (DEBUG_LABEL, "END");
524 }
525
526
527 /*****************************************************************************/
528 /* Set font weight method.                                                   */
529 /*****************************************************************************/
530 static void
531 set_font_weight (glLabelObject   *object,
532                  PangoWeight      font_weight)
533 {
534         glLabelText    *ltext = (glLabelText *)object;
535
536         gl_debug (DEBUG_LABEL, "START");
537
538         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
539
540         if (ltext->priv->font_weight != font_weight) {
541
542                 ltext->priv->size_changed = TRUE;
543
544                 ltext->priv->font_weight = font_weight;
545                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
546
547         }
548
549         gl_debug (DEBUG_LABEL, "END");
550 }
551
552
553 /*****************************************************************************/
554 /* Set font italic flag method.                                              */
555 /*****************************************************************************/
556 static void
557 set_font_italic_flag (glLabelObject *object,
558                       gboolean       font_italic_flag)
559 {
560         glLabelText    *ltext = (glLabelText *)object;
561
562         gl_debug (DEBUG_LABEL, "START");
563
564         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
565
566         if (ltext->priv->font_italic_flag != font_italic_flag) {
567
568                 ltext->priv->size_changed = TRUE;
569
570                 ltext->priv->font_italic_flag = font_italic_flag;
571                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
572
573         }
574
575         gl_debug (DEBUG_LABEL, "END");
576 }
577
578
579 /*****************************************************************************/
580 /* Set text alignment method.                                                */
581 /*****************************************************************************/
582 static void
583 set_text_alignment (glLabelObject    *object,
584                     PangoAlignment    text_alignment)
585 {
586         glLabelText    *ltext = (glLabelText *)object;
587
588         gl_debug (DEBUG_LABEL, "START");
589
590         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
591
592         if (ltext->priv->align != text_alignment) {
593
594                 ltext->priv->size_changed = TRUE;
595
596                 ltext->priv->align = text_alignment;
597                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
598
599         }
600
601         gl_debug (DEBUG_LABEL, "END");
602 }
603
604
605 /*****************************************************************************/
606 /* Set text line spacing method.                                             */
607 /*****************************************************************************/
608 static void
609 set_text_line_spacing (glLabelObject *object,
610                        gdouble        line_spacing)
611 {
612         glLabelText    *ltext = (glLabelText *)object;
613
614         gl_debug (DEBUG_LABEL, "START");
615
616         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
617
618         if (ltext->priv->line_spacing != line_spacing) {
619
620                 ltext->priv->size_changed = TRUE;
621
622                 ltext->priv->line_spacing = line_spacing;
623                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
624
625         }
626
627         gl_debug (DEBUG_LABEL, "END");
628 }
629
630
631 /*****************************************************************************/
632 /* Set text color method.                                                    */
633 /*****************************************************************************/
634 static void
635 set_text_color (glLabelObject *object,
636                 glColorNode   *text_color_node)
637 {
638         glLabelText    *ltext = (glLabelText *)object;
639
640         gl_debug (DEBUG_LABEL, "START");
641
642         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
643
644         if (!gl_color_node_equal (ltext->priv->color_node, text_color_node)) {
645
646                 gl_color_node_free (&(ltext->priv->color_node));
647                 ltext->priv->color_node = gl_color_node_dup (text_color_node);
648                 
649                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
650
651         }
652
653         gl_debug (DEBUG_LABEL, "END");
654 }
655
656
657 /*****************************************************************************/
658 /* Get font family method.                                                   */
659 /*****************************************************************************/
660 static gchar *
661 get_font_family (glLabelObject *object)
662 {
663         glLabelText    *ltext = (glLabelText *)object;
664
665         gl_debug (DEBUG_LABEL, "");
666
667         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), NULL);
668
669         return g_strdup (ltext->priv->font_family);
670 }
671
672
673 /*****************************************************************************/
674 /* Get font size method.                                                     */
675 /*****************************************************************************/
676 static gdouble
677 get_font_size (glLabelObject *object)
678 {
679         glLabelText    *ltext = (glLabelText *)object;
680
681         gl_debug (DEBUG_LABEL, "");
682
683         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), 0.0);
684
685         return ltext->priv->font_size;
686 }
687
688
689 /*****************************************************************************/
690 /* Get font weight method.                                                   */
691 /*****************************************************************************/
692 static PangoWeight
693 get_font_weight (glLabelObject   *object)
694 {
695         glLabelText    *ltext = (glLabelText *)object;
696
697         gl_debug (DEBUG_LABEL, "");
698
699         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), PANGO_WEIGHT_NORMAL);
700
701         return ltext->priv->font_weight;
702 }
703
704
705 /*****************************************************************************/
706 /* Get font italic flag method.                                              */
707 /*****************************************************************************/
708 static gboolean
709 get_font_italic_flag (glLabelObject *object)
710 {
711         glLabelText    *ltext = (glLabelText *)object;
712
713         gl_debug (DEBUG_LABEL, "");
714
715         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), FALSE);
716
717         return ltext->priv->font_italic_flag;
718 }
719
720
721 /*****************************************************************************/
722 /* Get text alignment method.                                                */
723 /*****************************************************************************/
724 static PangoAlignment
725 get_text_alignment (glLabelObject    *object)
726 {
727         glLabelText    *ltext = (glLabelText *)object;
728
729         gl_debug (DEBUG_LABEL, "");
730
731         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), GTK_JUSTIFY_LEFT);
732
733         return ltext->priv->align;
734 }
735
736
737 /*****************************************************************************/
738 /* Get text line spacing method.                                             */
739 /*****************************************************************************/
740 static gdouble
741 get_text_line_spacing (glLabelObject *object)
742 {
743         glLabelText    *ltext = (glLabelText *)object;
744
745         gl_debug (DEBUG_LABEL, "");
746
747         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), 0.0);
748
749         return ltext->priv->line_spacing;
750 }
751
752
753 /*****************************************************************************/
754 /* Get text color method.                                                    */
755 /*****************************************************************************/
756 static glColorNode*
757 get_text_color (glLabelObject *object)
758 {
759         glLabelText    *ltext = (glLabelText *)object;
760
761         gl_debug (DEBUG_LABEL, "");
762
763         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), 0);
764
765         return gl_color_node_dup (ltext->priv->color_node);
766 }
767
768
769 /*****************************************************************************/
770 /* Set auto shrink flag.                                                     */
771 /*****************************************************************************/
772 void
773 gl_label_text_set_auto_shrink (glLabelText      *ltext,
774                                gboolean          auto_shrink)
775 {
776         gl_debug (DEBUG_LABEL, "BEGIN");
777
778         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
779
780         if (ltext->priv->auto_shrink != auto_shrink) {
781
782                 ltext->priv->auto_shrink = auto_shrink;
783                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
784
785         }
786
787         gl_debug (DEBUG_LABEL, "END");
788 }
789
790
791 /*****************************************************************************/
792 /* Query auto shrink flag.                                                   */
793 /*****************************************************************************/
794 gboolean
795 gl_label_text_get_auto_shrink (glLabelText      *ltext)
796 {
797         gl_debug (DEBUG_LABEL, "");
798
799         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), 0);
800
801         return ltext->priv->auto_shrink;
802 }
803
804
805 /*****************************************************************************/
806 /* Draw object method.                                                       */
807 /*****************************************************************************/
808 static void
809 draw_object (glLabelObject *object,
810              cairo_t       *cr,
811              gboolean       screen_flag,
812              glMergeRecord *record)
813 {
814         glColorNode     *color_node;
815         guint            color;
816
817         gl_debug (DEBUG_LABEL, "START");
818
819         color_node = gl_label_object_get_text_color (object);
820         color = gl_color_node_expand (color_node, record);
821         if (color_node->field_flag && screen_flag)
822         {
823                 color = GL_COLOR_MERGE_DEFAULT;
824         }
825         gl_color_node_free (&color_node);
826         
827         draw_text_real (object, cr, screen_flag, record, color);
828
829         gl_debug (DEBUG_LABEL, "END");
830 }
831
832
833 /*****************************************************************************/
834 /* Draw shadow method.                                                       */
835 /*****************************************************************************/
836 static void
837 draw_shadow (glLabelObject *object,
838              cairo_t       *cr,
839              gboolean       screen_flag,
840              glMergeRecord *record)
841 {
842         glColorNode     *color_node;
843         guint            color;
844         glColorNode     *shadow_color_node;
845         gdouble          shadow_opacity;
846         guint            shadow_color;
847
848         gl_debug (DEBUG_LABEL, "START");
849
850         color_node = gl_label_object_get_text_color (object);
851         color = gl_color_node_expand (color_node, record);
852         if (color_node->field_flag && screen_flag)
853         {
854                 color = GL_COLOR_MERGE_DEFAULT;
855         }
856         gl_color_node_free (&color_node);
857         
858         shadow_color_node = gl_label_object_get_shadow_color (object);
859         if (shadow_color_node->field_flag)
860         {
861                 shadow_color_node->color = GL_COLOR_SHADOW_MERGE_DEFAULT;
862         }
863         shadow_opacity = gl_label_object_get_shadow_opacity (object);
864         shadow_color = gl_color_shadow (shadow_color_node->color, shadow_opacity, color);
865         gl_color_node_free (&shadow_color_node);
866
867         draw_text_real (object, cr, screen_flag, record, shadow_color);
868
869         gl_debug (DEBUG_LABEL, "END");
870 }
871
872
873 /*****************************************************************************/
874 /* Draw text.                                                                */
875 /*****************************************************************************/
876 static void
877 draw_text_real (glLabelObject *object,
878                 cairo_t       *cr,
879                 gboolean       screen_flag,
880                 glMergeRecord *record,
881                 guint          color)
882 {
883         gdouble          x0, y0;
884         cairo_matrix_t   matrix;
885         gdouble          object_w, object_h;
886         gdouble          raw_w, raw_h;
887         gchar           *text;
888         GList           *lines;
889         gchar           *font_family;
890         gdouble          font_size;
891         PangoWeight      font_weight;
892         gboolean         font_italic_flag;
893         gboolean         auto_shrink;
894         gdouble          text_line_spacing;
895         PangoAlignment   alignment;
896         PangoStyle       style;
897         PangoLayout     *layout;
898         PangoFontDescription *desc;
899         gdouble          scale_x, scale_y;
900         cairo_font_options_t *font_options;
901         PangoContext         *context;
902
903
904         gl_debug (DEBUG_LABEL, "START");
905
906         gl_label_object_get_position (object, &x0, &y0);
907         gl_label_object_get_matrix (object, &matrix);
908
909         gl_label_object_get_size (object, &object_w, &object_h);
910         gl_label_object_get_raw_size (object, &raw_w, &raw_h);
911         lines = gl_label_text_get_lines (GL_LABEL_TEXT (object));
912         font_family = gl_label_object_get_font_family (object);
913         font_size = gl_label_object_get_font_size (object) * FONT_SCALE;
914         font_weight = gl_label_object_get_font_weight (object);
915         font_italic_flag = gl_label_object_get_font_italic_flag (object);
916
917         alignment = gl_label_object_get_text_alignment (object);
918         text_line_spacing =
919                 gl_label_object_get_text_line_spacing (object);
920         auto_shrink = gl_label_text_get_auto_shrink (GL_LABEL_TEXT (object));
921
922         text = gl_text_node_lines_expand (lines, record);
923
924         style = font_italic_flag ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL;
925
926
927         if (!screen_flag && record && auto_shrink && (raw_w != 0.0))
928         {
929                 font_size = auto_shrink_font_size (cr,
930                                                    font_family,
931                                                    font_size,
932                                                    font_weight,
933                                                    style,
934                                                    text,
935                                                    object_w);
936         }
937
938
939         /*
940          * Workaround for pango Bug#341481.
941          * Render font at device scale and scale font size accordingly.
942          */
943         scale_x = 1.0;
944         scale_y = 1.0;
945         cairo_device_to_user_distance (cr, &scale_x, &scale_y);
946         scale_x = fabs (scale_x);
947         scale_y = fabs (scale_y);
948         cairo_save (cr);
949         cairo_scale (cr, scale_x, scale_y);
950
951         layout = pango_cairo_create_layout (cr);
952
953         font_options = cairo_font_options_create ();
954         cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
955         context = pango_layout_get_context (layout);
956         pango_cairo_context_set_font_options (context, font_options);
957         cairo_font_options_destroy (font_options);
958
959         desc = pango_font_description_new ();
960         pango_font_description_set_family (desc, font_family);
961         pango_font_description_set_weight (desc, font_weight);
962         pango_font_description_set_style  (desc, style);
963         pango_font_description_set_size   (desc, font_size * PANGO_SCALE / scale_x);
964         pango_layout_set_font_description (layout, desc);
965         pango_font_description_free       (desc);
966
967         pango_layout_set_text (layout, text, -1);
968         pango_layout_set_spacing (layout, font_size * (text_line_spacing-1) * PANGO_SCALE / scale_x);
969         if (raw_w == 0.0)
970         {
971                 pango_layout_set_width (layout, -1);
972         }
973         else
974         {
975                 pango_layout_set_width (layout, object_w * PANGO_SCALE / scale_x);
976         }
977         pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
978         pango_layout_set_alignment (layout, alignment);
979
980
981         cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (color));
982         cairo_move_to (cr, GL_LABEL_TEXT_MARGIN/scale_x, 0);
983         pango_cairo_show_layout (cr, layout);
984
985         cairo_restore (cr);
986
987         g_object_unref (layout);
988
989
990         gl_text_node_lines_free (&lines);
991         g_free (font_family);
992
993         gl_debug (DEBUG_LABEL, "END");
994 }
995
996
997 /*****************************************************************************/
998 /* Automatically shrink text size to fit within horizontal width.            */
999 /*****************************************************************************/
1000 static gdouble
1001 auto_shrink_font_size (cairo_t     *cr,
1002                        gchar       *family,
1003                        gdouble      size,
1004                        PangoWeight  weight,
1005                        PangoStyle   style,
1006                        gchar       *text,
1007                        gdouble      width)
1008 {
1009         PangoLayout          *layout;
1010         PangoFontDescription *desc;
1011         gint                  iw, ih;
1012         gdouble               layout_width;
1013         gdouble               new_size;
1014
1015         layout = pango_cairo_create_layout (cr);
1016
1017         desc = pango_font_description_new ();
1018         pango_font_description_set_family (desc, family);
1019         pango_font_description_set_weight (desc, weight);
1020         pango_font_description_set_style  (desc, style);
1021         pango_font_description_set_size   (desc, size * PANGO_SCALE);
1022         
1023         pango_layout_set_font_description (layout, desc);
1024         pango_font_description_free       (desc);
1025
1026         pango_layout_set_text (layout, text, -1);
1027         pango_layout_set_width (layout, -1);
1028         pango_layout_get_size (layout, &iw, &ih);
1029         layout_width = (gdouble)iw / (gdouble)PANGO_SCALE;
1030
1031         g_object_unref (layout);
1032
1033         g_print ("Object w = %g, layout w = %g\n", width, layout_width);
1034
1035         if ( layout_width > width )
1036         {
1037                 /* Scale down. */
1038                 new_size = size * (width-2*GL_LABEL_TEXT_MARGIN)/layout_width;
1039
1040                 /* Round down to nearest 1/2 point */
1041                 new_size = (int)(new_size*2.0) / 2.0;
1042
1043                 /* don't get ridiculously small. */
1044                 if (new_size < 1.0)
1045                 {
1046                         new_size = 1.0;
1047                 }
1048         }
1049         else
1050         {
1051                 new_size = size;
1052         }
1053
1054         return new_size;
1055 }
1056
1057
1058
1059 /*
1060  * Local Variables:       -- emacs
1061  * mode: C                -- emacs
1062  * c-basic-offset: 8      -- emacs
1063  * tab-width: 8           -- emacs
1064  * indent-tabs-mode: nil  -- emacs
1065  * End:                   -- emacs
1066  */