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