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