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