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