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