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