diff options
-rw-r--r-- | src/presentation.h | 2 | ||||
-rw-r--r-- | src/render.c | 202 | ||||
-rw-r--r-- | src/render.h | 37 | ||||
-rw-r--r-- | src/tool_text.c | 984 |
4 files changed, 1225 insertions, 0 deletions
diff --git a/src/presentation.h b/src/presentation.h index 423ea42..8707151 100644 --- a/src/presentation.h +++ b/src/presentation.h @@ -28,6 +28,7 @@ #endif #include <cairo.h> +#include <pango/pango.h> struct slide { @@ -50,6 +51,7 @@ struct slide struct frame { struct frame_class *cl; + PangoContext *pc; struct frame **rendering_order; int num_ro; diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..1e8a697 --- /dev/null +++ b/src/render.c @@ -0,0 +1,202 @@ +/* + * render.c + * + * Colloquium - A tiny presentation program + * + * Copyright (c) 2011 Thomas White <taw@bitwiz.org.uk> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <cairo.h> +#include <cairo-pdf.h> +#include <pango/pangocairo.h> +#include <assert.h> +#include <gdk/gdk.h> + +#include "storycode.h" +#include "stylesheet.h" +#include "presentation.h" + + +static void render_bgblock(cairo_t *cr, struct bgblock *b) +{ + GdkColor col1; + GdkColor col2; + cairo_pattern_t *patt = NULL; + double cx, cy, r, r1, r2; + + cairo_rectangle(cr, b->min_x, b->min_y, + b->max_x - b->min_x, + b->max_y - b->min_y); + + switch ( b->type ) { + + case BGBLOCK_SOLID : + gdk_color_parse(b->colour1, &col1); + gdk_cairo_set_source_color(cr, &col1); + /* FIXME: Honour alpha as well */ + cairo_fill(cr); + break; + + case BGBLOCK_GRADIENT_CIRCULAR : + cx = b->min_x + (b->max_x-b->min_x)/2.0; + cy = b->min_y + (b->max_y-b->min_y)/2.0; + r1 = (b->max_x-b->min_x)/2.0; + r2 = (b->max_y-b->min_y)/2.0; + r = r1 > r2 ? r1 : r2; + patt = cairo_pattern_create_radial(cx, cy, r, cx, cy, 0.0); + /* Fall-through */ + + case BGBLOCK_GRADIENT_X : + if ( patt == NULL ) { + patt = cairo_pattern_create_linear(b->min_x, 0.0, + b->max_y, 0.0); + } + /* Fall-through */ + + case BGBLOCK_GRADIENT_Y : + if ( patt == NULL ) { + patt = cairo_pattern_create_linear(0.0, b->min_y, + 0.0, b->max_y); + } + + gdk_color_parse(b->colour1, &col1); + gdk_color_parse(b->colour2, &col2); + cairo_pattern_add_color_stop_rgba(patt, 0.0, col1.red/65535.0, + col1.green/65535.0, + col1.blue/65535.0, + b->alpha1); + cairo_pattern_add_color_stop_rgba(patt, 1.0, col2.red/65535.0, + col2.green/65535.0, + col2.blue/65535.0, + b->alpha2); + cairo_set_source(cr, patt); + cairo_fill(cr); + cairo_pattern_destroy(patt); + break; + + case BGBLOCK_IMAGE : + cairo_set_source_rgb(cr, 1.0, 1.0, 1.0); + cairo_fill(cr); + + } +} + + +/* Render Level 1 Storycode */ +int render_sc(const char *sc, cairo_t *cr, double w, double h, PangoContext *pc) +{ + PangoLayout *layout; + PangoFontDescription *fontdesc; + GdkColor col; + + /* FIXME: Check for Level 1 markup e.g. image */ + + layout = pango_layout_new(pc); + + pango_cairo_update_layout(cr, layout); + pango_layout_set_width(layout, w*PANGO_SCALE); + pango_layout_set_height(layout, h*PANGO_SCALE); + + pango_layout_set_text(layout, sc, -1); + fontdesc = pango_font_description_from_string("Sans 12"); + + pango_layout_set_font_description(layout, fontdesc); + + cairo_move_to(cr, 0.0, 0.0); + + /* FIXME: Honour alpha as well */ + gdk_color_parse("#000000", &col); + gdk_cairo_set_source_color(cr, &col); + + pango_cairo_show_layout(cr, layout); + + pango_font_description_free(fontdesc); + g_object_unref(G_OBJECT(layout)); + + return 0; +} + + +static int render_frame(struct frame *fr, cairo_t *cr, double w, double h) +{ + int i; + + /* The rendering order is a list of children, but it also contains the + * current frame. In this way, it contains information about which + * layer the current frame is to be rendered as relative to its + * children. */ + for ( i=0; i<fr->num_ro; i++ ) { + + double nw, nh; + + if ( fr->rendering_order[i] == fr ) { + render_sc(fr->sc, cr, w, h, fr->pc); + continue; + } + + /* Sort out the transformation for the margins */ + cairo_translate(cr, fr->rendering_order[i]->cl->margin_left, + fr->rendering_order[i]->cl->margin_top); + + nw = w - fr->rendering_order[i]->cl->margin_left; + nw -= fr->rendering_order[i]->cl->margin_right; + + nh = h - fr->rendering_order[i]->cl->margin_top; + nh -= fr->rendering_order[i]->cl->margin_bottom; + + render_frame(fr->rendering_order[i], cr, nw, nh); + + } + + return 0; +} + + +cairo_surface_t *render_slide(struct slide *s, int w, int h) +{ + cairo_surface_t *surf; + cairo_t *cr; + cairo_font_options_t *fopts; + int i; + + surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + + cr = cairo_create(surf); + cairo_scale(cr, w, h); + + fopts = cairo_font_options_create(); + cairo_font_options_set_hint_style(fopts, CAIRO_HINT_STYLE_NONE); + cairo_font_options_set_hint_metrics(fopts, CAIRO_HINT_METRICS_OFF); + cairo_font_options_set_antialias(fopts, CAIRO_ANTIALIAS_SUBPIXEL); + cairo_set_font_options(cr, fopts); + + for ( i=0; i<s->st->n_bgblocks; i++ ) { + render_bgblock(cr, s->st->bgblocks[i]); + } + + render_frame(s->top, cr, w, h); + + cairo_font_options_destroy(fopts); + cairo_destroy(cr); + + return surf; +} diff --git a/src/render.h b/src/render.h new file mode 100644 index 0000000..d5054cd --- /dev/null +++ b/src/render.h @@ -0,0 +1,37 @@ +/* + * render.h + * + * Colloquium - A tiny presentation program + * + * Copyright (c) 2011 Thomas White <taw@bitwiz.org.uk> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef RENDER_H +#define RENDER_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "presentation.h" + +extern cairo_surface_t *render_slide(struct slide *s, int w, int h); + +extern int render_sc(const char *sc, cairo_t *cr, double w, double h, + PangoContext *pc); + +#endif /* RENDER_H */ diff --git a/src/tool_text.c b/src/tool_text.c new file mode 100644 index 0000000..1b62f91 --- /dev/null +++ b/src/tool_text.c @@ -0,0 +1,984 @@ +/* + * tool_text.c + * + * Colloquium - A tiny presentation program + * + * Copyright (c) 2011 Thomas White <taw@bitwiz.org.uk> + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <math.h> +#include <gdk/gdkkeysyms.h> + +#include "presentation.h" +#include "objects.h" +#include "mainwindow.h" +#include "slide_render.h" +#include "loadsave.h" + + +enum text_drag_reason +{ + TEXT_DRAG_REASON_NONE, + TEXT_DRAG_REASON_RESIZE, +}; + + +struct text_toolinfo +{ + struct toolinfo base; + PangoContext *pc; + enum text_drag_reason drag_reason; + enum corner drag_corner; + double box_x; + double box_y; + double box_width; + double box_height; + double drag_initial_x; + double drag_initial_y; +}; + + +struct text_object +{ + struct object base; + + char *text; + size_t text_len; + int insertion_point; + int insertion_trail; + PangoLayout *layout; + PangoFontDescription *fontdesc; + double offs_x; + double offs_y; + int furniture; + + PangoContext **pc; +}; + + +static void calculate_size_from_style(struct text_object *o, + double *peright, double *pebottom, + double *pmw, double *pmh) +{ + double max_width, max_height; + double ebottom, eright, mw, mh; + + eright = o->base.parent->parent->slide_width + - o->base.style->margin_right; + ebottom = o->base.parent->parent->slide_height + - o->base.style->margin_bottom; + mw = o->base.parent->parent->slide_width; + mh = o->base.parent->parent->slide_height; + + *peright = eright; *pebottom = ebottom; + *pmw = mw; *pmh = mh; + + max_width = mw - o->base.style->margin_left + - o->base.style->margin_right; + + + /* Use the provided maximum width if it exists and is smaller */ + if ( o->base.style->use_max_width + && (o->base.style->max_width < max_width) ) + { + max_width = o->base.style->max_width; + } + + max_height = mh - o->base.style->margin_top + - o->base.style->margin_bottom; + + pango_layout_set_width(o->layout, max_width*PANGO_SCALE); + pango_layout_set_height(o->layout, max_height*PANGO_SCALE); + pango_layout_set_wrap(o->layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_ellipsize(o->layout, PANGO_ELLIPSIZE_MIDDLE); + + switch ( o->base.style->halign ) { + case J_LEFT : + pango_layout_set_alignment(o->layout, PANGO_ALIGN_LEFT); + break; + case J_RIGHT : + pango_layout_set_alignment(o->layout, PANGO_ALIGN_RIGHT); + break; + case J_CENTER : + pango_layout_set_alignment(o->layout, PANGO_ALIGN_CENTER); + break; + } +} + + +static void calculate_position_from_style(struct text_object *o, + double eright, double ebottom, + double mw, double mh) +{ + double xo, yo; + + xo = o->base.bb_width; yo = o->base.bb_height; + + switch ( o->base.style->halign ) { + case J_LEFT : + o->base.x = o->base.style->margin_left; + break; + case J_RIGHT : + o->base.x = eright - xo; + break; + case J_CENTER : + o->base.x = mw/2.0 - xo/2.0 + o->base.style->offset_x; + break; + } + + /* Correct if centering crashes into opposite margin */ + if ( o->base.style->halign == J_CENTER ) + { + if ( o->base.x < o->base.style->margin_left ) { + o->base.x = o->base.style->margin_left; + } + + if ( o->base.x + xo > eright ) { + o->base.x = eright - xo; + } + } + + switch ( o->base.style->valign ) { + case V_TOP : + o->base.y = o->base.style->margin_top; + break; + case V_BOTTOM : + o->base.y = ebottom - yo; + break; + case V_CENTER : + o->base.y = mh/2.0 - yo/2.0 + o->base.style->offset_y; + break; + } + + if ( o->base.style->valign == V_CENTER ) { + + if ( o->base.y < o->base.style->margin_top ) { + o->base.y = o->base.style->margin_top; + } + + if ( o->base.y+yo + yo > ebottom ) + { + o->base.y = ebottom - yo; + } + } +} + + +static void update_text(struct text_object *o, cairo_t *cr) +{ + PangoRectangle logical; + double eright = 0.0; + double ebottom = 0.0; + double mw = 0.0; + double mh = 0.0; + struct object *ex; + struct text_object *ext; + + switch ( o->base.style->role ) { + + case S_ROLE_SLIDENUMBER: + if ( o->text_len < 32 ) { + free(o->text); + o->text = malloc(32); + o->text_len = 32; + } + snprintf(o->text, o->text_len-1, "Slide %i", + 1+slide_number(o->base.parent->parent, o->base.parent)); + break; + + case S_ROLE_PTITLE: + ex = o->base.parent->roles[S_ROLE_PTITLE_REF]; + ext = (struct text_object *)ex; + if ( (ext != NULL) && (ext->text != NULL) ) { + free(o->text); + o->text = strdup(ext->text); + o->text_len = strlen(o->text); + } + if ( ext == NULL ) { + free(o->text); + o->text = strdup("<presentation title>"); + o->text_len = strlen(o->text); + } + break; + + case S_ROLE_PDATE: + ex = o->base.parent->roles[S_ROLE_PDATE_REF]; + ext = (struct text_object *)ex; + if ( (ext != NULL) && (ext->text != NULL) ) { + free(o->text); + o->text = strdup(ext->text); + o->text_len = strlen(o->text); + } + if ( ext == NULL ) { + free(o->text); + o->text = strdup("<date>"); + o->text_len = strlen(o->text); + } + break; + + default: + break; + + } + + if ( o->layout == NULL ) { + if ( *o->pc != NULL ) { + o->layout = pango_layout_new(*o->pc); + } else { + /* Can't render yet */ + return; + } + } + + if ( cr != NULL ) pango_cairo_update_layout(cr, o->layout); + + o->furniture = o->base.style != o->base.parent->parent->ss->styles[0]; + + pango_layout_set_text(o->layout, o->text, -1); + o->fontdesc = pango_font_description_from_string(o->base.style->font); + pango_layout_set_font_description(o->layout, o->fontdesc); + + if ( o->furniture ) { + + calculate_size_from_style(o, &eright, &ebottom, &mw, &mh); + + pango_layout_get_extents(o->layout, NULL, &logical); + + o->base.bb_width = logical.width / PANGO_SCALE; + o->base.bb_height = logical.height / PANGO_SCALE; + o->offs_x = logical.x / PANGO_SCALE; + o->offs_y = logical.y / PANGO_SCALE; + + } else { + + pango_layout_set_width(o->layout, + o->base.bb_width*PANGO_SCALE); + pango_layout_set_height(o->layout, + o->base.bb_height*PANGO_SCALE); + pango_layout_set_wrap(o->layout, PANGO_WRAP_WORD_CHAR); + pango_layout_set_ellipsize(o->layout, PANGO_ELLIPSIZE_MIDDLE); + + } + + if ( o->furniture ) { + calculate_position_from_style(o, eright, ebottom, mw, mh); + } +} + + +void insert_text(struct object *op, char *t) +{ + struct text_object *o = (struct text_object *)op; + char *tmp; + size_t tlen, olen, offs; + int i; + + assert(o->base.type == OBJ_TEXT); + tlen = strlen(t); + olen = strlen(o->text); + + if ( tlen + olen + 1 > o->text_len ) { + + char *try; + + try = realloc(o->text, o->text_len + tlen + 64); + if ( try == NULL ) return; /* Failed to insert */ + o->text = try; + o->text_len += 64; + o->text_len += tlen; + + } + + tmp = malloc(o->text_len); + if ( tmp == NULL ) return; + + offs = o->insertion_point + o->insertion_trail; + + for ( i=0; i<offs; i++ ) { + tmp[i] = o->text[i]; + } + for ( i=0; i<tlen; i++ ) { + tmp[i+offs] = t[i]; + } + for ( i=0; i<olen-o->insertion_point; i++ ) { + tmp[i+offs+tlen] = o->text[i+offs]; + } + tmp[olen+tlen] = '\0'; + memcpy(o->text, tmp, o->text_len); + free(tmp); + + redraw_slide(op->parent); + o->insertion_point += tlen; + o->base.empty = 0; +} + + +void move_cursor(struct object *op, int dir) +{ + struct text_object *o = (struct text_object *)op; + int new_idx, new_trail; + + pango_layout_move_cursor_visually(o->layout, TRUE, o->insertion_point, + o->insertion_trail, + dir, &new_idx, &new_trail); + + if ( (new_idx >= 0) && (new_idx < G_MAXINT) ) { + o->insertion_point = new_idx; + o->insertion_trail = new_trail; + } +} + + +void move_cursor_left(struct object *op) +{ + move_cursor(op, -1); + redraw_overlay(op->parent->parent); +} + + +void move_cursor_right(struct object *op) +{ + move_cursor(op, +1); + redraw_overlay(op->parent->parent); +} + + +void handle_text_backspace(struct object *op) +{ + int old_idx, new_idx; + struct text_object *o = (struct text_object *)op; + + assert(o->base.type == OBJ_TEXT); + + if ( o->insertion_point == 0 ) return; /* Nothing to delete */ + + old_idx = o->insertion_point + o->insertion_trail; + move_cursor_left(op); + new_idx = o->insertion_point + o->insertion_trail; + + memmove(o->text+new_idx, o->text+old_idx, + o->text_len-new_idx); + + if ( strlen(o->text) == 0 ) o->base.empty = 1; + + redraw_slide(op->parent); +} + + +static void render_text_object(cairo_t *cr, struct object *op) +{ + struct text_object *o = (struct text_object *)op; + GdkColor col; + + if ( o->layout == NULL ) { + return; + } + + update_text(o, cr); + + cairo_move_to(cr, o->base.x - o->offs_x, o->base.y - o->offs_y); + gdk_color_parse(o->base.style->colour, &col); + gdk_cairo_set_source_color(cr, &col); /* FIXME: Honour alpha as well */ + pango_cairo_show_layout(cr, o->layout); +} + + +static void draw_caret(cairo_t *cr, struct text_object *o) +{ + double xposd, yposd, cx; + double clow, chigh; + PangoRectangle pos; + const double t = 1.8; + + assert(o->base.type == OBJ_TEXT); + + pango_layout_get_cursor_pos(o->layout, + o->insertion_point+o->insertion_trail, + &pos, NULL); + + xposd = pos.x/PANGO_SCALE; + cx = o->base.x - o->offs_x + xposd; + yposd = pos.y/PANGO_SCALE; + clow = o->base.y - o->offs_y + yposd; + chigh = clow + (pos.height/PANGO_SCALE); + + cairo_move_to(cr, cx, clow); + cairo_line_to(cr, cx, chigh); + + cairo_move_to(cr, cx-t, clow-t); + cairo_line_to(cr, cx, clow); + cairo_move_to(cr, cx+t, clow-t); + cairo_line_to(cr, cx, clow); + + cairo_move_to(cr, cx-t, chigh+t); + cairo_line_to(cr, cx, chigh); + cairo_move_to(cr, cx+t, chigh+t); + cairo_line_to(cr, cx, chigh); + + cairo_set_source_rgb(cr, 0.86, 0.0, 0.0); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); +} + + +static void update_text_object(struct object *op) +{ + struct text_object *o = (struct text_object *)op; + update_text(o, NULL); +} + + +static void delete_text_object(struct object *op) +{ + struct text_object *o = (struct text_object *)op; + + if ( o->layout != NULL ) g_object_unref(o->layout); + if ( o->fontdesc != NULL ) pango_font_description_free(o->fontdesc); +} + + +static void serialize(struct object *op, struct serializer *ser) +{ + struct text_object *o = (struct text_object *)op; + + serialize_s(ser, "style", op->style->name); + if ( op->style == op->parent->parent->ss->styles[0] ) { + serialize_f(ser, "x", op->x); + serialize_f(ser, "y", op->y); + serialize_f(ser, "w", op->bb_width); + serialize_f(ser, "h", op->bb_height); + } + + serialize_s(ser, "text", o->text); +} + + +static struct object *new_text_object(double x, double y, struct style *sty, + struct text_toolinfo *ti) +{ + struct text_object *new; + + new = calloc(1, sizeof(*new)); + if ( new == NULL ) return NULL; + + /* Base properties */ + new->base.x = x; new->base.y = y; + new->base.bb_width = 10.0; + new->base.bb_height = 40.0; + new->base.type = OBJ_TEXT; + new->base.empty = 1; + new->base.parent = NULL; + new->base.style = sty; + + /* Text-specific stuff */ + new->text = malloc(1); + new->text[0] = '\0'; + new->text_len = 1; + new->insertion_point = 0; + new->insertion_trail = 0; + if ( ti->pc != NULL ) { + new->layout = pango_layout_new(ti->pc); + new->pc = NULL; + } else { + new->layout = NULL; + new->pc = &ti->pc; + } + new->fontdesc = NULL; + + /* Methods for this object */ + new->base.render_object = render_text_object; + new->base.delete_object = delete_text_object; + new->base.update_object = update_text_object; + new->base.serialize = serialize; + + return (struct object *)new; +} + + +static struct object *add_text_object(struct slide *s, double x, double y, + struct style *sty, + struct text_toolinfo *ti) +{ + struct object *o; + + o = new_text_object(x, y, sty, ti); + + if ( add_object_to_slide(s, o) ) { + delete_object(o); + return NULL; + } + + redraw_slide(o->parent); + + return o; +} + + +static void calculate_box_size(struct object *o, double x, double y, + struct text_toolinfo *ti) +{ + double ddx, ddy; + + ddx = x - ti->drag_initial_x; + ddy = y - ti->drag_initial_y; + + switch ( ti->drag_corner ) { + + case CORNER_BR : + ti->box_x = o->x; + ti->box_y = o->y; + ti->box_width = o->bb_width + ddx; + ti->box_height = o->bb_height + ddy; + break; + + case CORNER_BL : + ti->box_x = o->x + ddx; + ti->box_y = o->y; + ti->box_width = o->bb_width - ddx; + ti->box_height = o->bb_height + ddy; + break; + + case CORNER_TL : + ti->box_x = o->x + ddx; + ti->box_y = o->y + ddy; + ti->box_width = o->bb_width - ddx; + ti->box_height = o->bb_height - ddy; + break; + + case CORNER_TR : + ti->box_x = o->x; + ti->box_y = o->y + ddy; + ti->box_width = o->bb_width + ddx; + ti->box_height = o->bb_height - ddy; + break; + + case CORNER_NONE : + break; + + } + + if ( ti->box_width < 20.0 ) ti->box_width = 20.0; + if ( ti->box_height < 20.0 ) ti->box_height = 20.0; +} + + +static void click_select(struct presentation *p, struct toolinfo *tip, + double x, double y, GdkEventButton *event, + enum drag_status *drag_status, + enum drag_reason *drag_reason) +{ + int xp, yp; + double xo, yo; + gboolean v; + enum corner c; + struct text_toolinfo *ti = (struct text_toolinfo *)tip; + struct text_object *o = (struct text_object *)p->editing_object; + int idx, trail; + + assert(o->base.type == OBJ_TEXT); + + xo = x - o->base.x; yo = y - o->base.y; + + /* Within the resizing region? */ + c = which_corner(x, y, &o->base); + if ( (c != CORNER_NONE) && !o->furniture ) + { + ti->drag_reason = TEXT_DRAG_REASON_RESIZE; + ti->drag_corner = c; + + ti->drag_initial_x = x; + ti->drag_initial_y = y; + + calculate_box_size((struct object *)o, x, y, ti); + + /* Tell the MCP what we did, and return */ + *drag_status = DRAG_STATUS_DRAGGING; + *drag_reason = DRAG_REASON_TOOL; + return; + } + + xp = (xo + o->offs_x)*PANGO_SCALE; + yp = (yo + o->offs_y)*PANGO_SCALE; + + v = pango_layout_xy_to_index(o->layout, xp, yp, &idx, &trail); + + o->insertion_point = idx; + o->insertion_trail = trail; +} + + +static void drag(struct toolinfo *tip, struct presentation *p, + struct object *o, double x, double y) +{ + struct text_toolinfo *ti = (struct text_toolinfo *)tip; + + if ( ti->drag_reason == TEXT_DRAG_REASON_RESIZE ) { + + calculate_box_size(o, x, y, ti); + redraw_overlay(p); + + } +} + + +static void end_drag(struct toolinfo *tip, struct presentation *p, + struct object *o, double x, double y) +{ + struct text_toolinfo *ti = (struct text_toolinfo *)tip; + + calculate_box_size((struct object *)o, x, y, ti); + + o->x = ti->box_x; + o->y = ti->box_y; + o->bb_width = ti->box_width; + o->bb_height = ti->box_height; + update_text((struct text_object *)o, NULL); + redraw_slide(o->parent); + + ti->drag_reason = TEXT_DRAG_REASON_NONE; +} + + +static void update_subsequent_refs(struct presentation *p, int start, + enum object_role role, struct object *o) +{ + int i; + + for ( i=start; i<p->num_slides; i++ ) + { + struct slide *s = p->slides[i]; + s->roles[role] = o; + } +} + + +static void create_default(struct presentation *p, struct style *sty, + struct slide *s, struct toolinfo *tip) +{ + struct object *n; + struct text_object *o; + struct text_toolinfo *ti = (struct text_toolinfo *)tip; + + n = add_text_object(s, 0.0, 0.0, sty, ti); + o = (struct text_object *)n; + o->furniture = 1; + n->empty = 0; + + if ( sty->role != S_ROLE_NONE ) { + s->roles[sty->role] = n; + } + + switch ( sty->role ) + { + case S_ROLE_SLIDENUMBER: + case S_ROLE_PTITLE: + case S_ROLE_PAUTHOR: + case S_ROLE_PDATE: + break; + + default: + p->editing_object = n; + break; + } + + /* If we just created a new presentation title (etc), set this object as + * the presentation title reference for all subsequent slides. + * Any relevant objects will get updated when the current object gets + * deselected. */ + if ( sty->role == S_ROLE_PTITLE_REF ) { + update_subsequent_refs(p, slide_number(p, s)+1, + S_ROLE_PTITLE_REF, n); + } + if ( sty->role == S_ROLE_PAUTHOR_REF ) { + update_subsequent_refs(p, slide_number(p, s)+1, + S_ROLE_PAUTHOR_REF, n); + } + if ( sty->role == S_ROLE_PDATE_REF ) { + update_subsequent_refs(p, slide_number(p, s)+1, + S_ROLE_PDATE_REF, n); + } + + redraw_slide(((struct object *)o)->parent); +} + + +static void create_region(struct toolinfo *tip, struct presentation *p, + double x1, double y1, double x2, double y2) +{ + struct object *n; + struct text_toolinfo *ti = (struct text_toolinfo *)tip; + struct text_object *o; + + n = add_text_object(p->cur_edit_slide, 0.0, 0.0, p->ss->styles[0], ti); + n->x = x1<x2 ? x1 : x2; + n->y = y1<y2 ? y1 : y2; + n->bb_width = fabs(x1-x2); + n->bb_height = fabs(y1-y2); + + o = (struct text_object *)n; + o->furniture = 0; + + redraw_slide(((struct object *)o)->parent); + p->editing_object = n; +} + + +static void select_object(struct object *o, struct toolinfo *tip) +{ + /* Do nothing */ +} + + +static void update_subsequent(struct presentation *p, int start, + enum object_role role, struct object *o) +{ + int i; + + for ( i=start; i<p->num_slides; i++ ) { + + struct slide *s = p->slides[i]; + + if ( s->roles[role] != NULL ) { + s->roles[role]->update_object(s->roles[role]); + } + } +} + + +static int deselect_object(struct object *o, struct toolinfo *tip) +{ + struct slide *s; + struct presentation *p; + + if ( o == NULL ) return 0; + + s = o->parent; + p = s->parent; + + if ( o->empty ) { + delete_object(o); + return 1; + } + + if ( o->style->role == S_ROLE_PTITLE_REF ) { + update_subsequent(p, slide_number(p, s)+1, S_ROLE_PTITLE, o); + } + if ( o->style->role == S_ROLE_PAUTHOR_REF ) { + update_subsequent(p, slide_number(p, s)+1, S_ROLE_PAUTHOR, o); + } + if ( o->style->role == S_ROLE_PDATE_REF ) { + update_subsequent(p, slide_number(p, s)+1, S_ROLE_PDATE, o); + } + + return 0; +} + + +static void draw_overlay(struct toolinfo *tip, cairo_t *cr, struct object *n) +{ + struct text_toolinfo *ti = (struct text_toolinfo *)tip; + struct text_object *o = (struct text_object *)n; + + if ( n != NULL ) { + + draw_editing_box(cr, n->x, n->y, n->bb_width, n->bb_height); + + if ( !o->furniture ) { + + /* Draw resize handles */ + draw_resize_handle(cr, n->x, n->y+n->bb_height-20.0); + draw_resize_handle(cr, n->x+n->bb_width-20.0, n->y); + draw_resize_handle(cr, n->x, n->y); + draw_resize_handle(cr, n->x+n->bb_width-20.0, + n->y+n->bb_height-20.0); + + } + + draw_caret(cr, o); + } + + if ( ti->drag_reason == TEXT_DRAG_REASON_RESIZE ) { + draw_rubberband_box(cr, ti->box_x, ti->box_y, + ti->box_width, ti->box_height); + } + +} + + +static void key_pressed(struct object *o, guint keyval, struct toolinfo *tip) +{ + if ( o == NULL ) return; + + switch ( keyval ) { + + case GDK_KEY_BackSpace : + handle_text_backspace(o); + break; + + case GDK_KEY_Left : + move_cursor_left(o); + break; + + case GDK_KEY_Right : + move_cursor_right(o); + break; + + } +} + + +static void im_commit(struct object *o, gchar *str, struct toolinfo *tip) +{ + insert_text(o, str); +} + + +static int valid_object(struct object *o) +{ + if ( o->type == OBJ_TEXT ) return 1; + return 0; +} + + +static void realise(struct toolinfo *ti, GtkWidget *w, struct presentation *p) +{ + struct text_toolinfo *tip = (struct text_toolinfo *)ti; + tip->pc = gtk_widget_get_pango_context(w); + + ti->tbox = gtk_label_new("Text tool"); + g_object_ref(ti->tbox); + gtk_widget_show(ti->tbox); +} + + +static struct object *deserialize(struct presentation *p, struct ds_node *root, + struct toolinfo *tip) +{ + struct object *o; + struct text_object *to; + char *style; + char *text; + struct style *sty; + double x, y, w, h; + struct text_toolinfo *ti = (struct text_toolinfo *)tip; + + if ( get_field_s(root, "style", &style) ) { + fprintf(stderr, "Couldn't find style for object '%s'\n", + root->key); + return NULL; + } + + sty = find_style(p->ss, style); + if ( sty == NULL ) { + fprintf(stderr, "Style '%s' not found in style sheet.\n", + style); + free(style); + return NULL; + } + free(style); + + if ( sty == p->ss->styles[0] ) { + + if ( get_field_f(root, "x", &x) ) { + fprintf(stderr, + "Couldn't find x position for object '%s'\n", + root->key); + return NULL; + } + if ( get_field_f(root, "y", &y) ) { + fprintf(stderr, + "Couldn't find y position for object '%s'\n", + root->key); + return NULL; + } + if ( get_field_f(root, "w", &w) ) { + fprintf(stderr, + "Couldn't find width for object '%s'\n", + root->key); + return NULL; + } + if ( get_field_f(root, "h", &h) ) { + fprintf(stderr, + "Couldn't find height for object '%s'\n", + root->key); + return NULL; + } + + } else { + + /* Furniture */ + x = 0.0; + y = 0.0; + w = 0.0; + h = 0.0; + + } + + o = new_text_object(x, y, sty, ti); + o->bb_width = w; + o->bb_height = h; + + /* Apply the correct text */ + if ( get_field_s(root, "text", &text) ) { + fprintf(stderr, "Couldn't find text for object '%s'\n", + root->key); + return NULL; + } + to = (struct text_object *)o; + free(to->text); + to->text = text; + to->text_len = strlen(text); + o->empty = 0; + + return o; +} + + +struct toolinfo *initialise_text_tool(GtkWidget *w) +{ + struct text_toolinfo *ti; + + ti = malloc(sizeof(*ti)); + + ti->base.click_select = click_select; + ti->base.create_default = create_default; + ti->base.select = select_object; + ti->base.deselect = deselect_object; + ti->base.drag = drag; + ti->base.end_drag = end_drag; + ti->base.create_region = create_region; + ti->base.draw_editing_overlay = draw_overlay; + ti->base.key_pressed = key_pressed; + ti->base.im_commit = im_commit; + ti->base.valid_object = valid_object; + ti->base.realise = realise; + ti->base.deserialize = deserialize; + + ti->pc = NULL; + + ti->drag_reason = DRAG_REASON_NONE; + + return (struct toolinfo *)ti; +} |