]> git.sur5r.net Git - glabels/blob - src/label-text.c
Imported Upstream version 3.0.1
[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         cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
525         pango_cairo_context_set_font_options (context, options);
526         cairo_font_options_destroy (options);
527
528         layout = pango_layout_new (context);
529
530         style = GL_LABEL_TEXT (object)->priv->font_italic_flag ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL;
531
532         desc = pango_font_description_new ();
533         pango_font_description_set_family (desc, GL_LABEL_TEXT (object)->priv->font_family);
534         pango_font_description_set_weight (desc, GL_LABEL_TEXT (object)->priv->font_weight);
535         pango_font_description_set_style  (desc, style);
536         pango_font_description_set_size   (desc, font_size * PANGO_SCALE);
537         pango_layout_set_font_description (layout, desc);
538         pango_font_description_free       (desc);
539
540         pango_layout_set_spacing (layout, font_size * (line_spacing-1) * PANGO_SCALE);
541         pango_layout_set_text (layout, text, -1);
542         pango_layout_get_size (layout, &iw, &ih);
543         *w = ltext->priv->w = iw / PANGO_SCALE + 2*GL_LABEL_TEXT_MARGIN;
544         *h = ltext->priv->h = ih / PANGO_SCALE;
545         ltext->priv->size_changed = FALSE;
546
547         g_object_unref (layout);
548         g_object_unref (context);
549         g_object_unref (fontmap);
550         g_free (text);
551
552         gl_debug (DEBUG_LABEL, "END");
553 }
554
555
556 /*****************************************************************************/
557 /* Set font family method.                                                   */
558 /*****************************************************************************/
559 static void
560 set_font_family (glLabelObject *object,
561                  const gchar   *font_family,
562                  gboolean       checkpoint)
563 {
564         glLabelText    *ltext = (glLabelText *)object;
565         gchar          *good_font_family;
566         glLabel        *label;
567
568         gl_debug (DEBUG_LABEL, "START");
569
570         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
571         g_return_if_fail (font_family);
572
573         good_font_family = gl_font_util_validate_family (font_family);
574
575         if (ltext->priv->font_family) {
576                 if (strcmp (ltext->priv->font_family, good_font_family) == 0) {
577                         g_free (good_font_family);
578                         gl_debug (DEBUG_LABEL, "END (no change)");
579                         return;
580                 }
581                 g_free (ltext->priv->font_family);
582         }
583
584         if ( checkpoint )
585         {
586                 label = gl_label_object_get_parent (GL_LABEL_OBJECT (ltext));
587                 gl_label_checkpoint (label, _("Font family"));
588         }
589
590         ltext->priv->font_family = g_strdup (good_font_family);
591         g_free (good_font_family);
592
593         gl_debug (DEBUG_LABEL, "new font family = %s", ltext->priv->font_family);
594
595         ltext->priv->size_changed = TRUE;
596
597         gl_font_history_model_add_family (gl_font_history, ltext->priv->font_family);
598
599         gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
600
601         gl_debug (DEBUG_LABEL, "END");
602 }
603
604
605 /*****************************************************************************/
606 /* Set font size method.                                                     */
607 /*****************************************************************************/
608 static void
609 set_font_size (glLabelObject *object,
610                gdouble        font_size,
611                gboolean       checkpoint)
612 {
613         glLabelText    *ltext = (glLabelText *)object;
614         glLabel        *label;
615
616         gl_debug (DEBUG_LABEL, "START");
617
618         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
619
620         if (ltext->priv->font_size != font_size)
621         {
622                 if ( checkpoint )
623                 {
624                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (ltext));
625                         gl_label_checkpoint (label, _("Font size"));
626                 }
627
628                 ltext->priv->size_changed = TRUE;
629
630                 ltext->priv->font_size = font_size;
631                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
632         }
633
634         gl_debug (DEBUG_LABEL, "END");
635 }
636
637
638 /*****************************************************************************/
639 /* Set font weight method.                                                   */
640 /*****************************************************************************/
641 static void
642 set_font_weight (glLabelObject   *object,
643                  PangoWeight      font_weight,
644                  gboolean         checkpoint)
645 {
646         glLabelText    *ltext = (glLabelText *)object;
647         glLabel        *label;
648
649         gl_debug (DEBUG_LABEL, "START");
650
651         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
652
653         if (ltext->priv->font_weight != font_weight)
654         {
655                 if ( checkpoint )
656                 {
657                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (ltext));
658                         gl_label_checkpoint (label, _("Font weight"));
659                 }
660
661                 ltext->priv->size_changed = TRUE;
662
663                 ltext->priv->font_weight = font_weight;
664                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
665         }
666
667         gl_debug (DEBUG_LABEL, "END");
668 }
669
670
671 /*****************************************************************************/
672 /* Set font italic flag method.                                              */
673 /*****************************************************************************/
674 static void
675 set_font_italic_flag (glLabelObject *object,
676                       gboolean       font_italic_flag,
677                       gboolean       checkpoint)
678 {
679         glLabelText    *ltext = (glLabelText *)object;
680         glLabel        *label;
681
682         gl_debug (DEBUG_LABEL, "START");
683
684         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
685
686         if (ltext->priv->font_italic_flag != font_italic_flag)
687         {
688                 if ( checkpoint )
689                 {
690                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (ltext));
691                         gl_label_checkpoint (label, _("Italic"));
692                 }
693
694                 ltext->priv->size_changed = TRUE;
695
696                 ltext->priv->font_italic_flag = font_italic_flag;
697                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
698         }
699
700         gl_debug (DEBUG_LABEL, "END");
701 }
702
703
704 /*****************************************************************************/
705 /* Set text alignment method.                                                */
706 /*****************************************************************************/
707 static void
708 set_text_alignment (glLabelObject    *object,
709                     PangoAlignment    text_alignment,
710                     gboolean          checkpoint)
711 {
712         glLabelText    *ltext = (glLabelText *)object;
713         glLabel        *label;
714
715         gl_debug (DEBUG_LABEL, "START");
716
717         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
718
719         if (ltext->priv->align != text_alignment)
720         {
721                 if ( checkpoint )
722                 {
723                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (ltext));
724                         gl_label_checkpoint (label, _("Align text"));
725                 }
726
727                 ltext->priv->size_changed = TRUE;
728
729                 ltext->priv->align = text_alignment;
730                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
731         }
732
733         gl_debug (DEBUG_LABEL, "END");
734 }
735
736
737 /*****************************************************************************/
738 /* Set text line spacing method.                                             */
739 /*****************************************************************************/
740 static void
741 set_text_line_spacing (glLabelObject *object,
742                        gdouble        line_spacing,
743                        gboolean       checkpoint)
744 {
745         glLabelText    *ltext = (glLabelText *)object;
746         glLabel        *label;
747
748         gl_debug (DEBUG_LABEL, "START");
749
750         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
751
752         if (ltext->priv->line_spacing != line_spacing)
753         {
754                 if ( checkpoint )
755                 {
756                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (ltext));
757                         gl_label_checkpoint (label, _("Line spacing"));
758                 }
759
760                 ltext->priv->size_changed = TRUE;
761
762                 ltext->priv->line_spacing = line_spacing;
763                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
764         }
765
766         gl_debug (DEBUG_LABEL, "END");
767 }
768
769
770 /*****************************************************************************/
771 /* Set text color method.                                                    */
772 /*****************************************************************************/
773 static void
774 set_text_color (glLabelObject *object,
775                 glColorNode   *text_color_node,
776                 gboolean       checkpoint)
777 {
778         glLabelText    *ltext = (glLabelText *)object;
779         glLabel        *label;
780
781         gl_debug (DEBUG_LABEL, "START");
782
783         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
784
785         if (!gl_color_node_equal (ltext->priv->color_node, text_color_node))
786         {
787                 if ( checkpoint )
788                 {
789                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (ltext));
790                         gl_label_checkpoint (label, _("Text color"));
791                 }
792
793                 gl_color_node_free (&(ltext->priv->color_node));
794                 ltext->priv->color_node = gl_color_node_dup (text_color_node);
795                 
796                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
797         }
798
799         gl_debug (DEBUG_LABEL, "END");
800 }
801
802
803 /*****************************************************************************/
804 /* Get font family method.                                                   */
805 /*****************************************************************************/
806 static gchar *
807 get_font_family (glLabelObject *object)
808 {
809         glLabelText    *ltext = (glLabelText *)object;
810
811         gl_debug (DEBUG_LABEL, "");
812
813         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), NULL);
814
815         return g_strdup (ltext->priv->font_family);
816 }
817
818
819 /*****************************************************************************/
820 /* Get font size method.                                                     */
821 /*****************************************************************************/
822 static gdouble
823 get_font_size (glLabelObject *object)
824 {
825         glLabelText    *ltext = (glLabelText *)object;
826
827         gl_debug (DEBUG_LABEL, "");
828
829         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), 0.0);
830
831         return ltext->priv->font_size;
832 }
833
834
835 /*****************************************************************************/
836 /* Get font weight method.                                                   */
837 /*****************************************************************************/
838 static PangoWeight
839 get_font_weight (glLabelObject   *object)
840 {
841         glLabelText    *ltext = (glLabelText *)object;
842
843         gl_debug (DEBUG_LABEL, "");
844
845         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), PANGO_WEIGHT_NORMAL);
846
847         return ltext->priv->font_weight;
848 }
849
850
851 /*****************************************************************************/
852 /* Get font italic flag method.                                              */
853 /*****************************************************************************/
854 static gboolean
855 get_font_italic_flag (glLabelObject *object)
856 {
857         glLabelText    *ltext = (glLabelText *)object;
858
859         gl_debug (DEBUG_LABEL, "");
860
861         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), FALSE);
862
863         return ltext->priv->font_italic_flag;
864 }
865
866
867 /*****************************************************************************/
868 /* Get text alignment method.                                                */
869 /*****************************************************************************/
870 static PangoAlignment
871 get_text_alignment (glLabelObject    *object)
872 {
873         glLabelText    *ltext = (glLabelText *)object;
874
875         gl_debug (DEBUG_LABEL, "");
876
877         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), GTK_JUSTIFY_LEFT);
878
879         return ltext->priv->align;
880 }
881
882
883 /*****************************************************************************/
884 /* Get text line spacing method.                                             */
885 /*****************************************************************************/
886 static gdouble
887 get_text_line_spacing (glLabelObject *object)
888 {
889         glLabelText    *ltext = (glLabelText *)object;
890
891         gl_debug (DEBUG_LABEL, "");
892
893         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), 0.0);
894
895         return ltext->priv->line_spacing;
896 }
897
898
899 /*****************************************************************************/
900 /* Get text color method.                                                    */
901 /*****************************************************************************/
902 static glColorNode*
903 get_text_color (glLabelObject *object)
904 {
905         glLabelText    *ltext = (glLabelText *)object;
906
907         gl_debug (DEBUG_LABEL, "");
908
909         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), 0);
910
911         return gl_color_node_dup (ltext->priv->color_node);
912 }
913
914
915 /*****************************************************************************/
916 /* Set auto shrink flag.                                                     */
917 /*****************************************************************************/
918 void
919 gl_label_text_set_auto_shrink (glLabelText      *ltext,
920                                gboolean          auto_shrink,
921                                gboolean          checkpoint)
922 {
923         glLabel *label;
924
925         gl_debug (DEBUG_LABEL, "BEGIN");
926
927         g_return_if_fail (ltext && GL_IS_LABEL_TEXT (ltext));
928
929         if (ltext->priv->auto_shrink != auto_shrink)
930         {
931                 if ( checkpoint )
932                 {
933                         label = gl_label_object_get_parent (GL_LABEL_OBJECT (ltext));
934                         gl_label_checkpoint (label, _("Auto shrink"));
935                 }
936
937                 ltext->priv->auto_shrink = auto_shrink;
938                 gl_label_object_emit_changed (GL_LABEL_OBJECT(ltext));
939         }
940
941         gl_debug (DEBUG_LABEL, "END");
942 }
943
944
945 /*****************************************************************************/
946 /* Query auto shrink flag.                                                   */
947 /*****************************************************************************/
948 gboolean
949 gl_label_text_get_auto_shrink (glLabelText      *ltext)
950 {
951         gl_debug (DEBUG_LABEL, "");
952
953         g_return_val_if_fail (ltext && GL_IS_LABEL_TEXT (ltext), 0);
954
955         return ltext->priv->auto_shrink;
956 }
957
958
959 /*****************************************************************************/
960 /* Automatically shrink text size to fit within horizontal width.            */
961 /*****************************************************************************/
962 static gdouble
963 auto_shrink_font_size (cairo_t     *cr,
964                        gchar       *family,
965                        gdouble      size,
966                        PangoWeight  weight,
967                        PangoStyle   style,
968                        gchar       *text,
969                        gdouble      width)
970 {
971         PangoLayout          *layout;
972         PangoFontDescription *desc;
973         gint                  iw, ih;
974         gdouble               layout_width;
975         gdouble               new_size;
976
977         layout = pango_cairo_create_layout (cr);
978
979         desc = pango_font_description_new ();
980         pango_font_description_set_family (desc, family);
981         pango_font_description_set_weight (desc, weight);
982         pango_font_description_set_style  (desc, style);
983         pango_font_description_set_size   (desc, size * PANGO_SCALE);
984
985         pango_layout_set_font_description (layout, desc);
986         pango_font_description_free       (desc);
987
988         pango_layout_set_text (layout, text, -1);
989         pango_layout_set_width (layout, -1);
990         pango_layout_get_size (layout, &iw, &ih);
991         layout_width = (gdouble)iw / (gdouble)PANGO_SCALE;
992
993         g_object_unref (layout);
994
995         g_print ("Object w = %g, layout w = %g\n", width, layout_width);
996
997         if ( layout_width > width )
998         {
999                 /* Scale down. */
1000                 new_size = size * (width-2*GL_LABEL_TEXT_MARGIN)/layout_width;
1001
1002                 /* Round down to nearest 1/2 point */
1003                 new_size = (int)(new_size*2.0) / 2.0;
1004
1005                 /* don't get ridiculously small. */
1006                 if (new_size < 1.0)
1007                 {
1008                         new_size = 1.0;
1009                 }
1010         }
1011         else
1012         {
1013                 new_size = size;
1014         }
1015
1016         return new_size;
1017 }
1018
1019
1020 /*****************************************************************************/
1021 /* Update pango layout.                                                      */
1022 /*****************************************************************************/
1023 static void
1024 set_text_path (glLabelText      *this,
1025                cairo_t          *cr,
1026                gboolean          screen_flag,
1027                glMergeRecord    *record)
1028 {
1029         gdouble               object_w, object_h;
1030         gdouble               raw_w, raw_h;
1031         gchar                *text;
1032         GList                *lines;
1033         gdouble               font_size;
1034         gboolean              auto_shrink;
1035         PangoLayout          *layout;
1036         PangoStyle            style;
1037         PangoFontDescription *desc;
1038         cairo_font_options_t *font_options;
1039         PangoContext         *context;
1040
1041
1042         gl_debug (DEBUG_LABEL, "START");
1043
1044         cairo_save (cr);
1045
1046         gl_label_object_get_size (GL_LABEL_OBJECT (this), &object_w, &object_h);
1047         gl_label_object_get_raw_size (GL_LABEL_OBJECT (this), &raw_w, &raw_h);
1048
1049         lines = gl_label_text_get_lines (this);
1050         text = gl_text_node_lines_expand (lines, record);
1051
1052         style = this->priv->font_italic_flag ? PANGO_STYLE_ITALIC : PANGO_STYLE_NORMAL;
1053
1054         font_size   = this->priv->font_size * FONT_SCALE;
1055         auto_shrink = gl_label_text_get_auto_shrink (this);
1056         if (!screen_flag && record && auto_shrink && (raw_w != 0.0))
1057         {
1058                 font_size = auto_shrink_font_size (cr,
1059                                                    this->priv->font_family,
1060                                                    font_size,
1061                                                    this->priv->font_weight,
1062                                                    style,
1063                                                    text,
1064                                                    object_w);
1065         }
1066
1067
1068         layout = pango_cairo_create_layout (cr);
1069
1070         font_options = cairo_font_options_create ();
1071         cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
1072         cairo_font_options_set_hint_metrics (font_options, CAIRO_HINT_METRICS_OFF);
1073         context = pango_layout_get_context (layout);
1074         pango_cairo_context_set_font_options (context, font_options);
1075         cairo_font_options_destroy (font_options);
1076
1077         desc = pango_font_description_new ();
1078         pango_font_description_set_family (desc, this->priv->font_family);
1079         pango_font_description_set_weight (desc, this->priv->font_weight);
1080         pango_font_description_set_size   (desc, font_size * PANGO_SCALE);
1081         pango_font_description_set_style  (desc, style);
1082         pango_layout_set_font_description (layout, desc);
1083         pango_font_description_free       (desc);
1084
1085         pango_layout_set_text (layout, text, -1);
1086         pango_layout_set_spacing (layout, font_size * (this->priv->line_spacing-1) * PANGO_SCALE);
1087         if (raw_w == 0.0)
1088         {
1089                 pango_layout_set_width (layout, -1);
1090         }
1091         else
1092         {
1093                 pango_layout_set_width (layout, object_w * PANGO_SCALE);
1094         }
1095         pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
1096         pango_layout_set_alignment (layout, this->priv->align);
1097
1098
1099         cairo_move_to (cr, GL_LABEL_TEXT_MARGIN, 0);
1100         pango_cairo_layout_path (cr, layout);
1101
1102         g_object_unref (layout);
1103         gl_text_node_lines_free (&lines);
1104
1105         cairo_restore (cr);
1106
1107         gl_debug (DEBUG_LABEL, "END");
1108 }
1109
1110
1111 /*****************************************************************************/
1112 /* Draw object method.                                                       */
1113 /*****************************************************************************/
1114 static void
1115 draw_object (glLabelObject *object,
1116              cairo_t       *cr,
1117              gboolean       screen_flag,
1118              glMergeRecord *record)
1119 {
1120         glColorNode     *color_node;
1121         guint            color;
1122
1123         gl_debug (DEBUG_LABEL, "START");
1124
1125         color_node = gl_label_object_get_text_color (object);
1126         color = gl_color_node_expand (color_node, record);
1127         if (color_node->field_flag && screen_flag)
1128         {
1129                 color = GL_COLOR_MERGE_DEFAULT;
1130         }
1131         gl_color_node_free (&color_node);
1132
1133         draw_text_real (object, cr, screen_flag, record, color);
1134
1135         gl_debug (DEBUG_LABEL, "END");
1136 }
1137
1138
1139 /*****************************************************************************/
1140 /* Draw shadow method.                                                       */
1141 /*****************************************************************************/
1142 static void
1143 draw_shadow (glLabelObject *object,
1144              cairo_t       *cr,
1145              gboolean       screen_flag,
1146              glMergeRecord *record)
1147 {
1148         glColorNode     *color_node;
1149         guint            color;
1150         glColorNode     *shadow_color_node;
1151         gdouble          shadow_opacity;
1152         guint            shadow_color;
1153
1154         gl_debug (DEBUG_LABEL, "START");
1155
1156         color_node = gl_label_object_get_text_color (object);
1157         color = gl_color_node_expand (color_node, record);
1158         if (color_node->field_flag && screen_flag)
1159         {
1160                 color = GL_COLOR_MERGE_DEFAULT;
1161         }
1162         gl_color_node_free (&color_node);
1163
1164         shadow_color_node = gl_label_object_get_shadow_color (object);
1165         if (shadow_color_node->field_flag)
1166         {
1167                 shadow_color_node->color = GL_COLOR_SHADOW_MERGE_DEFAULT;
1168         }
1169         shadow_opacity = gl_label_object_get_shadow_opacity (object);
1170         shadow_color = gl_color_shadow (shadow_color_node->color, shadow_opacity, color);
1171         gl_color_node_free (&shadow_color_node);
1172
1173         draw_text_real (object, cr, screen_flag, record, shadow_color);
1174
1175         gl_debug (DEBUG_LABEL, "END");
1176 }
1177
1178
1179 /*****************************************************************************/
1180 /* Draw text.                                                                */
1181 /*****************************************************************************/
1182 static void
1183 draw_text_real (glLabelObject *object,
1184                 cairo_t       *cr,
1185                 gboolean       screen_flag,
1186                 glMergeRecord *record,
1187                 guint          color)
1188 {
1189         gl_debug (DEBUG_LABEL, "START");
1190
1191         set_text_path (GL_LABEL_TEXT (object), cr, screen_flag, record);
1192
1193         cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (color));
1194         cairo_fill (cr);
1195
1196         gl_debug (DEBUG_LABEL, "END");
1197 }
1198
1199
1200 /*****************************************************************************/
1201 /* Is object at coordinates?                                                 */
1202 /*****************************************************************************/
1203 static gboolean
1204 object_at (glLabelObject *object,
1205            cairo_t       *cr,
1206            gdouble        x,
1207            gdouble        y)
1208 {
1209         gdouble           w, h;
1210         gdouble           scale_x, scale_y;
1211
1212         gl_label_object_get_size (object, &w, &h);
1213
1214         if ( (x >= 0) && (x <= w) && (y >= 0) && (y <= h) )
1215         {
1216                 cairo_new_path (cr);
1217                 set_text_path (GL_LABEL_TEXT (object), cr, TRUE, NULL);
1218                 if (cairo_in_fill (cr, x, y))
1219                 {
1220                         return TRUE;
1221                 }
1222
1223
1224                 scale_x = 1.0;
1225                 scale_y = 1.0;
1226                 cairo_device_to_user_distance (cr, &scale_x, &scale_y);
1227
1228                 cairo_set_line_width (cr, 2*SELECTION_SLOP_PIXELS*scale_x);
1229
1230                 if (cairo_in_stroke (cr, x, y))
1231                 {
1232                         return TRUE;
1233                 }
1234
1235
1236                 if (gl_label_object_is_selected (object))
1237                 {
1238                         cairo_new_path (cr);
1239                         cairo_rectangle (cr, 0, 0, w, h);
1240
1241                         scale_x = 1.0;
1242                         scale_y = 1.0;
1243                         cairo_device_to_user_distance (cr, &scale_x, &scale_y);
1244
1245                         cairo_set_line_width (cr, 2*SELECTION_SLOP_PIXELS*scale_x);
1246
1247                         if (cairo_in_stroke (cr, x, y))
1248                         {
1249                                 return TRUE;
1250                         }
1251                 }
1252
1253         }
1254
1255         return FALSE;
1256 }
1257
1258
1259 /*****************************************************************************/
1260 /* Draw text style handles.                                                  */
1261 /*****************************************************************************/
1262 static void
1263 draw_handles (glLabelObject     *object,
1264               cairo_t           *cr)
1265 {
1266         gdouble w, h;
1267         gdouble scale_x, scale_y;
1268         gdouble dashes[2] = { 2, 2 };
1269
1270         gl_label_object_get_size (GL_LABEL_OBJECT(object), &w, &h);
1271
1272         cairo_save (cr);
1273
1274         cairo_rectangle (cr, 0, 0, w, h);
1275
1276         scale_x = 1.0;
1277         scale_y = 1.0;
1278         cairo_device_to_user_distance (cr, &scale_x, &scale_y);
1279         cairo_scale (cr, scale_x, scale_y);
1280
1281         cairo_set_dash (cr, dashes, 2, 0);
1282         cairo_set_line_width (cr, HANDLE_OUTLINE_WIDTH_PIXELS);
1283         cairo_set_source_rgba (cr, HANDLE_OUTLINE_RGBA_ARGS);
1284         cairo_stroke (cr);
1285
1286         cairo_restore (cr);
1287
1288         gl_label_object_draw_handles_box (object, cr);
1289 }
1290
1291
1292
1293
1294 /*
1295  * Local Variables:       -- emacs
1296  * mode: C                -- emacs
1297  * c-basic-offset: 8      -- emacs
1298  * tab-width: 8           -- emacs
1299  * indent-tabs-mode: nil  -- emacs
1300  * End:                   -- emacs
1301  */