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