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