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