aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorThomas White <taw@bitwiz.org.uk>2015-12-21 15:54:51 +0100
committerThomas White <taw@bitwiz.org.uk>2015-12-21 15:54:51 +0100
commit79f3fdbb0b6e379c90eb3783d200e6d4249f4a3b (patch)
tree6039af892c014fa46334c23a0031659746b5bddd /src
parent3cbb8d0124e839136795ae8dcf475a5fbe86c093 (diff)
Multiple segments in a box
Diffstat (limited to 'src')
-rw-r--r--src/render.c9
-rw-r--r--src/sc_editor.c39
-rw-r--r--src/shape.c178
-rw-r--r--src/wrap.c86
-rw-r--r--src/wrap.h21
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; i<box->n_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; i<ln->n_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; i<box->n_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; i<box->n_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; i<nseg; i++ ) {
+ PangoRectangle rect;
+ pango_glyph_string_extents(box->segs[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; i<line->n_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; i<l->n_boxes; i++ ) {
switch ( l->boxes[i].type ) {
case WRAP_BOX_PANGO :
- pango_glyph_string_free(l->boxes[i].glyphs);
+ for ( j=0; j<l->boxes[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 */