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 /*****************************************************************************/
G_DEFINE_TYPE(glLabelText,gl_label_text,GL_TYPE_LABEL_OBJECT)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
gl_label_text_init(glLabelText * ltext)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
gl_label_text_finalize(GObject * object)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 *
gl_label_text_new(glLabel * label,gboolean checkpoint)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
copy(glLabelObject * dst_object,glLabelObject * src_object)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
gl_label_text_set_lines(glLabelText * ltext,GList * lines,gboolean checkpoint)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
gl_label_text_set_text(glLabelText * ltext,const gchar * text,gboolean checkpoint)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 *
gl_label_text_get_buffer(glLabelText * ltext)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 *
gl_label_text_get_lines(glLabelText * ltext)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 *
gl_label_text_get_text(glLabelText * ltext)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
buffer_begin_user_action_cb(GtkTextBuffer * textbuffer,glLabelText * ltext)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
buffer_changed_cb(GtkTextBuffer * textbuffer,glLabelText * ltext)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
get_size(glLabelObject * object,gdouble * w,gdouble * h)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
set_font_family(glLabelObject * object,const gchar * font_family,gboolean checkpoint)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
set_font_size(glLabelObject * object,gdouble font_size,gboolean checkpoint)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
set_font_weight(glLabelObject * object,PangoWeight font_weight,gboolean checkpoint)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
set_font_italic_flag(glLabelObject * object,gboolean font_italic_flag,gboolean checkpoint)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
set_text_alignment(glLabelObject * object,PangoAlignment text_alignment,gboolean checkpoint)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
set_text_valignment(glLabelObject * object,glValignment text_valignment,gboolean checkpoint)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
set_text_line_spacing(glLabelObject * object,gdouble line_spacing,gboolean checkpoint)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
set_text_color(glLabelObject * object,glColorNode * text_color_node,gboolean checkpoint)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 *
get_font_family(glLabelObject * object)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
get_font_size(glLabelObject * object)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
get_font_weight(glLabelObject * object)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
get_font_italic_flag(glLabelObject * object)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
get_text_alignment(glLabelObject * object)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
get_text_valignment(glLabelObject * object)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
get_text_line_spacing(glLabelObject * object)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*
get_text_color(glLabelObject * object)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
gl_label_text_set_auto_shrink(glLabelText * ltext,gboolean auto_shrink,gboolean checkpoint)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
gl_label_text_get_auto_shrink(glLabelText * ltext)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
auto_shrink_font_size(cairo_t * cr,gchar * family,gdouble size,PangoWeight weight,PangoStyle style,gdouble line_spacing,gchar * text,gdouble width,gdouble height)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
layout_text(glLabelText * this,cairo_t * cr,gboolean screen_flag,glMergeRecord * record,gboolean path_only_flag)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) || auto_shrink )
1183 {
1184 pango_layout_set_width (layout, -1);
1185 }
1186 else
1187 {
1188 pango_layout_set_width (layout, (object_w - 2*GL_LABEL_TEXT_MARGIN) * 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
draw_object(glLabelObject * object,cairo_t * cr,gboolean screen_flag,glMergeRecord * record)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
draw_shadow(glLabelObject * object,cairo_t * cr,gboolean screen_flag,glMergeRecord * record)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 shadow_color = gl_color_node_expand (shadow_color_node, record);
1281 if (shadow_color_node->field_flag)
1282 {
1283 shadow_color_node->color = GL_COLOR_SHADOW_MERGE_DEFAULT;
1284 }
1285 shadow_opacity = gl_label_object_get_shadow_opacity (object);
1286 shadow_color = gl_color_set_opacity (shadow_color, shadow_opacity);
1287 gl_color_node_free (&shadow_color_node);
1288
1289 draw_text_real (object, cr, screen_flag, record, shadow_color);
1290
1291 gl_debug (DEBUG_LABEL, "END");
1292 }
1293
1294
1295 /*****************************************************************************/
1296 /* Draw text. */
1297 /*****************************************************************************/
1298 static void
draw_text_real(glLabelObject * object,cairo_t * cr,gboolean screen_flag,glMergeRecord * record,guint color)1299 draw_text_real (glLabelObject *object,
1300 cairo_t *cr,
1301 gboolean screen_flag,
1302 glMergeRecord *record,
1303 guint color)
1304 {
1305 gl_debug (DEBUG_LABEL, "START");
1306
1307 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (color));
1308 layout_text (GL_LABEL_TEXT (object), cr, screen_flag, record, FALSE);
1309
1310 gl_debug (DEBUG_LABEL, "END");
1311 }
1312
1313
1314 /*****************************************************************************/
1315 /* Is object at coordinates? */
1316 /*****************************************************************************/
1317 static gboolean
object_at(glLabelObject * object,cairo_t * cr,gdouble x,gdouble y)1318 object_at (glLabelObject *object,
1319 cairo_t *cr,
1320 gdouble x,
1321 gdouble y)
1322 {
1323 gdouble w, h;
1324 gdouble scale_x, scale_y;
1325
1326 gl_label_object_get_size (object, &w, &h);
1327
1328 if ( (x >= 0) && (x <= w) && (y >= 0) && (y <= h) )
1329 {
1330 cairo_new_path (cr);
1331 layout_text (GL_LABEL_TEXT (object), cr, TRUE, NULL, TRUE);
1332 if (cairo_in_fill (cr, x, y))
1333 {
1334 return TRUE;
1335 }
1336
1337
1338 scale_x = 1.0;
1339 scale_y = 1.0;
1340 cairo_device_to_user_distance (cr, &scale_x, &scale_y);
1341
1342 cairo_set_line_width (cr, 2*SELECTION_SLOP_PIXELS*scale_x);
1343
1344 if (cairo_in_stroke (cr, x, y))
1345 {
1346 return TRUE;
1347 }
1348
1349
1350 if (gl_label_object_is_selected (object))
1351 {
1352 cairo_new_path (cr);
1353 cairo_rectangle (cr, 0, 0, w, h);
1354
1355 scale_x = 1.0;
1356 scale_y = 1.0;
1357 cairo_device_to_user_distance (cr, &scale_x, &scale_y);
1358
1359 cairo_set_line_width (cr, 2*SELECTION_SLOP_PIXELS*scale_x);
1360
1361 if (cairo_in_stroke (cr, x, y))
1362 {
1363 return TRUE;
1364 }
1365 }
1366
1367 }
1368
1369 return FALSE;
1370 }
1371
1372
1373 /*****************************************************************************/
1374 /* Draw text style handles. */
1375 /*****************************************************************************/
1376 static void
draw_handles(glLabelObject * object,cairo_t * cr)1377 draw_handles (glLabelObject *object,
1378 cairo_t *cr)
1379 {
1380 gdouble w, h;
1381 gdouble scale_x, scale_y;
1382 gdouble dashes[2] = { 2, 2 };
1383
1384 gl_label_object_get_size (GL_LABEL_OBJECT(object), &w, &h);
1385
1386 cairo_save (cr);
1387
1388 cairo_rectangle (cr, 0, 0, w, h);
1389
1390 scale_x = 1.0;
1391 scale_y = 1.0;
1392 cairo_device_to_user_distance (cr, &scale_x, &scale_y);
1393 cairo_scale (cr, scale_x, scale_y);
1394
1395 cairo_set_dash (cr, dashes, 2, 0);
1396 cairo_set_line_width (cr, HANDLE_OUTLINE_WIDTH_PIXELS);
1397 cairo_set_source_rgba (cr, HANDLE_OUTLINE_RGBA_ARGS);
1398 cairo_stroke (cr);
1399
1400 cairo_restore (cr);
1401
1402 gl_label_object_draw_handles_box (object, cr);
1403 }
1404
1405
1406
1407
1408 /*
1409 * Local Variables: -- emacs
1410 * mode: C -- emacs
1411 * c-basic-offset: 8 -- emacs
1412 * tab-width: 8 -- emacs
1413 * indent-tabs-mode: nil -- emacs
1414 * End: -- emacs
1415 */
1416