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