68e53c73c972d4f5309c5b614b9010c70ff43ea0
[colloquium.git] / src / tool_text.c
1 /*
2  * tool_text.c
3  *
4  * Colloquium - A tiny presentation program
5  *
6  * Copyright (c) 2011 Thomas White <taw@bitwiz.org.uk>
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
20  *
21  */
22
23
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27
28 #include <stdlib.h>
29 #include <string.h>
30 #include <assert.h>
31 #include <math.h>
32 #include <gdk/gdkkeysyms.h>
33
34 #include "presentation.h"
35 #include "objects.h"
36 #include "mainwindow.h"
37 #include "slide_render.h"
38 #include "loadsave.h"
39
40
41 enum text_drag_reason
42 {
43         TEXT_DRAG_REASON_NONE,
44         TEXT_DRAG_REASON_RESIZE,
45 };
46
47
48 struct text_toolinfo
49 {
50         struct toolinfo        base;
51         PangoContext          *pc;
52         enum text_drag_reason  drag_reason;
53         enum corner            drag_corner;
54         double                 box_x;
55         double                 box_y;
56         double                 box_width;
57         double                 box_height;
58         double                 drag_initial_x;
59         double                 drag_initial_y;
60 };
61
62
63 struct text_object
64 {
65         struct object        base;
66
67         char                 *text;
68         size_t                text_len;
69         int                   insertion_point;
70         int                   insertion_trail;
71         PangoLayout          *layout;
72         PangoFontDescription *fontdesc;
73         double                offs_x;
74         double                offs_y;
75         int                   furniture;
76
77         PangoContext        **pc;
78 };
79
80
81 static void calculate_size_from_style(struct text_object *o,
82                                       double *peright, double *pebottom,
83                                       double *pmw, double *pmh)
84 {
85         double max_width, max_height;
86         double ebottom, eright, mw, mh;
87
88         eright = o->base.parent->parent->slide_width
89                               - o->base.style->margin_right;
90         ebottom = o->base.parent->parent->slide_height
91                               - o->base.style->margin_bottom;
92         mw = o->base.parent->parent->slide_width;
93         mh = o->base.parent->parent->slide_height;
94
95         *peright = eright;  *pebottom = ebottom;
96         *pmw = mw;  *pmh = mh;
97
98         max_width = mw - o->base.style->margin_left
99                                       - o->base.style->margin_right;
100
101
102         /* Use the provided maximum width if it exists and is smaller */
103         if ( o->base.style->use_max_width
104              && (o->base.style->max_width < max_width) )
105         {
106                 max_width = o->base.style->max_width;
107         }
108
109         max_height = mh - o->base.style->margin_top
110                                            - o->base.style->margin_bottom;
111
112         pango_layout_set_width(o->layout, max_width*PANGO_SCALE);
113         pango_layout_set_height(o->layout, max_height*PANGO_SCALE);
114         pango_layout_set_wrap(o->layout, PANGO_WRAP_WORD_CHAR);
115         pango_layout_set_ellipsize(o->layout, PANGO_ELLIPSIZE_MIDDLE);
116
117         switch ( o->base.style->halign ) {
118         case J_LEFT :
119                 pango_layout_set_alignment(o->layout, PANGO_ALIGN_LEFT);
120                 break;
121         case J_RIGHT :
122                 pango_layout_set_alignment(o->layout, PANGO_ALIGN_RIGHT);
123                 break;
124         case J_CENTER :
125                 pango_layout_set_alignment(o->layout, PANGO_ALIGN_CENTER);
126                 break;
127         }
128 }
129
130
131 static void calculate_position_from_style(struct text_object *o,
132                                           double eright, double ebottom,
133                                           double mw, double mh)
134 {
135         double xo, yo;
136
137         xo = o->base.bb_width;  yo = o->base.bb_height;
138
139         switch ( o->base.style->halign ) {
140         case J_LEFT :
141                 o->base.x = o->base.style->margin_left;
142                 break;
143         case J_RIGHT :
144                 o->base.x = eright - xo;
145                 break;
146         case J_CENTER :
147                 o->base.x = mw/2.0 - xo/2.0 + o->base.style->offset_x;
148                 break;
149         }
150
151         /* Correct if centering crashes into opposite margin */
152         if ( o->base.style->halign == J_CENTER )
153         {
154                 if ( o->base.x < o->base.style->margin_left ) {
155                         o->base.x = o->base.style->margin_left;
156                 }
157
158                 if ( o->base.x + xo > eright ) {
159                         o->base.x = eright - xo;
160                 }
161         }
162
163         switch ( o->base.style->valign ) {
164         case V_TOP :
165                 o->base.y = o->base.style->margin_top;
166                 break;
167         case V_BOTTOM :
168                 o->base.y = ebottom - yo;
169                 break;
170         case V_CENTER :
171                 o->base.y = mh/2.0 - yo/2.0 + o->base.style->offset_y;
172                 break;
173         }
174
175         if ( o->base.style->valign == V_CENTER ) {
176
177                 if ( o->base.y < o->base.style->margin_top ) {
178                         o->base.y = o->base.style->margin_top;
179                 }
180
181                 if ( o->base.y+yo + yo > ebottom )
182                 {
183                         o->base.y = ebottom - yo;
184                 }
185         }
186 }
187
188
189 static void update_text(struct text_object *o)
190 {
191         PangoRectangle logical;
192         double eright = 0.0;
193         double ebottom = 0.0;
194         double mw = 0.0;
195         double mh = 0.0;
196
197         if ( o->layout == NULL ) {
198                 if ( *o->pc != NULL ) {
199                         o->layout = pango_layout_new(*o->pc);
200                 } else {
201                         /* Can't render yet */
202                         return;
203                 }
204         }
205
206         o->furniture = o->base.style != o->base.parent->parent->ss->styles[0];
207
208         pango_layout_set_text(o->layout, o->text, -1);
209         o->fontdesc = pango_font_description_from_string(o->base.style->font);
210         pango_layout_set_font_description(o->layout, o->fontdesc);
211
212         if ( o->furniture ) {
213
214                 calculate_size_from_style(o, &eright, &ebottom, &mw, &mh);
215
216                 pango_layout_get_extents(o->layout, NULL, &logical);
217
218                 o->base.bb_width = logical.width / PANGO_SCALE;
219                 o->base.bb_height = logical.height / PANGO_SCALE;
220                 o->offs_x = logical.x / PANGO_SCALE;
221                 o->offs_y = logical.y / PANGO_SCALE;
222
223         } else {
224
225                 pango_layout_set_width(o->layout,
226                                        o->base.bb_width*PANGO_SCALE);
227                 pango_layout_set_height(o->layout,
228                                         o->base.bb_height*PANGO_SCALE);
229                 pango_layout_set_wrap(o->layout, PANGO_WRAP_WORD_CHAR);
230                 pango_layout_set_ellipsize(o->layout, PANGO_ELLIPSIZE_MIDDLE);
231
232         }
233
234         if ( o->furniture ) {
235                 calculate_position_from_style(o, eright, ebottom, mw, mh);
236         }
237 }
238
239
240 void insert_text(struct object *op, char *t)
241 {
242         struct text_object *o = (struct text_object *)op;
243         char *tmp;
244         size_t tlen, olen, offs;
245         int i;
246
247         assert(o->base.type == OBJ_TEXT);
248         tlen = strlen(t);
249         olen = strlen(o->text);
250
251         if ( tlen + olen + 1 > o->text_len ) {
252
253                 char *try;
254
255                 try = realloc(o->text, o->text_len + tlen + 64);
256                 if ( try == NULL ) return;  /* Failed to insert */
257                 o->text = try;
258                 o->text_len += 64;
259                 o->text_len += tlen;
260
261         }
262
263         tmp = malloc(o->text_len);
264         if ( tmp == NULL ) return;
265
266         offs = o->insertion_point + o->insertion_trail;
267
268         for ( i=0; i<offs; i++ ) {
269                 tmp[i] = o->text[i];
270         }
271         for ( i=0; i<tlen; i++ ) {
272                 tmp[i+offs] = t[i];
273         }
274         for ( i=0; i<olen-o->insertion_point; i++ ) {
275                 tmp[i+offs+tlen] = o->text[i+offs];
276         }
277         tmp[olen+tlen] = '\0';
278         memcpy(o->text, tmp, o->text_len);
279         free(tmp);
280
281         update_text(o);
282         redraw_slide(op->parent);
283         o->insertion_point += tlen;
284         o->base.empty = 0;
285 }
286
287
288 void move_cursor(struct object *op, int dir)
289 {
290         struct text_object *o = (struct text_object *)op;
291         int new_idx, new_trail;
292
293         pango_layout_move_cursor_visually(o->layout, TRUE, o->insertion_point,
294                                    o->insertion_trail,
295                                    dir, &new_idx, &new_trail);
296
297         if ( (new_idx >= 0) && (new_idx < G_MAXINT) ) {
298                 o->insertion_point = new_idx;
299                 o->insertion_trail = new_trail;
300         }
301 }
302
303
304 void move_cursor_left(struct object *op)
305 {
306         move_cursor(op, -1);
307         redraw_overlay(op->parent->parent);
308 }
309
310
311 void move_cursor_right(struct object *op)
312 {
313         move_cursor(op, +1);
314         redraw_overlay(op->parent->parent);
315 }
316
317
318 void handle_text_backspace(struct object *op)
319 {
320         int old_idx, new_idx;
321         struct text_object *o = (struct text_object *)op;
322
323         assert(o->base.type == OBJ_TEXT);
324
325         if ( o->insertion_point == 0 ) return;  /* Nothing to delete */
326
327         old_idx = o->insertion_point + o->insertion_trail;
328         move_cursor_left(op);
329         new_idx = o->insertion_point + o->insertion_trail;
330
331         memmove(o->text+new_idx, o->text+old_idx,
332                 o->text_len-new_idx);
333
334         if ( strlen(o->text) == 0 ) o->base.empty = 1;
335
336         update_text(o);
337         redraw_slide(op->parent);
338 }
339
340
341 static void render_text_object(cairo_t *cr, struct object *op)
342 {
343         struct text_object *o = (struct text_object *)op;
344         GdkColor col;
345
346         if ( o->layout == NULL ) {
347                 return;
348         }
349
350         cairo_move_to(cr, o->base.x - o->offs_x, o->base.y - o->offs_y);
351         gdk_color_parse(o->base.style->colour, &col);
352         gdk_cairo_set_source_color(cr, &col);  /* FIXME: Honour alpha as well */
353         pango_cairo_update_layout(cr, o->layout);
354         pango_cairo_show_layout(cr, o->layout);
355 }
356
357
358 static void draw_caret(cairo_t *cr, struct text_object *o)
359 {
360         double xposd, yposd, cx;
361         double clow, chigh;
362         PangoRectangle pos;
363         const double t = 1.8;
364
365         assert(o->base.type == OBJ_TEXT);
366
367         pango_layout_get_cursor_pos(o->layout,
368                                     o->insertion_point+o->insertion_trail,
369                                     &pos, NULL);
370
371         xposd = pos.x/PANGO_SCALE;
372         cx = o->base.x - o->offs_x + xposd;
373         yposd = pos.y/PANGO_SCALE;
374         clow = o->base.y - o->offs_y + yposd;
375         chigh = clow + (pos.height/PANGO_SCALE);
376
377         cairo_move_to(cr, cx, clow);
378         cairo_line_to(cr, cx, chigh);
379
380         cairo_move_to(cr, cx-t, clow-t);
381         cairo_line_to(cr, cx, clow);
382         cairo_move_to(cr, cx+t, clow-t);
383         cairo_line_to(cr, cx, clow);
384
385         cairo_move_to(cr, cx-t, chigh+t);
386         cairo_line_to(cr, cx, chigh);
387         cairo_move_to(cr, cx+t, chigh+t);
388         cairo_line_to(cr, cx, chigh);
389
390         cairo_set_source_rgb(cr, 0.86, 0.0, 0.0);
391         cairo_set_line_width(cr, 1.0);
392         cairo_stroke(cr);
393 }
394
395
396 static void update_text_object(struct object *op)
397 {
398         struct text_object *o = (struct text_object *)op;
399         update_text(o);
400 }
401
402
403 static void delete_text_object(struct object *op)
404 {
405         struct text_object *o = (struct text_object *)op;
406
407         if ( o->layout != NULL ) g_object_unref(o->layout);
408         if ( o->fontdesc != NULL ) pango_font_description_free(o->fontdesc);
409 }
410
411
412 static void serialize(struct object *op, struct serializer *ser)
413 {
414         struct text_object *o = (struct text_object *)op;
415
416         serialize_s(ser, "style", op->style->name);
417         if ( op->style == op->parent->parent->ss->styles[0] ) {
418                 serialize_f(ser, "x", op->x);
419                 serialize_f(ser, "y", op->y);
420                 serialize_f(ser, "w", op->bb_width);
421                 serialize_f(ser, "h", op->bb_height);
422         }
423
424         serialize_s(ser, "text", o->text);
425 }
426
427
428 static struct object *new_text_object(double x, double y, struct style *sty,
429                                       struct text_toolinfo *ti)
430 {
431         struct text_object *new;
432
433         new = calloc(1, sizeof(*new));
434         if ( new == NULL ) return NULL;
435
436         /* Base properties */
437         new->base.x = x;  new->base.y = y;
438         new->base.bb_width = 10.0;
439         new->base.bb_height = 40.0;
440         new->base.type = OBJ_TEXT;
441         new->base.empty = 1;
442         new->base.parent = NULL;
443         new->base.style = sty;
444
445         /* Text-specific stuff */
446         new->text = malloc(1);
447         new->text[0] = '\0';
448         new->text_len = 1;
449         new->insertion_point = 0;
450         new->insertion_trail = 0;
451         if ( ti->pc != NULL ) {
452                 new->layout = pango_layout_new(ti->pc);
453                 new->pc = NULL;
454         } else {
455                 new->layout = NULL;
456                 new->pc = &ti->pc;
457         }
458         new->fontdesc = NULL;
459
460         /* Methods for this object */
461         new->base.render_object = render_text_object;
462         new->base.delete_object = delete_text_object;
463         new->base.update_object = update_text_object;
464         new->base.serialize = serialize;
465
466         return (struct object *)new;
467 }
468
469
470 static struct object *add_text_object(struct slide *s, double x, double y,
471                                       struct style *sty,
472                                       struct text_toolinfo *ti)
473 {
474         struct object *o;
475
476         o = new_text_object(x, y, sty, ti);
477
478         if ( add_object_to_slide(s, o) ) {
479                 delete_object(o);
480                 return NULL;
481         }
482
483         redraw_slide(o->parent);
484
485         return o;
486 }
487
488
489 static void calculate_box_size(struct object *o, double x, double y,
490                                struct text_toolinfo *ti)
491 {
492         double ddx, ddy;
493
494         ddx = x - ti->drag_initial_x;
495         ddy = y - ti->drag_initial_y;
496
497         switch ( ti->drag_corner ) {
498
499                 case CORNER_BR :
500                 ti->box_x = o->x;
501                 ti->box_y = o->y;
502                 ti->box_width = o->bb_width + ddx;
503                 ti->box_height = o->bb_height + ddy;
504                 break;
505
506                 case CORNER_BL :
507                 ti->box_x = o->x + ddx;
508                 ti->box_y = o->y;
509                 ti->box_width = o->bb_width - ddx;
510                 ti->box_height = o->bb_height + ddy;
511                 break;
512
513                 case CORNER_TL :
514                 ti->box_x = o->x + ddx;
515                 ti->box_y = o->y + ddy;
516                 ti->box_width = o->bb_width - ddx;
517                 ti->box_height = o->bb_height - ddy;
518                 break;
519
520                 case CORNER_TR :
521                 ti->box_x = o->x;
522                 ti->box_y = o->y + ddy;
523                 ti->box_width = o->bb_width + ddx;
524                 ti->box_height = o->bb_height - ddy;
525                 break;
526
527                 case CORNER_NONE :
528                 break;
529
530         }
531
532         if ( ti->box_width < 20.0 ) ti->box_width = 20.0;
533         if ( ti->box_height < 20.0 ) ti->box_height = 20.0;
534 }
535
536
537 static void click_select(struct presentation *p, struct toolinfo *tip,
538                          double x, double y, GdkEventButton *event,
539                          enum drag_status *drag_status,
540                          enum drag_reason *drag_reason)
541 {
542         int xp, yp;
543         double xo, yo;
544         gboolean v;
545         enum corner c;
546         struct text_toolinfo *ti = (struct text_toolinfo *)tip;
547         struct text_object *o = (struct text_object *)p->editing_object;
548         int idx, trail;
549
550         assert(o->base.type == OBJ_TEXT);
551
552         xo = x - o->base.x;  yo = y - o->base.y;
553
554         /* Within the resizing region? */
555         c = which_corner(x, y, &o->base);
556         if ( (c != CORNER_NONE) && !o->furniture )
557         {
558                 ti->drag_reason = TEXT_DRAG_REASON_RESIZE;
559                 ti->drag_corner = c;
560
561                 ti->drag_initial_x = x;
562                 ti->drag_initial_y = y;
563
564                 calculate_box_size((struct object *)o, x, y, ti);
565
566                 /* Tell the MCP what we did, and return */
567                 *drag_status = DRAG_STATUS_DRAGGING;
568                 *drag_reason = DRAG_REASON_TOOL;
569                 return;
570         }
571
572         xp = (xo + o->offs_x)*PANGO_SCALE;
573         yp = (yo + o->offs_y)*PANGO_SCALE;
574
575         v = pango_layout_xy_to_index(o->layout, xp, yp, &idx, &trail);
576
577         o->insertion_point = idx;
578         o->insertion_trail = trail;
579 }
580
581
582 static void drag(struct toolinfo *tip, struct presentation *p,
583                  struct object *o, double x, double y)
584 {
585         struct text_toolinfo *ti = (struct text_toolinfo *)tip;
586
587         if ( ti->drag_reason == TEXT_DRAG_REASON_RESIZE ) {
588
589                 calculate_box_size(o, x, y, ti);
590                 redraw_overlay(p);
591
592         }
593 }
594
595
596 static void end_drag(struct toolinfo *tip, struct presentation *p,
597                      struct object *o, double x, double y)
598 {
599         struct text_toolinfo *ti = (struct text_toolinfo *)tip;
600
601         calculate_box_size((struct object *)o, x, y, ti);
602
603         o->x = ti->box_x;
604         o->y = ti->box_y;
605         o->bb_width = ti->box_width;
606         o->bb_height = ti->box_height;
607         update_text((struct text_object *)o);
608         redraw_slide(o->parent);
609
610         ti->drag_reason = TEXT_DRAG_REASON_NONE;
611 }
612
613
614 static void create_default(struct presentation *p, struct style *sty,
615                            struct toolinfo *tip)
616 {
617         struct object *n;
618         struct text_object *o;
619         struct text_toolinfo *ti = (struct text_toolinfo *)tip;
620
621         n = add_text_object(p->cur_edit_slide, 0.0, 0.0, sty, ti);
622         o = (struct text_object *)n;
623         o->furniture = 1;
624         update_text(o);
625         redraw_slide(((struct object *)o)->parent);
626         p->editing_object = n;
627 }
628
629
630 static void create_region(struct toolinfo *tip, struct presentation *p,
631                           double x1, double y1, double x2, double y2)
632 {
633         struct object *n;
634         struct text_toolinfo *ti = (struct text_toolinfo *)tip;
635         struct text_object *o;
636
637         n = add_text_object(p->cur_edit_slide, 0.0, 0.0, p->ss->styles[0], ti);
638         n->x = x1<x2 ? x1 : x2;
639         n->y = y1<y2 ? y1 : y2;
640         n->bb_width = fabs(x1-x2);
641         n->bb_height = fabs(y1-y2);
642
643         o = (struct text_object *)n;
644         o->furniture = 0;
645
646         update_text(o);
647         redraw_slide(((struct object *)o)->parent);
648         p->editing_object = n;
649 }
650
651
652 static void select_object(struct object *o, struct toolinfo *tip)
653 {
654         /* Do nothing */
655 }
656
657
658 static int deselect_object(struct object *o, struct toolinfo *tip)
659 {
660         if ( (o != NULL) && o->empty ) {
661                 delete_object(o);
662                 return 1;
663         }
664
665         return 0;
666 }
667
668
669 static void draw_overlay(struct toolinfo *tip, cairo_t *cr, struct object *n)
670 {
671         struct text_toolinfo *ti = (struct text_toolinfo *)tip;
672         struct text_object *o = (struct text_object *)n;
673
674         if ( n != NULL ) {
675
676                 draw_editing_box(cr, n->x, n->y, n->bb_width, n->bb_height);
677
678                 if ( !o->furniture ) {
679
680                         /* Draw resize handles */
681                         draw_resize_handle(cr, n->x, n->y+n->bb_height-20.0);
682                         draw_resize_handle(cr, n->x+n->bb_width-20.0, n->y);
683                         draw_resize_handle(cr, n->x, n->y);
684                         draw_resize_handle(cr, n->x+n->bb_width-20.0,
685                                            n->y+n->bb_height-20.0);
686
687                 }
688
689                 draw_caret(cr, o);
690         }
691
692         if ( ti->drag_reason == TEXT_DRAG_REASON_RESIZE ) {
693                 draw_rubberband_box(cr, ti->box_x, ti->box_y,
694                                         ti->box_width, ti->box_height);
695         }
696
697 }
698
699
700 static void key_pressed(struct object *o, guint keyval, struct toolinfo *tip)
701 {
702         if ( o == NULL ) return;
703
704         switch ( keyval ) {
705
706         case GDK_KEY_BackSpace :
707                 handle_text_backspace(o);
708                 break;
709
710         case GDK_KEY_Left :
711                 move_cursor_left(o);
712                 break;
713
714         case GDK_KEY_Right :
715                 move_cursor_right(o);
716                 break;
717
718         }
719 }
720
721
722 static void im_commit(struct object *o, gchar *str, struct toolinfo *tip)
723 {
724         insert_text(o, str);
725 }
726
727
728 static int valid_object(struct object *o)
729 {
730         if ( o->type == OBJ_TEXT ) return 1;
731         return 0;
732 }
733
734
735 static void realise(struct toolinfo *ti, GtkWidget *w, struct presentation *p)
736 {
737         struct text_toolinfo *tip = (struct text_toolinfo *)ti;
738         tip->pc = gtk_widget_get_pango_context(w);
739
740         ti->tbox = gtk_label_new("Text tool");
741         g_object_ref(ti->tbox);
742         gtk_widget_show(ti->tbox);
743 }
744
745
746 static struct object *deserialize(struct presentation *p, struct ds_node *root,
747                                   struct toolinfo *tip)
748 {
749         struct object *o;
750         struct text_object *to;
751         char *style;
752         char *text;
753         struct style *sty;
754         double x, y, w, h;
755         struct text_toolinfo *ti = (struct text_toolinfo *)tip;
756
757         if ( get_field_s(root, "style", &style) ) {
758                 fprintf(stderr, "Couldn't find style for object '%s'\n",
759                         root->key);
760                 return NULL;
761         }
762
763         sty = find_style(p->ss, style);
764         if ( sty == NULL ) {
765                 fprintf(stderr, "Style '%s' not found in style sheet.\n",
766                         style);
767                 free(style);
768                 return NULL;
769         }
770         free(style);
771
772         if ( sty == p->ss->styles[0] ) {
773
774                 if ( get_field_f(root, "x", &x) ) {
775                         fprintf(stderr,
776                                 "Couldn't find x position for object '%s'\n",
777                                 root->key);
778                         return NULL;
779                 }
780                 if ( get_field_f(root, "y", &y) ) {
781                         fprintf(stderr,
782                                 "Couldn't find y position for object '%s'\n",
783                                 root->key);
784                         return NULL;
785                 }
786                 if ( get_field_f(root, "w", &w) ) {
787                         fprintf(stderr,
788                                 "Couldn't find width for object '%s'\n",
789                                 root->key);
790                         return NULL;
791                 }
792                 if ( get_field_f(root, "h", &h) ) {
793                         fprintf(stderr,
794                                 "Couldn't find height for object '%s'\n",
795                                 root->key);
796                         return NULL;
797                 }
798
799         } else {
800
801                 /* Furniture */
802                 x = 0.0;
803                 y = 0.0;
804                 w = 0.0;
805                 h = 0.0;
806
807         }
808
809         o = new_text_object(x, y, sty, ti);
810         o->bb_width = w;
811         o->bb_height = h;
812
813         /* Apply the correct text */
814         if ( get_field_s(root, "text", &text) ) {
815                 fprintf(stderr, "Couldn't find text for object '%s'\n",
816                         root->key);
817                 return NULL;
818         }
819         to = (struct text_object *)o;
820         free(to->text);
821         to->text = text;
822         to->text_len = strlen(text);
823         o->empty = 0;
824
825         return o;
826 }
827
828
829 struct toolinfo *initialise_text_tool(GtkWidget *w)
830 {
831         struct text_toolinfo *ti;
832
833         ti = malloc(sizeof(*ti));
834
835         ti->base.click_select = click_select;
836         ti->base.create_default = create_default;
837         ti->base.select = select_object;
838         ti->base.deselect = deselect_object;
839         ti->base.drag = drag;
840         ti->base.end_drag = end_drag;
841         ti->base.create_region = create_region;
842         ti->base.draw_editing_overlay = draw_overlay;
843         ti->base.key_pressed = key_pressed;
844         ti->base.im_commit = im_commit;
845         ti->base.valid_object = valid_object;
846         ti->base.realise = realise;
847         ti->base.deserialize = deserialize;
848
849         ti->pc = NULL;
850
851         ti->drag_reason = DRAG_REASON_NONE;
852
853         return (struct toolinfo *)ti;
854 }