Better handling of paragraph ends in which_run
[colloquium.git] / src / frame.c
1 /*
2  * frame.c
3  *
4  * Copyright © 2013-2018 Thomas White <taw@bitwiz.org.uk>
5  *
6  * This file is part of Colloquium.
7  *
8  * Colloquium 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 <assert.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <math.h>
32 #include <gdk-pixbuf/gdk-pixbuf.h>
33
34 #include "sc_parse.h"
35 #include "frame.h"
36 #include "imagestore.h"
37
38 struct text_run
39 {
40         SCBlock              *scblock;   /* If macro, this is \macro */
41         SCBlock              *rscblock;  /* The block with the actual text */
42         PangoFontDescription *fontdesc;
43         double                col[4];
44 };
45
46 struct _paragraph
47 {
48         enum para_type   type;
49         double           height;
50         float            space[4];
51         SCBlock         *newline_at_end;
52
53         /* For PARA_TYPE_TEXT */
54         int              n_runs;
55         struct text_run *runs;
56         int              open;
57         PangoLayout     *layout;
58
59         /* For anything other than PARA_TYPE_TEXT
60          * (for text paragraphs, these things are in the runs) */
61         SCBlock         *scblock;
62
63         /* For PARA_TYPE_IMAGE */
64         char            *filename;
65         double           image_w;
66         double           image_h;
67         int              image_real_w;
68         int              image_real_h;
69
70         /* For PARA_TYPE_CALLBACK */
71         double                cb_w;
72         double                cb_h;
73         SCCallbackDrawFunc    draw_func;
74         SCCallbackClickFunc   click_func;
75         void                 *bvp;
76         void                 *vp;
77 };
78
79
80 PangoLayout *paragraph_layout(Paragraph *para)
81 {
82         return para->layout;
83 }
84
85
86 double paragraph_height(Paragraph *para)
87 {
88         return para->height;
89 }
90
91
92 static int alloc_ro(struct frame *fr)
93 {
94         struct frame **new_ro;
95
96         new_ro = realloc(fr->children,
97                          fr->max_children*sizeof(struct frame *));
98         if ( new_ro == NULL ) return 1;
99
100         fr->children = new_ro;
101
102         return 0;
103 }
104
105
106 struct frame *frame_new()
107 {
108         struct frame *n;
109
110         n = calloc(1, sizeof(struct frame));
111         if ( n == NULL ) return NULL;
112
113         n->children = NULL;
114         n->max_children = 32;
115         if ( alloc_ro(n) ) {
116                 fprintf(stderr, "Couldn't allocate children\n");
117                 free(n);
118                 return NULL;
119         }
120         n->num_children = 0;
121
122         n->scblocks = NULL;
123         n->n_paras = 0;
124         n->paras = NULL;
125
126         return n;
127 }
128
129
130 static void free_paragraph(Paragraph *para)
131 {
132         int i;
133
134         for ( i=0; i<para->n_runs; i++ ) {
135                 pango_font_description_free(para->runs[i].fontdesc);
136         }
137         free(para->runs);
138         if ( para->layout != NULL ) g_object_unref(para->layout);
139         free(para);
140 }
141
142
143 void frame_free(struct frame *fr)
144 {
145         int i;
146
147         if ( fr == NULL ) return;
148
149         /* Free paragraphs */
150         if ( fr->paras != NULL ) {
151                 for ( i=0; i<fr->n_paras; i++ ) {
152                         free_paragraph(fr->paras[i]);
153                 }
154                 free(fr->paras);
155         }
156
157         /* Free all children */
158         for ( i=0; i<fr->num_children; i++ ) {
159                 frame_free(fr->children[i]);
160         }
161         free(fr->children);
162
163         free(fr);
164 }
165
166
167 struct frame *add_subframe(struct frame *fr)
168 {
169         struct frame *n;
170
171         n = frame_new();
172         if ( n == NULL ) return NULL;
173
174         if ( fr->num_children == fr->max_children ) {
175                 fr->max_children += 32;
176                 if ( alloc_ro(fr) ) return NULL;
177         }
178
179         fr->children[fr->num_children++] = n;
180
181         return n;
182 }
183
184
185 void show_hierarchy(struct frame *fr, const char *t)
186 {
187         int i;
188         char tn[1024];
189
190         strcpy(tn, t);
191         strcat(tn, "      ");
192
193         printf("%s%p (%.2f x %.2f)\n", t, fr, fr->w, fr->h);
194
195         for ( i=0; i<fr->num_children; i++ ) {
196                 show_hierarchy(fr->children[i], tn);
197         }
198
199 }
200
201
202 static struct frame *find_parent(struct frame *fr, struct frame *search)
203 {
204         int i;
205
206         for ( i=0; i<fr->num_children; i++ ) {
207                 if ( fr->children[i] == search ) {
208                         return fr;
209                 }
210         }
211
212         for ( i=0; i<fr->num_children; i++ ) {
213                 struct frame *tt;
214                 tt = find_parent(fr->children[i], search);
215                 if ( tt != NULL ) return tt;
216         }
217
218         return NULL;
219 }
220
221
222 void delete_subframe(struct frame *top, struct frame *fr)
223 {
224         struct frame *parent;
225         int i, idx, found;
226
227         parent = find_parent(top, fr);
228         if ( parent == NULL ) {
229                 fprintf(stderr, "Couldn't find parent when deleting frame.\n");
230                 return;
231         }
232
233         found = 0;
234         for ( i=0; i<parent->num_children; i++ ) {
235                 if ( parent->children[i] == fr ) {
236                         idx = i;
237                         found = 1;
238                         break;
239                 }
240         }
241
242         if ( !found ) {
243                 fprintf(stderr, "Couldn't find child when deleting frame.\n");
244                 return;
245         }
246
247         for ( i=idx; i<parent->num_children-1; i++ ) {
248                 parent->children[i] = parent->children[i+1];
249         }
250
251         parent->num_children--;
252 }
253
254
255 struct frame *find_frame_with_scblocks(struct frame *fr, SCBlock *scblocks)
256 {
257         int i;
258
259         if ( fr->scblocks == scblocks ) return fr;
260
261         for ( i=0; i<fr->num_children; i++ ) {
262                 struct frame *tt;
263                 tt = find_frame_with_scblocks(fr->children[i], scblocks);
264                 if ( tt != NULL ) return tt;
265         }
266
267         return NULL;
268 }
269
270
271 static size_t run_text_len(const struct text_run *run)
272 {
273         if ( run == NULL ) {
274                 fprintf(stderr, "NULL run passed to run_text_len\n");
275                 return 0;
276         }
277
278         if ( run->rscblock == NULL ) {
279                 fprintf(stderr, "NULL rscblock in run_text_len\n");
280                 return 0;
281         }
282
283         if ( sc_block_contents(run->rscblock) == NULL ) {
284                 if ( sc_block_name(run->rscblock) != NULL ) {
285                         if ( strcmp("newpara", sc_block_name(run->rscblock)) == 0 ) {
286                                 return 0;
287                         }
288                 }
289                 fprintf(stderr, "NULL rscblock contents in run_text_len\n");
290                 return 0;
291         }
292
293         return strlen(sc_block_contents(run->rscblock));
294 }
295
296
297 void wrap_paragraph(Paragraph *para, PangoContext *pc, double w,
298                     size_t sel_start, size_t sel_end)
299 {
300         size_t total_len = 0;
301         int i;
302         char *text;
303         PangoAttrList *attrs;
304         PangoRectangle rect;
305         size_t pos = 0;
306
307         w -= para->space[0] + para->space[1];
308
309         if ( para->type == PARA_TYPE_IMAGE ) {
310                 if ( para->image_w < 0.0 ) {
311                         para->image_w = w;
312                         para->image_h = w*((float)para->image_real_h/para->image_real_w);
313                 }
314                 para->height = para->image_h;
315                 return;
316         }
317
318         if ( para->type != PARA_TYPE_TEXT ) return;
319
320         for ( i=0; i<para->n_runs; i++ ) {
321                 total_len += run_text_len(&para->runs[i]);
322         }
323
324         /* Allocate the complete text */
325         text = malloc(total_len+1);
326         if ( text == NULL ) {
327                 fprintf(stderr, "Couldn't allocate combined text (%lli)\n",
328                        (long long int)total_len);
329                 return;
330         }
331
332         /* Allocate the attributes */
333         attrs = pango_attr_list_new();
334
335         /* Put all of the text together */
336         text[0] = '\0';
337         for ( i=0; i<para->n_runs; i++ ) {
338
339                 PangoAttribute *attr;
340                 const char *run_text;
341                 size_t run_len;
342                 guint16 r, g, b;
343
344                 run_text = sc_block_contents(para->runs[i].rscblock);
345
346                 if ( run_text == NULL ) continue;  /* Could be \newpara */
347                 run_len = strlen(run_text);
348
349                 attr = pango_attr_font_desc_new(para->runs[i].fontdesc);
350                 attr->start_index = pos;
351                 attr->end_index = pos + run_len;
352                 pango_attr_list_insert(attrs, attr);
353
354                 r = para->runs[i].col[0] * 65535;
355                 g = para->runs[i].col[1] * 65535;
356                 b = para->runs[i].col[2] * 65535;
357                 attr = pango_attr_foreground_new(r, g, b);
358                 attr->start_index = pos;
359                 attr->end_index = pos + run_len;
360                 pango_attr_list_insert(attrs, attr);
361
362                 pos += run_len;
363                 strncat(text, run_text, run_len);
364
365         }
366
367         /* Add attributes for selected text */
368         if ( sel_start > 0 || sel_end > 0 ) {
369                 PangoAttribute *attr;
370                 attr = pango_attr_background_new(42919, 58853, 65535);
371                 attr->start_index = sel_start;
372                 attr->end_index = sel_end;
373                 pango_attr_list_insert(attrs, attr);
374         }
375
376         if ( para->layout == NULL ) {
377                 para->layout = pango_layout_new(pc);
378                 pango_layout_set_spacing(para->layout, 5000);
379         }
380         pango_layout_set_width(para->layout, pango_units_from_double(w));
381         pango_layout_set_text(para->layout, text, total_len);
382         pango_layout_set_attributes(para->layout, attrs);
383         free(text);
384         pango_attr_list_unref(attrs);
385
386         pango_layout_get_extents(para->layout, NULL, &rect);
387         para->height = pango_units_to_double(rect.height);
388         para->height += para->space[2] + para->space[3];
389 }
390
391 SCBlock *get_newline_at_end(Paragraph *para)
392 {
393         return para->newline_at_end;
394 }
395
396
397 void set_newline_at_end(Paragraph *para, SCBlock *bl)
398 {
399         para->newline_at_end = bl;
400 }
401
402
403 void add_run(Paragraph *para, SCBlock *scblock, SCBlock *rscblock,
404              size_t len_bytes, PangoFontDescription *fdesc,
405              double col[4])
406 {
407         struct text_run *runs_new;
408
409         if ( !para->open ) {
410                 fprintf(stderr, "Adding a run to a closed paragraph!\n");
411                 return;
412         }
413
414         runs_new = realloc(para->runs,
415                            (para->n_runs+1)*sizeof(struct text_run));
416         if ( runs_new == NULL ) {
417                 fprintf(stderr, "Failed to add run.\n");
418                 return;
419         }
420
421         para->runs = runs_new;
422         para->runs[para->n_runs].scblock = scblock;
423         para->runs[para->n_runs].rscblock = rscblock;
424         para->runs[para->n_runs].fontdesc = pango_font_description_copy(fdesc);
425         para->runs[para->n_runs].col[0] = col[0];
426         para->runs[para->n_runs].col[1] = col[1];
427         para->runs[para->n_runs].col[2] = col[2];
428         para->runs[para->n_runs].col[3] = col[3];
429         para->n_runs++;
430 }
431
432
433 static Paragraph *create_paragraph(struct frame *fr)
434 {
435         Paragraph **paras_new;
436         Paragraph *pnew;
437
438         paras_new = realloc(fr->paras, (fr->n_paras+1)*sizeof(Paragraph *));
439         if ( paras_new == NULL ) return NULL;
440
441         pnew = calloc(1, sizeof(struct _paragraph));
442         if ( pnew == NULL ) return NULL;
443
444         fr->paras = paras_new;
445         fr->paras[fr->n_paras++] = pnew;
446
447         return pnew;
448 }
449
450
451 /* Create a new paragraph in 'fr' just after paragraph 'pos' */
452 Paragraph *insert_paragraph(struct frame *fr, int pos)
453 {
454         Paragraph **paras_new;
455         Paragraph *pnew;
456         int i;
457
458         if ( pos >= fr->n_paras ) {
459                 fprintf(stderr, "insert_paragraph(): pos too high!\n");
460                 return NULL;
461         }
462
463         paras_new = realloc(fr->paras, (fr->n_paras+1)*sizeof(Paragraph *));
464         if ( paras_new == NULL ) return NULL;
465
466         pnew = calloc(1, sizeof(struct _paragraph));
467         if ( pnew == NULL ) return NULL;
468
469         pnew->open = 1;
470
471         fr->paras = paras_new;
472         fr->n_paras++;
473
474         for ( i=fr->n_paras-1; i>pos; i-- ) {
475                 fr->paras[i] = fr->paras[i-1];
476         }
477         fr->paras[pos+1] = pnew;
478
479         return pnew;
480 }
481
482
483 void add_callback_para(struct frame *fr, SCBlock *bl,
484                        double w, double h,
485                        SCCallbackDrawFunc draw_func,
486                        SCCallbackClickFunc click_func, void *bvp,
487                        void *vp)
488 {
489         Paragraph *pnew;
490
491         pnew = create_paragraph(fr);
492         if ( pnew == NULL ) {
493                 fprintf(stderr, "Failed to add callback paragraph\n");
494                 return;
495         }
496
497         pnew->type = PARA_TYPE_CALLBACK;
498         pnew->scblock = bl;
499         pnew->cb_w = w;
500         pnew->cb_h = h;
501         pnew->draw_func = draw_func;
502         pnew->click_func = click_func;
503         pnew->bvp = bvp;
504         pnew->vp = vp;
505         pnew->height = h;
506         pnew->open = 0;
507 }
508
509
510 void add_image_para(struct frame *fr, SCBlock *scblock, const char *filename,
511                     ImageStore *is, double w, double h, int editable)
512 {
513         Paragraph *pnew;
514         int wi, hi;
515
516         pnew = create_paragraph(fr);
517         if ( pnew == NULL ) {
518                 fprintf(stderr, "Failed to add image paragraph\n");
519                 return;
520         }
521
522         if ( imagestore_get_size(is, filename, &wi, &hi) ) {
523                 fprintf(stderr, "Couldn't get size for %s\n", filename);
524                 wi = 100;
525                 hi = 100;
526         }
527
528         pnew->type = PARA_TYPE_IMAGE;
529         pnew->scblock = scblock;
530         pnew->filename = strdup(filename);
531         pnew->image_w = w;
532         pnew->image_h = h;
533         pnew->image_real_w = wi;
534         pnew->image_real_h = hi;
535         pnew->height = h;
536         pnew->open = 0;
537         pnew->space[0] = 0.0;
538         pnew->space[1] = 0.0;
539         pnew->space[2] = 0.0;
540         pnew->space[3] = 0.0;
541 }
542
543
544 double total_height(struct frame *fr)
545 {
546         int i;
547         double t = 0.0;
548         for ( i=0; i<fr->n_paras; i++ ) {
549                 t += fr->paras[i]->height;
550         }
551         return t;
552 }
553
554
555 Paragraph *last_open_para(struct frame *fr)
556 {
557         Paragraph *pnew;
558
559         if ( (fr->paras != NULL) && (fr->paras[fr->n_paras-1]->open) ) {
560                 return fr->paras[fr->n_paras-1];
561         }
562
563         /* No open paragraph found, create a new one */
564         pnew = create_paragraph(fr);
565         if ( pnew == NULL ) return NULL;
566
567         pnew->type = PARA_TYPE_TEXT;
568         pnew->open = 1;
569         pnew->n_runs = 0;
570         pnew->runs = NULL;
571         pnew->layout = NULL;
572         pnew->height = 0.0;
573
574         return pnew;
575 }
576
577
578 void close_last_paragraph(struct frame *fr)
579 {
580         if ( fr->paras == NULL ) return;
581         if ( fr->paras[fr->n_paras-1]->type != PARA_TYPE_TEXT ) {
582                 printf("Closing a non-text paragraph!\n");
583         }
584         fr->paras[fr->n_paras-1]->open = 0;
585 }
586
587
588 int last_para_available_for_text(struct frame *fr)
589 {
590         Paragraph *last_para;
591         if ( fr->paras == NULL ) return 0;
592         last_para = fr->paras[fr->n_paras-1];
593         if ( last_para->type == PARA_TYPE_TEXT ) {
594                 if ( last_para->open ) return 1;
595         }
596         return 0;
597 }
598
599
600 static void render_from_surf(cairo_surface_t *surf, cairo_t *cr,
601                              double w, double h, int border)
602 {
603         double x, y;
604         int sw, sh;
605
606         x = 0.0;  y = 0.0;
607         cairo_user_to_device(cr, &x, &y);
608         x = rint(x);  y = rint(y);
609         cairo_device_to_user(cr, &x, &y);
610
611         sw = cairo_image_surface_get_width(surf);
612         sh = cairo_image_surface_get_height(surf);
613
614         cairo_save(cr);
615         cairo_scale(cr, w/sw, h/sh);
616         cairo_new_path(cr);
617         cairo_rectangle(cr, x, y, sw, sh);
618         cairo_set_source_surface(cr, surf, 0.0, 0.0);
619         cairo_pattern_t *patt = cairo_get_source(cr);
620         cairo_pattern_set_extend(patt, CAIRO_EXTEND_PAD);
621         cairo_pattern_set_filter(patt, CAIRO_FILTER_BEST);
622         cairo_fill(cr);
623         cairo_restore(cr);
624
625         if ( border ) {
626                 cairo_new_path(cr);
627                 cairo_rectangle(cr, x+0.5, y+0.5, w, h);
628                 cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
629                 cairo_set_line_width(cr, 1.0);
630                 cairo_stroke(cr);
631         }
632 }
633
634
635 void render_paragraph(cairo_t *cr, Paragraph *para, ImageStore *is)
636 {
637         cairo_surface_t *surf;
638         cairo_surface_type_t type;
639         double w, h;
640
641         cairo_translate(cr, para->space[0], para->space[2]);
642
643         type = cairo_surface_get_type(cairo_get_target(cr));
644
645         switch ( para->type ) {
646
647                 case PARA_TYPE_TEXT :
648                 cairo_set_source_rgba(cr, 0.0, 0.0, 0.0, 1.0);
649                 pango_cairo_update_layout(cr, para->layout);
650                 pango_cairo_show_layout(cr, para->layout);
651                 cairo_fill(cr);
652                 break;
653
654                 case PARA_TYPE_IMAGE :
655                 w = para->image_w;
656                 h = para->image_h;
657                 cairo_user_to_device_distance(cr, &w, &h);
658                 surf = lookup_image(is, para->filename, w);
659                 if ( surf != NULL ) {
660                         render_from_surf(surf, cr, para->image_w, para->image_h, 0);
661                 } else {
662                         printf("surf = NULL!\n");
663                 }
664                 break;
665
666                 case PARA_TYPE_CALLBACK :
667                 w = para->cb_w;
668                 h = para->cb_h;
669                 cairo_user_to_device_distance(cr, &w, &h);
670                 if ( type == CAIRO_SURFACE_TYPE_PDF ) {
671                         w *= 6;  h *= 6;
672                 }
673                 surf = para->draw_func(w, h,
674                                        para->bvp, para->vp);
675                 render_from_surf(surf, cr, para->cb_w, para->cb_h, 1);
676                 cairo_surface_destroy(surf);  /* FIXME: Cache like crazy */
677                 break;
678
679         }
680 }
681
682
683 size_t end_offset_of_para(struct frame *fr, int pn)
684 {
685         int i;
686         size_t total = 0;
687         for ( i=0; i<fr->paras[pn]->n_runs; i++ ) {
688                 total += run_text_len(&fr->paras[pn]->runs[i]);
689         }
690         return total;
691 }
692
693
694 /* Local x,y in paragraph -> text offset */
695 static size_t text_para_pos(Paragraph *para, double x, double y, int *ptrail)
696 {
697         int idx;
698         pango_layout_xy_to_index(para->layout, pango_units_from_double(x),
699                                  pango_units_from_double(y), &idx, ptrail);
700         return idx;
701 }
702
703
704 void show_edit_pos(struct edit_pos a)
705 {
706         printf("para %i, pos %li, trail %i\n", a.para, (long int)a.pos, a.trail);
707 }
708
709
710 int positions_equal(struct edit_pos a, struct edit_pos b)
711 {
712         if ( a.para != b.para ) return 0;
713         if ( a.pos != b.pos ) return 0;
714         if ( a.trail != b.trail ) return 0;
715         return 1;
716 }
717
718
719 void sort_positions(struct edit_pos *a, struct edit_pos *b)
720 {
721         if ( a->para > b->para ) {
722                 size_t tpos;
723                 int tpara, ttrail;
724                 tpara = b->para;   tpos = b->pos;  ttrail = b->trail;
725                 b->para = a->para;  b->pos = a->pos;  b->trail = a->trail;
726                 a->para = tpara;    a->pos = tpos;    a->trail = ttrail;
727         }
728
729         if ( (a->para == b->para) && (a->pos > b->pos) )
730         {
731                 size_t tpos = b->pos;
732                 int ttrail = b->trail;
733                 b->pos = a->pos;  b->trail = a->trail;
734                 a->pos = tpos;    a->trail = ttrail;
735         }
736 }
737
738
739 int find_cursor_2(struct frame *fr, double x, double y,
740                   struct edit_pos *pos)
741 {
742         double pad = fr->pad_t;
743         int i;
744
745         if ( fr == NULL ) {
746                 fprintf(stderr, "Cursor frame is NULL.\n");
747                 return 1;
748         }
749
750         for ( i=0; i<fr->n_paras; i++ ) {
751                 double npos = pad + fr->paras[i]->height;
752                 if ( npos > y ) {
753                         pos->para = i;
754                         if ( fr->paras[i]->type == PARA_TYPE_TEXT ) {
755                                 pos->pos = text_para_pos(fr->paras[i],
756                                           x - fr->pad_l - fr->paras[i]->space[0],
757                                           y - pad - fr->paras[i]->space[2],
758                                           &pos->trail);
759                         } else {
760                                 pos->pos = 0;
761                         }
762                         return 0;
763                 }
764                 pad = npos;
765         }
766
767         if ( fr->n_paras == 0 ) {
768                 printf("No paragraphs in frame.\n");
769                 return 1;
770         }
771
772         /* Pretend it's in the last paragraph */
773         pad -= fr->paras[fr->n_paras-1]->height;
774         pos->para = fr->n_paras - 1;
775         pos->pos = text_para_pos(fr->paras[fr->n_paras-1],
776                                  x - fr->pad_l, y - pad, &pos->trail);
777         return 0;
778 }
779
780
781 int find_cursor(struct frame *fr, double x, double y,
782                 int *ppara, size_t *ppos, int *ptrail)
783 {
784         struct edit_pos p;
785         int r;
786         r = find_cursor_2(fr, x, y, &p);
787         if ( r ) return r;
788         *ppara = p.para;
789         *ppos = p.pos;
790         *ptrail = p.trail;
791         return 0;
792 }
793
794
795 int get_para_highlight(struct frame *fr, int cursor_para,
796                        double *cx, double *cy, double *cw, double *ch)
797 {
798         Paragraph *para;
799         int i;
800         double py = 0.0;
801
802         if ( fr == NULL ) {
803                 fprintf(stderr, "Cursor frame is NULL.\n");
804                 return 1;
805         }
806
807         if ( cursor_para >= fr->n_paras ) {
808                 fprintf(stderr, "Highlight paragraph number is too high!\n");
809                 return 1;
810         }
811
812         para = fr->paras[cursor_para];
813         for ( i=0; i<cursor_para; i++ ) {
814                 py += fr->paras[i]->height;
815         }
816
817         *cx = fr->pad_l + para->space[0];
818         *cy = fr->pad_t + py + para->space[2];
819         *cw = fr->w - fr->pad_l - fr->pad_r - para->space[0] - para->space[1];
820         *ch = para->height - para->space[2] - para->space[3];
821         return 0;
822 }
823
824
825 int get_cursor_pos(struct frame *fr, int cursor_para, int cursor_pos,
826                    double *cx, double *cy, double *ch)
827 {
828         Paragraph *para;
829         PangoRectangle rect;
830         int i;
831         double py = 0.0;
832
833         if ( fr == NULL ) {
834                 fprintf(stderr, "Cursor frame is NULL.\n");
835                 return 1;
836         }
837
838         if ( cursor_para >= fr->n_paras ) {
839                 fprintf(stderr, "Cursor paragraph number is too high!\n");
840                 return 1;
841         }
842
843         para = fr->paras[cursor_para];
844         for ( i=0; i<cursor_para; i++ ) {
845                 py += fr->paras[i]->height;
846         }
847
848         if ( para->type != PARA_TYPE_TEXT ) {
849                 return 1;
850         }
851
852         pango_layout_get_cursor_pos(para->layout, cursor_pos, &rect, NULL);
853
854         *cx = pango_units_to_double(rect.x) + fr->pad_l + para->space[0];
855         *cy = pango_units_to_double(rect.y) + fr->pad_t + py + para->space[2];
856         *ch = pango_units_to_double(rect.height);
857         return 0;
858 }
859
860
861 void cursor_moveh(struct frame *fr, int *cpara, size_t *cpos, int *ctrail,
862                   signed int dir)
863 {
864         Paragraph *para = fr->paras[*cpara];
865         int np = *cpos;
866
867         pango_layout_move_cursor_visually(para->layout, 1, *cpos, *ctrail,
868                                           dir, &np, ctrail);
869         if ( np == -1 ) {
870                 if ( *cpara > 0 ) {
871                         size_t end_offs;
872                         (*cpara)--;
873                         end_offs = end_offset_of_para(fr, *cpara);
874                         if ( end_offs > 0 ) {
875                                 *cpos = end_offs - 1;
876                                 *ctrail = 1;
877                         } else {
878                                 /* Jumping into an empty paragraph */
879                                 *cpos = 0;
880                                 *ctrail = 0;
881                         }
882                         return;
883                 } else {
884                         /* Can't move any further */
885                         return;
886                 }
887         }
888
889         if ( np == G_MAXINT ) {
890                 if ( *cpara < fr->n_paras-1 ) {
891                         (*cpara)++;
892                         *cpos = 0;
893                         *ctrail = 0;
894                         return;
895                 } else {
896                         /* Can't move any further */
897                         return;
898                 }
899         }
900
901         *cpos = np;
902 }
903
904
905 void cursor_movev(struct frame *fr, int *cpara, size_t *cpos, int *ctrail,
906                   signed int dir)
907 {
908 }
909
910
911 void check_callback_click(struct frame *fr, int para)
912 {
913         Paragraph *p = fr->paras[para];
914         if ( p->type == PARA_TYPE_CALLBACK ) {
915                 p->click_func(0.0, 0.0, p->bvp, p->vp);
916         }
917 }
918
919
920 static int get_paragraph_offset(Paragraph *para, int nrun)
921 {
922         int i;
923         size_t t = 0;
924
925         for ( i=0; i<nrun; i++ ) {
926                 struct text_run *run = &para->runs[i];
927                 t += run_text_len(run);
928         }
929         return t;
930 }
931
932
933 static int which_run(Paragraph *para, size_t offs)
934 {
935         int i;
936         size_t t = 0;
937
938         for ( i=0; i<para->n_runs; i++ ) {
939                 struct text_run *run = &para->runs[i];
940                 t += run_text_len(run);
941                 if ( t > offs ) return i;
942         }
943
944         /* Maybe offs points exactly to the end of the last run? */
945         if ( t == offs ) return para->n_runs-1;
946
947         return para->n_runs;
948 }
949
950
951 size_t pos_trail_to_offset(Paragraph *para, size_t offs, int trail)
952 {
953         glong char_offs;
954         size_t run_offs;
955         const char *run_text;
956         struct text_run *run;
957         int nrun;
958         char *ptr;
959         size_t para_offset_of_run;
960
961         nrun = which_run(para, offs);
962
963         if ( nrun == para->n_runs ) {
964                 fprintf(stderr, "pos_trail_to_offset: Offset too high\n");
965                 return 0;
966         }
967
968         run = &para->runs[nrun];
969
970         if ( para->type != PARA_TYPE_TEXT ) return 0;
971
972         if ( run == NULL ) {
973                 fprintf(stderr, "pos_trail_to_offset: No run\n");
974                 return 0;
975         }
976
977         if ( run->scblock == NULL ) {
978                 fprintf(stderr, "pos_trail_to_offset: SCBlock = NULL?\n");
979                 return 0;
980         }
981
982         if ( (sc_block_name(run->rscblock) != NULL)
983           && (strcmp(sc_block_name(run->rscblock), "newpara") == 0) )
984         {
985                 return 0;
986         }
987
988         if ( sc_block_contents(run->rscblock) == NULL ) {
989                 fprintf(stderr, "pos_trail_to_offset: No contents "
990                         "(%p name=%s, options=%s)\n",
991                         run->scblock, sc_block_name(run->scblock),
992                         sc_block_options(run->scblock));
993                 return 0;
994         }
995
996         /* Get the text for the run */
997         run_text = sc_block_contents(run->rscblock);
998
999         /* Turn  the paragraph offset into a run offset */
1000         para_offset_of_run = get_paragraph_offset(para, nrun);
1001         run_offs = offs - para_offset_of_run;
1002
1003         char_offs = g_utf8_pointer_to_offset(run_text, run_text+run_offs);
1004         char_offs += trail;
1005
1006         if ( char_offs > g_utf8_strlen(run_text, -1) ) {
1007                 printf("Offset outside string! '%s'\n"
1008                        "char_offs %li offs %li len %li\n",
1009                        run_text, (long int)char_offs, (long int)offs,
1010                        (long int)g_utf8_strlen(run_text, -1));
1011         }
1012
1013         ptr = g_utf8_offset_to_pointer(run_text, char_offs);
1014         return ptr - run_text + para_offset_of_run;
1015 }
1016
1017
1018 void insert_text_in_paragraph(Paragraph *para, size_t offs, const char *t)
1019 {
1020         int nrun;
1021         struct text_run *run;
1022         size_t run_offs;
1023
1024         /* Find which run we are in */
1025         nrun = which_run(para, offs);
1026         if ( nrun == para->n_runs ) {
1027                 fprintf(stderr, "Couldn't find run to insert into.\n");
1028                 return;
1029         }
1030         run = &para->runs[nrun];
1031
1032         if ( (sc_block_name(run->scblock) != NULL)
1033           && (strcmp(sc_block_name(run->scblock), "newpara") == 0) )
1034         {
1035
1036                 SCBlock *nnp;
1037                 printf("Inserting into newpara block...\n");
1038
1039                 /* Add a new \newpara block after this one */
1040                 nnp = sc_block_append(run->scblock, "newpara",
1041                                       NULL, NULL, NULL);
1042
1043                 /* The first \newpara block becomes a normal anonymous block */
1044                 sc_block_set_name(run->scblock, NULL);
1045
1046                 if ( para->newline_at_end == run->scblock ) {
1047                         para->newline_at_end = nnp;
1048                         printf("Replaced the newpara block\n");
1049                 }
1050
1051         }
1052
1053         /* Translate paragraph offset for insertion into SCBlock offset */
1054         run_offs = offs - get_paragraph_offset(para, nrun);
1055         sc_insert_text(run->rscblock, run_offs, t);
1056 }
1057
1058
1059 static SCBlock *pos_to_rscblock(struct frame *fr, struct edit_pos p)
1060 {
1061         int run;
1062         size_t paraoffs;
1063         Paragraph *para;
1064
1065         para = fr->paras[p.para];
1066
1067         if ( para->type != PARA_TYPE_TEXT ) {
1068                 return NULL;
1069         }
1070
1071         paraoffs = pos_trail_to_offset(para, p.pos, p.trail);
1072
1073         run = which_run(para, paraoffs);
1074         assert(run < para->n_runs);
1075
1076         return para->runs[run].rscblock;
1077 }
1078
1079
1080
1081 static SCBlock *pos_to_scblock(struct frame *fr, struct edit_pos p,
1082                                enum para_type *type)
1083 {
1084         int run;
1085         size_t paraoffs;
1086         Paragraph *para;
1087
1088         para = fr->paras[p.para];
1089         if ( type != NULL ) {
1090                 *type = para->type;
1091         }
1092
1093         if ( para->type != PARA_TYPE_TEXT ) {
1094                 return para->scblock;
1095         }
1096
1097         paraoffs = pos_trail_to_offset(para, p.pos, p.trail);
1098
1099         run = which_run(para, paraoffs);
1100         assert(run < para->n_runs);
1101
1102         return para->runs[run].scblock;
1103 }
1104
1105
1106 static size_t pos_to_offset(struct frame *fr, struct edit_pos p)
1107 {
1108         int run;
1109         size_t paraoffs;
1110         Paragraph *para;
1111
1112         para = fr->paras[p.para];
1113         if ( para->type != PARA_TYPE_TEXT ) {
1114                 return 0;
1115         }
1116
1117         /* Offset of this position into the paragraph */
1118         paraoffs = pos_trail_to_offset(para, p.pos, p.trail);
1119
1120         run = which_run(para, paraoffs);
1121         assert(run < para->n_runs);
1122
1123         /* Offset of this position into the run
1124          * (and therefore into the SCBlock) */
1125         return paraoffs - get_paragraph_offset(para, run);
1126 }
1127
1128
1129 static int pos_to_run_number(struct frame *fr, struct edit_pos p)
1130 {
1131         int run;
1132         size_t paraoffs;
1133         Paragraph *para;
1134
1135         para = fr->paras[p.para];
1136         if ( para->type != PARA_TYPE_TEXT ) {
1137                 return 0;
1138         }
1139
1140         paraoffs = pos_trail_to_offset(para, p.pos, p.trail);
1141
1142         run = which_run(para, paraoffs);
1143         assert(run < para->n_runs);
1144
1145         return run;
1146 }
1147
1148
1149 static void delete_run(Paragraph *para, int nrun)
1150 {
1151         printf("deleting run %i of %i from para %p\n", nrun, para->n_runs, para);
1152         memmove(&para->runs[nrun], &para->runs[nrun+1],
1153                 (para->n_runs-nrun-1)*sizeof(struct text_run));
1154         para->n_runs--;
1155 }
1156
1157
1158 static Paragraph *scan_runs_for_scblock(struct frame *fr, int pn1, int pn2,
1159                                         SCBlock *bl, int *run)
1160 {
1161         int i;
1162         for ( i=pn1; i<=pn2; i++ ) {
1163                 int j;
1164                 for ( j=0; j<fr->paras[i]->n_runs; j++ ) {
1165                         if ( fr->paras[i]->runs[j].rscblock == bl ) {
1166                                 *run = j;
1167                                 return fr->paras[i];
1168                         }
1169                         if ( fr->paras[i]->runs[j].scblock == bl ) {
1170                                 *run = j;
1171                                 return fr->paras[i];
1172                         }
1173                 }
1174         }
1175         return NULL;
1176 }
1177
1178
1179 static Paragraph *find_run_for_scblock_next(struct frame *fr, int pn1, int pn2,
1180                                             SCBlock *bl, int *run)
1181 {
1182         if ( sc_block_child(bl) != NULL ) {
1183                 Paragraph *para;
1184                 para = find_run_for_scblock_next(fr, pn1, pn2,
1185                                                  sc_block_child(bl), run);
1186                 if ( para != NULL ) return para;
1187         }
1188
1189         do {
1190                 Paragraph *para;
1191                 para = scan_runs_for_scblock(fr, pn1, pn2, bl, run);
1192                 if ( para != NULL ) return para;
1193                 bl = sc_block_next(bl);
1194         } while ( bl != NULL );
1195
1196         return NULL;
1197 }
1198
1199
1200 /* Find the run which contains the text from "bl",
1201  * taking into account that it might be a child block, for example:
1202  *   {some text}
1203  *   \italic          <---- bl points here
1204  *      {more text}   <---- but this block is referenced by the run
1205  *   {final text}
1206  */
1207 static Paragraph *find_run_for_scblock(struct frame *fr, int pn1, int pn2,
1208                                        SCBlock *bl, int *run)
1209 {
1210         Paragraph *para;
1211
1212         show_sc_block(bl, "searching ");
1213         para = scan_runs_for_scblock(fr, pn1, pn2, bl, run);
1214         if ( para != NULL ) return para;
1215
1216         if ( sc_block_child(bl) != NULL ) {
1217                 para = find_run_for_scblock_next(fr, pn1, pn2, sc_block_child(bl), run);
1218                 if ( para != NULL ) return para;
1219         }
1220
1221         return NULL;
1222 }
1223
1224
1225 static int paragraph_number(struct frame *fr, Paragraph *p, int *err)
1226 {
1227         int i;
1228         for ( i=0; i<fr->n_paras; i++ ) {
1229                 if ( fr->paras[i] == p ) return i;
1230         }
1231         fprintf(stderr, "Couldn't find paragraph %p\n", p);
1232         *err = 1;
1233         return 0;
1234 }
1235
1236
1237 static void delete_run_for_scblock(struct frame *fr,
1238                                    Paragraph *p1, Paragraph *p2, SCBlock *bl)
1239 {
1240         int pn1, pn2;
1241         int err = 0;
1242         Paragraph *para;
1243         int run;
1244
1245         pn1 = paragraph_number(fr, p1, &err);
1246         pn2 = paragraph_number(fr, p2, &err);
1247         if ( err ) return;
1248
1249         para = find_run_for_scblock(fr, pn1, pn2, bl, &run);
1250         if ( para == NULL ) {
1251                 fprintf(stderr, "Couldn't find block %p between paragraphs %p and %p\n",
1252                         bl, p1, p2);
1253                 return;
1254         }
1255
1256         delete_run(para, run);
1257 }
1258
1259
1260 static signed int merge_paragraph_runs(Paragraph *p1, Paragraph *p2)
1261 {
1262         struct text_run *runs_new;
1263         int i, spos;
1264
1265         /* All the runs from p2 get added to p1 */
1266         runs_new = realloc(p1->runs,
1267                            (p1->n_runs+p2->n_runs)*sizeof(struct text_run));
1268         if ( runs_new == NULL ) {
1269                 fprintf(stderr, "Failed to allocate merged runs.\n");
1270                 return -1;
1271         }
1272         p1->runs = runs_new;
1273
1274         assert(p1->runs[p1->n_runs-1].scblock == get_newline_at_end(p1));
1275         p1->n_runs--;  /* Chop off the run corresponding to \newpara */
1276         spos = p1->n_runs;
1277
1278         /* The end of the united paragraph should now be the end of the
1279          * second one */
1280         set_newline_at_end(p1, get_newline_at_end(p2));
1281
1282         for ( i=0; i<p2->n_runs; i++ ) {
1283                 p1->runs[p1->n_runs] = p2->runs[i];
1284                 p1->n_runs++;
1285         }
1286         free(p2->runs);
1287         free(p2);
1288
1289         return spos;
1290 }
1291
1292
1293 void merge_paragraphs(struct frame *fr, int para)
1294 {
1295         Paragraph *p1, *p2;
1296         int i;
1297         SCBlock *n;
1298
1299         if ( para >= fr->n_paras-1 ) {
1300                 printf("Paragraph number too high to merge.\n");
1301                 return;
1302         }
1303
1304         p1 = fr->paras[para];
1305         p2 = fr->paras[para+1];
1306
1307         if ( (p1->type != PARA_TYPE_TEXT) || (p2->type != PARA_TYPE_TEXT) ) {
1308                 printf("Trying to merge non-text paragraphs.\n");
1309                 return;
1310         }
1311
1312         /* Delete the \newpara block to unite the paragraphs */
1313         n = get_newline_at_end(p1);
1314         assert(n != NULL);
1315
1316         if ( sc_block_delete(&fr->scblocks, n) ) {
1317                 fprintf(stderr, "Failed to delete paragraph end sentinel.\n");
1318                 return;
1319         }
1320
1321         merge_paragraph_runs(p1, p2);
1322
1323         for ( i=para+1; i<fr->n_paras-1; i++ ) {
1324                 fr->paras[i] = fr->paras[i+1];
1325         }
1326         fr->n_paras--;
1327 }
1328
1329
1330
1331 static void merge_paragraphs_by_newpara(struct frame *fr, SCBlock *np)
1332 {
1333         int i;
1334         Paragraph *p1;
1335         Paragraph *p2;
1336
1337         for ( i=0; i<fr->n_paras-1; i++ ) {
1338                 if ( fr->paras[i]->newline_at_end == np ) {
1339
1340                         int j;
1341                         signed int spos;
1342
1343                         p1 = fr->paras[i];
1344                         p2 = fr->paras[i+1];
1345
1346                         printf("-------------------------------\n");
1347                         show_para(p1);
1348                         printf("---x--------x------------------\n");
1349                         show_para(p2);
1350                         spos = merge_paragraph_runs(p1, p2);
1351                         if ( spos < 0 ) {
1352                                 fprintf(stderr, "Failed to merge paragraphs\n");
1353                                 return;
1354                         }
1355                         printf("-------------------------------\n");
1356                         show_para(p1);
1357
1358                         for ( j=i+1; j<fr->n_paras-1; j++ ) {
1359                                 fr->paras[j] = fr->paras[j+1];
1360                         }
1361                         fr->n_paras--;
1362
1363                         return;
1364
1365                 }
1366         }
1367
1368         fprintf(stderr, "Couldn't find paragraphs to merge by newpara\n");
1369 }
1370
1371
1372 static int find_block_inside(SCBlock *needle, SCBlock *bl)
1373 {
1374         if ( needle == bl ) return 1;
1375
1376         if ( sc_block_child(bl) != NULL ) {
1377                 if ( find_block_inside(needle, sc_block_child(bl)) ) return 1;
1378         }
1379
1380         if ( sc_block_next(bl) != NULL ) {
1381                 if ( find_block_inside(needle, sc_block_next(bl)) ) return 1;
1382         }
1383
1384         return 0;
1385 }
1386
1387
1388 /* Return true if "top" either IS "child", or contains "child" somewhere
1389  * underneath, even if via a macro expansion */
1390 static int block_is_under(SCBlock *needle, SCBlock *top)
1391 {
1392         if ( needle == top ) return 1;
1393
1394         if ( sc_block_child(top) != NULL ) {
1395                 if ( find_block_inside(needle, sc_block_child(top)) ) return 1;
1396         }
1397
1398         /* Do not look at top->next here */
1399
1400         return 0;
1401 }
1402
1403
1404 void delete_text_from_frame(struct frame *fr, struct edit_pos p1, struct edit_pos p2,
1405                             double wrapw)
1406 {
1407         int i;
1408         SCBlock *p1scblock, *p2scblock;
1409         SCBlock *p1rscblock, *p2rscblock;
1410         enum para_type type1, type2;
1411         size_t p2offs;
1412         SCBlock *scblock;
1413
1414         sort_positions(&p1, &p2);
1415
1416         /* Find SC positions for start and end */
1417         p1scblock = pos_to_scblock(fr, p1, &type1);
1418         p2scblock = pos_to_scblock(fr, p2, &type2);
1419         p1rscblock = pos_to_rscblock(fr, p1);
1420         p2rscblock = pos_to_rscblock(fr, p2);
1421         p2offs = pos_to_offset(fr, p2);
1422
1423         printf("SCBlocks %p to %p\n", p1scblock, p2scblock);
1424         //show_sc_blocks(p1scblock);
1425
1426         if ( (p1scblock == p2scblock) && (type1 == PARA_TYPE_TEXT) ) {
1427
1428                 size_t p1offs;
1429                 printf("Simple case, one SCBlock\n");
1430
1431                 assert(type1 == type2);
1432
1433                 /* Remove the text and update the run length */
1434                 p1offs = pos_to_offset(fr, p1);
1435                 scblock_delete_text(p1scblock, p1offs, p2offs);
1436
1437                 wrap_paragraph(fr->paras[p1.para], NULL, wrapw, 0, 0);
1438
1439                 return;
1440         }
1441
1442         /* Starting point for iteration over blocks in middle of range.
1443          * Record this now, because p1scblock might be about to get deleted */
1444         scblock = sc_block_next(p1scblock);
1445
1446         /* First SCBlock in range: delete whole thing or second half */
1447         printf("First block %p\n", p1scblock);
1448         if ( type1 == PARA_TYPE_TEXT ) {
1449
1450                 size_t p1offs = pos_to_offset(fr, p1);
1451                 int p1run = pos_to_run_number(fr, p1);
1452                 printf("  offs %li\n", (long int)p1offs);
1453                 if ( p1offs != 0 ) {
1454                         printf("Partial delete\n");
1455                         printf("contents '%s'\n", sc_block_contents(p1rscblock));
1456                         printf("from offs %li\n", (long int)p1offs);
1457                         scblock_delete_text(p1rscblock, p1offs, -1);
1458                 } else {
1459                         printf("Deleting the whole text SCBlock\n");
1460                         sc_block_delete(&fr->scblocks, p1scblock);
1461                         delete_run(fr->paras[p1.para], p1run);
1462                 }
1463
1464         } else {
1465                 printf("Deleting the whole non-text SCBlock\n");
1466                 sc_block_delete(&fr->scblocks, p1scblock);
1467         }
1468
1469         /* Delete all the complete SCBlocks in the middle of the range */
1470         if ( !block_is_under(p2scblock, scblock) ) {
1471                 do {
1472
1473                         SCBlock *next;
1474
1475                         /* For each SC block in middle of range: */
1476                         printf("Deleting %p\n", scblock);
1477                         if ( scblock == NULL ) {
1478                                 fprintf(stderr, "nothing?\n");
1479                                 break;
1480                         }
1481                         printf("name is '%s'\n", sc_block_name(scblock));
1482                         if ( (sc_block_name(scblock) != NULL)
1483                           && (strcmp(sc_block_name(scblock), "newpara") == 0) )
1484                         {
1485                                 /* Deleting newpara block, merge the paragraphs */
1486                                 merge_paragraphs_by_newpara(fr, scblock);
1487                                 p2.para--;
1488
1489                         }
1490
1491                         next = sc_block_next(scblock);
1492                         delete_run_for_scblock(fr, fr->paras[p1.para],
1493                                                fr->paras[p2.para], scblock);
1494                         sc_block_delete(&fr->scblocks, scblock);
1495
1496                         scblock = next;
1497
1498                 } while ( !block_is_under(p2scblock, scblock) );
1499         }
1500
1501         /* Last SCBlock in range: delete whole thing or first half */
1502         printf("Last block %p (%s)\n", p2scblock, sc_block_name(p2scblock));
1503         if ( type2 == PARA_TYPE_TEXT ) {
1504                 size_t len;
1505                 printf("  offs %li\n", (long int)p2offs);
1506                 if ( sc_block_contents(p2rscblock) != NULL ) {
1507                         len = strlen(sc_block_contents(p2rscblock));
1508                 } else {
1509                         len = 0;
1510                 }
1511                 printf("  len %li\n", (long int)len);
1512                 if ( (len > 0) && (p2offs == len) ) {
1513                         printf("Deleting the whole text SCBlock\n");
1514                         printf("deleting block %p\n", p2scblock);
1515                         show_sc_block(p2scblock, "");
1516                         sc_block_delete(&fr->scblocks, p2scblock);
1517                         delete_run_for_scblock(fr, fr->paras[p1.para], fr->paras[p2.para], p2scblock);
1518                 } else if ( p2offs > 0 ) {
1519                         printf("Partial delete\n");
1520                         printf("contents '%s'\n", sc_block_contents(p2rscblock));
1521                         printf("up to offs %li\n", (long int)p2offs);
1522                         scblock_delete_text(p2rscblock, 0, p2offs);
1523                 } /* else do nothing */
1524         } else {
1525                 printf("Deleting the whole non-text SCBlock\n");
1526                 sc_block_delete(&fr->scblocks, p2scblock);
1527         }
1528
1529         /* If any paragraphs have been deleted, this will wrap too many
1530          * paragraphs, but it doesn't matter as long as we don't wrap
1531          * past the end of the frame's contents. */
1532         for ( i=p1.para; i<=p2.para; i++ ) {
1533                 if ( i >= fr->n_paras ) break;
1534                 printf("Wrapping para %i (%p)\n", i, fr->paras[i]);
1535                 wrap_paragraph(fr->paras[i], NULL, wrapw, 0, 0);
1536         }
1537         printf("All done.\n");
1538 }
1539
1540
1541 void show_para(Paragraph *p)
1542 {
1543         int i;
1544         printf("Paragraph %p\n", p);
1545
1546         if ( p->type == PARA_TYPE_TEXT ) {
1547
1548                 printf("%i runs:\n", p->n_runs);
1549                 for ( i=0; i<p->n_runs; i++ ) {
1550                         printf("  Run %2i: SCBlock %p %s '%s'\n",
1551                                i, p->runs[i].scblock,
1552                                pango_font_description_to_string(p->runs[i].fontdesc),
1553                                sc_block_contents(p->runs[i].rscblock));
1554                 }
1555
1556         } else if ( p->type == PARA_TYPE_IMAGE ) {
1557                 printf("  Image: %s\n", p->filename);
1558         } else {
1559                 printf("  Other paragraph type\n");
1560         }
1561 }
1562
1563
1564 static SCBlock *split_text_paragraph(struct frame *fr, int pn, size_t pos,
1565                                      PangoContext *pc)
1566 {
1567         Paragraph *pnew;
1568         int i;
1569         size_t run_offs;
1570         int run;
1571         Paragraph *para = fr->paras[pn];
1572         struct text_run *rr;
1573         int bf_pos;
1574
1575         pnew = insert_paragraph(fr, pn);
1576         if ( pnew == NULL ) {
1577                 fprintf(stderr, "Failed to insert paragraph\n");
1578                 return NULL;
1579         }
1580
1581         /* Determine which run the cursor is in */
1582         run = which_run(para, pos);
1583
1584         pnew->type = PARA_TYPE_TEXT;
1585         pnew->open = para->open;
1586         pnew->n_runs = para->n_runs - run;
1587         pnew->runs = malloc(pnew->n_runs * sizeof(struct text_run));
1588         if ( pnew->runs == NULL ) {
1589                 fprintf(stderr, "Failed to allocate runs.\n");
1590                 return NULL; /* Badness is coming */
1591         }
1592
1593         /* Copy spacing */
1594         for ( i=0; i<4; i++ ) pnew->space[i] = para->space[i];
1595
1596         rr = &para->runs[run];
1597         run_offs = pos - get_paragraph_offset(para, run);
1598         printf("split at run %i\n", run);
1599
1600         if ( run_offs == run_text_len(rr) ) {
1601
1602                 /* We are splitting at a run boundary, so things are easy */
1603
1604                 if ( run == para->n_runs-1 ) {
1605
1606                         /* It's actually a paragraph boundary.  Even easier still... */
1607                         printf("splitting at end of para\n");
1608                         pnew->runs[0].scblock = rr->scblock;
1609                         pnew->runs[0].rscblock = rr->rscblock;
1610                         pnew->runs[0].fontdesc = pango_font_description_copy(rr->fontdesc);
1611                         pnew->runs[0].col[0] = rr->col[0];
1612                         pnew->runs[0].col[1] = rr->col[1];
1613                         pnew->runs[0].col[2] = rr->col[2];
1614                         pnew->runs[0].col[3] = rr->col[3];
1615                         pnew->n_runs = 1;
1616
1617
1618                 } else {
1619
1620                         pnew->runs[0] = para->runs[run+1];
1621                         pnew->n_runs = 1;
1622
1623                 }
1624
1625                 /* We start copying runs after the whole second one which we
1626                  * just brought forward, i.e. we start at the THIRD run */
1627                 bf_pos = 2;
1628
1629         } else {
1630
1631                 /* First run of the new paragraph contains the leftover text */
1632                 pnew->runs[0].scblock = rr->scblock;
1633                 pnew->runs[0].rscblock = rr->rscblock;
1634                 pnew->runs[0].fontdesc = pango_font_description_copy(rr->fontdesc);
1635                 pnew->runs[0].col[0] = rr->col[0];
1636                 pnew->runs[0].col[1] = rr->col[1];
1637                 pnew->runs[0].col[2] = rr->col[2];
1638                 pnew->runs[0].col[3] = rr->col[3];
1639                 pnew->n_runs = 1;
1640
1641                 /* We start copying runs at the second run of the paragraph */
1642                 bf_pos = 1;
1643
1644         }
1645
1646         /* All later runs just get moved to the new paragraph */
1647         for ( i=run+bf_pos; i<para->n_runs; i++ ) {
1648                 pnew->runs[pnew->n_runs] = para->runs[i];
1649                 pnew->n_runs++;
1650         }
1651
1652         /* Truncate the first paragraph at the appropriate position */
1653         para->n_runs = run+1;
1654
1655         /* If the first and second paragraphs have the same SCBlock, split it */
1656         if ( (rr->rscblock != NULL) && (rr->rscblock == pnew->runs[0].rscblock) ) {
1657
1658                 printf("splitting SCBlock at %i\n", (int)run_offs);
1659                 printf("old block: '%s'\n", sc_block_contents(rr->rscblock));
1660                 pnew->runs[0].rscblock = sc_block_split(rr->rscblock, run_offs);
1661                 printf("new block 1: '%s'\n", sc_block_contents(rr->rscblock));
1662                 printf("new block 2: '%s'\n", sc_block_contents(pnew->runs[0].rscblock));
1663                 printf("run %p block %p\n", &pnew->runs[0], pnew->runs[0].rscblock);
1664
1665         }
1666
1667         /* Add a \newpara after the end of the first paragraph's SC */
1668         set_newline_at_end(para,
1669                            sc_block_append(rr->scblock, strdup("newpara"),
1670                                            NULL, NULL, NULL));
1671
1672         pnew->open = para->open;
1673         para->open = 0;
1674
1675         wrap_paragraph(para, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0);
1676         wrap_paragraph(pnew, pc, fr->w - fr->pad_l - fr->pad_r, 0, 0);
1677
1678         return sc_block_next(rr->scblock);
1679 }
1680
1681
1682 SCBlock *split_paragraph(struct frame *fr, int pn, size_t pos, PangoContext *pc)
1683 {
1684         Paragraph *para = fr->paras[pn];
1685
1686         if ( para->type == PARA_TYPE_TEXT ) {
1687                 return split_text_paragraph(fr, pn, pos, pc);
1688         } else {
1689                 /* Other types can't be split */
1690                 return NULL;
1691         }
1692 }
1693
1694
1695 SCBlock *block_at_cursor(struct frame *fr, int pn, size_t pos)
1696 {
1697         Paragraph *para = fr->paras[pn];
1698
1699         if ( para->type != PARA_TYPE_CALLBACK ) return NULL;
1700
1701         return para->scblock;
1702 }
1703
1704
1705 int get_sc_pos(struct frame *fr, int pn, size_t pos,
1706                SCBlock **bl, size_t *ppos)
1707 {
1708         Paragraph *para = fr->paras[pn];
1709         int nrun;
1710         struct text_run *run;
1711
1712         nrun = which_run(para, pos);
1713         if ( nrun == para->n_runs ) {
1714                 fprintf(stderr, "Couldn't find run to insert into.\n");
1715                 return 1;
1716         }
1717         run = &para->runs[nrun];
1718
1719         *ppos = pos - get_paragraph_offset(para, nrun);
1720         *bl = run->scblock;
1721
1722         return 0;
1723 }
1724
1725
1726 void set_para_spacing(Paragraph *para, float space[4])
1727 {
1728         if ( para == NULL ) return;
1729         para->space[0] = space[0];
1730         para->space[1] = space[1];
1731         para->space[2] = space[2];
1732         para->space[3] = space[3];
1733 }
1734
1735
1736 Paragraph *current_para(struct frame *fr)
1737 {
1738         if ( fr == NULL ) return NULL;
1739
1740         if ( (fr->paras != NULL) && (fr->paras[fr->n_paras-1]->open) ) {
1741                 return fr->paras[fr->n_paras-1];
1742         }
1743
1744         return NULL;
1745 }
1746
1747 void *get_para_bvp(Paragraph *para)
1748 {
1749         if ( para->type != PARA_TYPE_CALLBACK ) return NULL;
1750         return para->bvp;
1751 }
1752
1753
1754 SCBlock *para_scblock(Paragraph *para)
1755 {
1756         return para->scblock;
1757 }
1758
1759
1760 enum para_type para_type(Paragraph *para)
1761 {
1762         return para->type;
1763 }
1764
1765 int para_debug_num_runs(Paragraph *para)
1766 {
1767         if ( para->type != PARA_TYPE_TEXT ) return 0;
1768         return para->n_runs;
1769 }
1770
1771 int para_debug_run_info(Paragraph *para, int i, SCBlock **scblock)
1772 {
1773         if ( para->type != PARA_TYPE_TEXT ) return 1;
1774         if ( i >= para->n_runs ) return 1;
1775
1776         *scblock = para->runs[i].scblock;
1777         return 0;
1778 }