From 79f3fdbb0b6e379c90eb3783d200e6d4249f4a3b Mon Sep 17 00:00:00 2001 From: Thomas White Date: Mon, 21 Dec 2015 15:54:51 +0100 Subject: Multiple segments in a box --- src/render.c | 9 ++- src/sc_editor.c | 39 +++++++++++-- src/shape.c | 178 ++++++++++++++++++++++++++++++-------------------------- src/wrap.c | 86 ++++++++++++++++++++++----- src/wrap.h | 21 ++++++- 5 files changed, 225 insertions(+), 108 deletions(-) diff --git a/src/render.c b/src/render.c index a605ad2..e7d24f6 100644 --- a/src/render.c +++ b/src/render.c @@ -46,15 +46,20 @@ static void render_glyph_box(cairo_t *cr, struct wrap_box *box) { + int i; + cairo_new_path(cr); cairo_move_to(cr, 0.0, 0.0); - if ( box->glyphs == NULL ) { + if ( box->segs == NULL ) { fprintf(stderr, "Box %p has NULL pointer.\n", box); return; } cairo_set_source_rgba(cr, box->col[0], box->col[1], box->col[2], box->col[3]); - pango_cairo_show_glyph_string(cr, box->font, box->glyphs); + for ( i=0; in_segs; i++ ) { + pango_cairo_show_glyph_string(cr, box->font, + box->segs[i].glyphs); + } } diff --git a/src/sc_editor.c b/src/sc_editor.c index ed554c4..ef48e8d 100644 --- a/src/sc_editor.c +++ b/src/sc_editor.c @@ -420,7 +420,7 @@ void cur_box_diag(SCEditor *e) sbx = e->cursor_box; sps = e->cursor_pos; - struct wrap_box *sbox = &e->cursor_frame->lines[sln].boxes[sbx]; + struct wrap_box *sbox = &fr->lines[sln].boxes[sbx]; printf("line/box/pos: [%i of %i]/[%i of %i]/[%i of %i]\n", sln, fr->n_lines, @@ -430,6 +430,17 @@ void cur_box_diag(SCEditor *e) if ( sbox->type == WRAP_BOX_NOTHING ) { printf("Warning: in a nothing box!\n"); } + + struct wrap_line *ln = &fr->lines[sln]; + int i; + for ( i=0; in_boxes; i++ ) { + char pp = '['; + char pq = ']'; + struct wrap_box *bx = &ln->boxes[i]; + if ( i == sbx ) { pp = '<'; pq = '>'; } + printf("%c%i %i %i%c", pp, bx->offs_char, bx->len_chars, + bx->n_segs, pq); + } } @@ -714,7 +725,7 @@ static void fixup_cursor(SCEditor *e) /* We find ourselves in a box which doesn't exist */ - if ( e->cursor_line > fr->n_lines-1 ) { + if ( e->cursor_line < fr->n_lines-1 ) { /* This isn't the last line, so go to the first box of * the next line */ e->cursor_line++; @@ -728,6 +739,7 @@ static void fixup_cursor(SCEditor *e) } } + assert(e->cursor_box < sline->n_boxes); sbox = &sline->boxes[e->cursor_box]; if ( e->cursor_pos > sbox->len_chars ) { @@ -779,10 +791,15 @@ void insert_scblock(SCBlock *scblock, SCEditor *e) static void update_local(SCEditor *e, struct frame *fr, int line, int bn) { struct wrap_box *box = &fr->lines[line].boxes[bn]; - /* Shape the box again */ + + /* Shape the box again FIXME: Number of segments could change */ shape_box(box->cf->cf); - box->glyphs = box->cf->cf->glyphs; - box->cf->glyphs = box->cf->cf->glyphs; + + /* Update the segments */ + box->segs = box->cf->cf->segs; + box->n_segs = box->cf->cf->n_segs; + box->cf->segs = box->cf->cf->segs; + box->cf->n_segs = box->cf->cf->n_segs; /* Wrap the paragraph again */ wrap_contents(fr); /* FIXME: Only the current paragraph */ @@ -814,7 +831,7 @@ static void shift_box_offsets(struct frame *fr, struct wrap_box *box, int n) static void insert_text(char *t, SCEditor *e) { - int sln, sbx, sps; + int sln, sbx, sps, sseg; struct wrap_box *sbox; struct frame *fr = e->cursor_frame; const char *text; @@ -822,6 +839,7 @@ static void insert_text(char *t, SCEditor *e) int len_chars; PangoLogAttr *log_attrs; int offs; + int err = 0; if ( fr == NULL ) return; @@ -832,9 +850,16 @@ static void insert_text(char *t, SCEditor *e) sbx = e->cursor_box; sps = e->cursor_pos; sbox = &e->cursor_frame->lines[sln].boxes[sbx]; + sseg = which_segment(sbox, sps, &err); + if ( err ) return; cur_box_diag(e); printf("sps=%i, offs_char=%i\n", sps, sbox->offs_char); + if ( sbox->type == WRAP_BOX_NOTHING ) { + printf("Editing a nothing box!\n"); + return; + } + sc_insert_text(sbox->scblock, sps+sbox->offs_char, t); text = sc_block_contents(sbox->scblock); @@ -869,6 +894,8 @@ static void insert_text(char *t, SCEditor *e) free(log_attrs); + sbox->segs[sseg].len_chars += 1; + /* Update the length of the box in the unwrapped and un-paragraph-split * string of wrap boxes */ sbox->cf->cf->len_chars += 1; diff --git a/src/shape.c b/src/shape.c index 1983ae6..3fddaa6 100644 --- a/src/shape.c +++ b/src/shape.c @@ -35,18 +35,7 @@ #include "shape.h" -struct box_adding_stuff -{ - struct wrap_line *line; - SCInterpreter *scin; - int editable; - enum wrap_box_space space; - SCBlock *bl; - size_t offs; -}; - - -void shape_box(struct wrap_box *box) +static void shape_segment(struct wrap_box *box, struct text_seg *seg) { PangoRectangle rect; const char *tp; @@ -57,67 +46,48 @@ void shape_box(struct wrap_box *box) ep = g_utf8_offset_to_pointer(sc_block_contents(box->scblock), box->offs_char+box->len_chars); - if ( box->glyphs != NULL ) { - pango_glyph_string_free(box->glyphs); + if ( seg->glyphs != NULL ) { + pango_glyph_string_free(seg->glyphs); } - box->glyphs = pango_glyph_string_new(); - pango_shape(tp, ep-tp, &box->analysis, box->glyphs); + seg->glyphs = pango_glyph_string_new(); + pango_shape(tp, ep-tp, &seg->analysis, seg->glyphs); - pango_glyph_string_extents(box->glyphs, box->font, NULL, &rect); + pango_glyph_string_extents(seg->glyphs, box->font, NULL, &rect); - box->width = rect.width; - if ( rect.height > box->height ) { - box->height = rect.height; - } - if ( PANGO_ASCENT(rect) > box->ascent ) { - box->ascent = PANGO_ASCENT(rect); - } + seg->width = rect.width; + seg->height = rect.height; + seg->ascent = PANGO_ASCENT(rect); } -static void add_wrap_box(gpointer vi, gpointer vb) +static void calc_box_geometry(struct wrap_box *box) { - struct wrap_box *box; - double *col; - PangoItem *item = vi; - struct box_adding_stuff *bas = vb; - size_t offs_bytes; - const char *tp; + int i; - if ( bas->line->n_boxes == bas->line->max_boxes ) { - bas->line->max_boxes += 32; - alloc_boxes(bas->line); - if ( bas->line->n_boxes == bas->line->max_boxes ) return; - } - box = &bas->line->boxes[bas->line->n_boxes]; + if ( box->type != WRAP_BOX_PANGO ) return; - box->type = WRAP_BOX_PANGO; - box->space = bas->space; - box->font = sc_interp_get_font(bas->scin); box->width = 0; - box->editable = bas->editable; - box->ascent = sc_interp_get_ascent(bas->scin); - box->height = sc_interp_get_height(bas->scin); - box->cf = NULL; + box->height = 0; + box->ascent = 0; + + for ( i=0; in_segs; i++ ) { + struct text_seg *seg = &box->segs[i]; + box->width += seg->width; + if ( seg->height > box->height ) box->height = seg->height; + if ( seg->ascent > box->ascent ) box->ascent = seg->ascent; + } +} - /* Link to the actual text */ - tp = sc_block_contents(bas->bl); - offs_bytes = item->offset + bas->offs; - box->scblock = bas->bl; - box->offs_char = g_utf8_pointer_to_offset(tp, tp+offs_bytes); - box->len_chars = g_utf8_strlen(tp+offs_bytes, item->length); - col = sc_interp_get_fgcol(bas->scin); - box->col[0] = col[0]; /* Red */ - box->col[1] = col[1]; /* Green */ - box->col[2] = col[2]; /* Blue */ - box->col[3] = col[3]; /* Alpha */ - box->glyphs = NULL; - box->analysis = item->analysis; +void shape_box(struct wrap_box *box) +{ + int i; - bas->line->n_boxes++; + for ( i=0; in_segs; i++ ) { + shape_segment(box, &box->segs[i]); + } - shape_box(box); + calc_box_geometry(box); } @@ -197,41 +167,85 @@ static UNUSED void debug_log_attrs(size_t len_chars, const char *text, } +static void add_seg(gpointer vi, gpointer vb) +{ + PangoItem *item = vi; + struct wrap_box *box = vb; + + box->segs[box->n_segs].analysis = item->analysis; + box->segs[box->n_segs].glyphs = NULL; + box->segs[box->n_segs].offs_char = item->offset; + box->segs[box->n_segs].len_chars = item->num_chars; + shape_segment(box, &box->segs[box->n_segs++]); +} + + /* Add "text", followed by a space of type "space", to "line" */ -static int add_wrap_boxes(struct wrap_line *line, - enum wrap_box_space space, PangoContext *pc, - SCInterpreter *scin, SCBlock *bl, size_t offs, - size_t len, int editable) +static int add_text_box(struct wrap_line *line, + enum wrap_box_space space, PangoContext *pc, + SCInterpreter *scin, SCBlock *bl, size_t offs, + size_t len, int editable) { GList *pango_items; + struct wrap_box *box; PangoAttrList *attrs; PangoAttribute *attr; - struct box_adding_stuff bas; - - //printf("adding '%s'\n", swizzle(text+offs, len)); + const char *tp; + double *col; + int nseg; while ( len==0 ) { add_nothing_box(line, bl, editable, space, scin, offs); return 0; } + /* Create the box */ + if ( line->n_boxes == line->max_boxes ) { + line->max_boxes += 32; + alloc_boxes(line); + if ( line->n_boxes == line->max_boxes ) return 1; + } + box = &line->boxes[line->n_boxes]; + + box->type = WRAP_BOX_PANGO; + box->space = space; + box->font = sc_interp_get_font(scin); + box->width = 0; + box->editable = editable; + box->ascent = sc_interp_get_ascent(scin); + box->height = sc_interp_get_height(scin); + box->cf = NULL; + + /* Link to the actual text */ + box->scblock = bl; + tp = sc_block_contents(bl); + box->offs_char = g_utf8_pointer_to_offset(tp, tp+offs); + box->len_chars = g_utf8_strlen(tp+offs, len); + + col = sc_interp_get_fgcol(scin); + box->col[0] = col[0]; /* Red */ + box->col[1] = col[1]; /* Green */ + box->col[2] = col[2]; /* Blue */ + box->col[3] = col[3]; /* Alpha */ + attrs = pango_attr_list_new(); attr = pango_attr_font_desc_new(sc_interp_get_fontdesc(scin)); pango_attr_list_insert_before(attrs, attr); pango_items = pango_itemize(pc, sc_block_contents(bl)+offs, 0, len, attrs, NULL); + nseg = g_list_length(pango_items); + box->segs = malloc(nseg * sizeof(struct text_seg)); + if ( box->segs == NULL ) return 1; - bas.line = line; - bas.scin = scin; - bas.editable = editable; - bas.space = space; - bas.bl = bl; - bas.offs = offs; - - g_list_foreach(pango_items, add_wrap_box, &bas); + box->n_segs = 0; + g_list_foreach(pango_items, add_seg, box); g_list_free(pango_items); pango_attr_list_unref(attrs); + calc_box_geometry(box); + + line->n_boxes++; + return 0; } @@ -302,8 +316,8 @@ int split_words(struct wrap_line *boxes, PangoContext *pc, SCBlock *bl, len_chars = g_utf8_strlen(text, -1); if ( len_chars == 0 ) { - add_wrap_boxes(boxes, WRAP_SPACE_NONE, pc, scin, bl, - 0, 0, editable); + add_text_box(boxes, WRAP_SPACE_NONE, pc, scin, bl, + 0, 0, editable); return 1; } @@ -345,8 +359,8 @@ int split_words(struct wrap_line *boxes, PangoContext *pc, SCBlock *bl, type = WRAP_SPACE_NONE; } - if ( add_wrap_boxes(boxes, type, pc, scin, bl, - start, len, editable) ) { + if ( add_text_box(boxes, type, pc, scin, bl, + start, len, editable) ) { fprintf(stderr, "Failed to add wrap box.\n"); } start = offs; @@ -364,13 +378,13 @@ int split_words(struct wrap_line *boxes, PangoContext *pc, SCBlock *bl, if ( (text[start+l-1] == '\n') ) { /* There is a newline at the end of the SC */ - add_wrap_boxes(boxes, WRAP_SPACE_EOP, pc, scin, bl, - start, l-1, editable); + add_text_box(boxes, WRAP_SPACE_EOP, pc, scin, bl, + start, l-1, editable); } else { - add_wrap_boxes(boxes, WRAP_SPACE_NONE, pc, scin, bl, - start, l, editable); + add_text_box(boxes, WRAP_SPACE_NONE, pc, scin, bl, + start, l, editable); } diff --git a/src/wrap.c b/src/wrap.c index f06babc..c4ce531 100644 --- a/src/wrap.c +++ b/src/wrap.c @@ -120,14 +120,67 @@ static struct wrap_line *get_cursor_line(struct frame *fr, size_t pos, } #endif -void get_cursor_pos(struct wrap_box *box, int pos, - double *xposd, double *yposd, double *line_height) + +int which_segment(struct wrap_box *box, int pos, int *err) { - int p; - const char *block_text; - const char *box_text; + int i = 0; + int ch = 0; + + do { + if ( ch + box->segs[i].len_chars >= pos ) break; + ch += box->segs[i++].len_chars; + } while ( i < box->n_segs ); + + if ( i == box->n_segs ) { + fprintf(stderr, "Position not found in box!\n"); + *err = 1; + return 0; + } + + *err = 0; + return i; +} + + +/* Return the horizontal position of "pos" within "box", in cairo units */ +static double text_box_index_to_x(struct wrap_box *box, int pos) +{ + double x = 0.0; + int nseg; + struct text_seg *seg; + const char *seg_text; const char *ep; + int p; + int i; + int err; + + nseg = which_segment(box, pos, &err); + if ( err ) return 0.0; + + for ( i=0; isegs[i].glyphs, + box->font, NULL, &rect); + x += rect.width; + } + + /* We are in "seg" inside "box" */ + seg = &box->segs[nseg]; + seg_text = g_utf8_offset_to_pointer(sc_block_contents(box->scblock), + box->offs_char + seg->offs_char); + ep = g_utf8_offset_to_pointer(seg_text, seg->len_chars); + + /* FIXME: pos should be in bytes, not chars */ + pango_glyph_string_index_to_x(seg->glyphs, (char *)seg_text, + ep - seg_text, &seg->analysis, + pos, FALSE, &p); + return pango_units_to_double(x+p); +} + +void get_cursor_pos(struct wrap_box *box, int pos, + double *xposd, double *yposd, double *line_height) +{ *xposd = 0.0; *yposd = 0.0; *line_height = 20.0; @@ -144,13 +197,7 @@ void get_cursor_pos(struct wrap_box *box, int pos, switch ( box->type ) { case WRAP_BOX_PANGO : - block_text = sc_block_contents(box->scblock); - box_text = g_utf8_offset_to_pointer(block_text, box->offs_char); - ep = g_utf8_offset_to_pointer(box_text, box->len_chars); - pango_glyph_string_index_to_x(box->glyphs, (char *)box_text, - ep - box_text, &box->analysis, - pos, FALSE, &p); - *xposd += pango_units_to_double(p); + *xposd += text_box_index_to_x(box, pos); break; case WRAP_BOX_IMAGE : @@ -277,10 +324,11 @@ void find_cursor(struct frame *fr, double xposd, double yposd, b->offs_char); printf("box text '%s'\n", box_text); /* cast because this function is not const-clean */ - pango_glyph_string_x_to_index(b->glyphs, + /* FIXME: Assumes one segment per box! */ + pango_glyph_string_x_to_index(b->segs[0].glyphs, (char *)box_text, strlen(box_text), - &b->analysis, + &b->segs[0].analysis, x_pos_i, &idx, &trail); offs = idx + trail; /* FIXME: Bug in Pango? */ @@ -310,12 +358,15 @@ static void calc_line_geometry(struct wrap_line *line) line->height = 0; for ( i=0; in_boxes; i++ ) { + struct wrap_box *box = &line->boxes[i]; + line->width += box->width; if ( box->space == WRAP_SPACE_EOP ) box->sp = 0.0; line->width += box->sp; if ( box->height > line->height ) line->height = box->height; if ( box->ascent > line->ascent ) line->ascent = box->ascent; + } line->height *= 1.07; @@ -785,13 +836,16 @@ static void first_fit(struct wrap_line *boxes, double line_length, void wrap_line_free(struct wrap_line *l) { - int i; + int i, j; for ( i=0; in_boxes; i++ ) { switch ( l->boxes[i].type ) { case WRAP_BOX_PANGO : - pango_glyph_string_free(l->boxes[i].glyphs); + for ( j=0; jboxes[i].n_segs; j++ ) { + pango_glyph_string_free(l->boxes[i].segs[j].glyphs); + } + free(l->boxes[i].segs); break; case WRAP_BOX_IMAGE : diff --git a/src/wrap.h b/src/wrap.h index bf091ee..c293ea9 100644 --- a/src/wrap.h +++ b/src/wrap.h @@ -55,6 +55,22 @@ enum wrap_box_space }; +struct text_seg +{ + PangoGlyphString *glyphs; + PangoAnalysis analysis; + + /* Offset of this text segment into the wrap box */ + int offs_char; + int len_chars; + + /* Pango units */ + int width; + int height; + int ascent; +}; + + /* A wrap box is a run of content - could be text, an image or so one - that is * one logical unit as far as Colloquium is concerned. It might consist of * multiple units, for example, in Pango's mind. */ @@ -76,11 +92,11 @@ struct wrap_box double sp; /* Calculated space (Pango units) after box */ /* For type == WRAP_BOX_PANGO */ - PangoGlyphString *glyphs; PangoFont *font; double col[4]; /* rgba colour */ - PangoAnalysis analysis; int len_chars; + int n_segs; + struct text_seg *segs; /* For type == WRAP_BOX_IMAGE */ char *filename; @@ -124,5 +140,6 @@ extern void show_boxes(struct wrap_line *boxes); extern double total_height(struct frame *fr); extern int insert_box(struct wrap_line *l, int pos); +extern int which_segment(struct wrap_box *box, int pos, int *err); #endif /* WRAP_H */ -- cgit v1.2.3