frinsert.c (6816B)
1 #include <u.h> 2 #include <libc.h> 3 #include <draw.h> 4 #include <mouse.h> 5 #include <frame.h> 6 7 #define DELTA 25 8 #define TMPSIZE 256 9 static Frame frame; 10 11 static 12 Point 13 bxscan(Frame *f, Rune *sp, Rune *ep, Point *ppt) 14 { 15 int w, c, nb, delta, nl, nr, rw; 16 Frbox *b; 17 char *s, tmp[TMPSIZE+3]; /* +3 for rune overflow */ 18 uchar *p; 19 20 frame.r = f->r; 21 frame.b = f->b; 22 frame.font = f->font; 23 frame.maxtab = f->maxtab; 24 frame.nbox = 0; 25 frame.nchars = 0; 26 memmove(frame.cols, f->cols, sizeof frame.cols); 27 delta = DELTA; 28 nl = 0; 29 for(nb=0; sp<ep && nl<=f->maxlines; nb++,frame.nbox++){ 30 if(nb == frame.nalloc){ 31 _frgrowbox(&frame, delta); 32 if(delta < 10000) 33 delta *= 2; 34 } 35 b = &frame.box[nb]; 36 c = *sp; 37 if(c=='\t' || c=='\n'){ 38 b->bc = c; 39 b->wid = 5000; 40 b->minwid = (c=='\n')? 0 : stringwidth(frame.font, " "); 41 b->nrune = -1; 42 if(c=='\n') 43 nl++; 44 frame.nchars++; 45 sp++; 46 }else{ 47 s = tmp; 48 nr = 0; 49 w = 0; 50 while(sp < ep){ 51 c = *sp; 52 if(c=='\t' || c=='\n') 53 break; 54 rw = runetochar(s, sp); 55 if(s+rw >= tmp+TMPSIZE) 56 break; 57 w += runestringnwidth(frame.font, sp, 1); 58 sp++; 59 s += rw; 60 nr++; 61 } 62 *s++ = 0; 63 p = _frallocstr(f, s-tmp); 64 b = &frame.box[nb]; 65 b->ptr = p; 66 memmove(p, tmp, s-tmp); 67 b->wid = w; 68 b->nrune = nr; 69 frame.nchars += nr; 70 } 71 } 72 _frcklinewrap0(f, ppt, &frame.box[0]); 73 return _frdraw(&frame, *ppt); 74 } 75 76 static 77 void 78 chopframe(Frame *f, Point pt, ulong p, int bn) 79 { 80 Frbox *b; 81 82 for(b = &f->box[bn]; ; b++){ 83 if(b >= &f->box[f->nbox]) 84 drawerror(f->display, "endofframe"); 85 _frcklinewrap(f, &pt, b); 86 if(pt.y >= f->r.max.y) 87 break; 88 p += NRUNE(b); 89 _fradvance(f, &pt, b); 90 } 91 f->nchars = p; 92 f->nlines = f->maxlines; 93 if(b<&f->box[f->nbox]) /* BUG */ 94 _frdelbox(f, (int)(b-f->box), f->nbox-1); 95 } 96 97 void 98 frinsert(Frame *f, Rune *sp, Rune *ep, ulong p0) 99 { 100 Point pt0, pt1, opt0, ppt0, ppt1, pt; 101 Frbox *b; 102 int n, n0, nn0, y; 103 ulong cn0; 104 Image *col, *tcol; 105 Rectangle r; 106 static struct{ 107 Point pt0, pt1; 108 }*pts; 109 static int nalloc=0; 110 int npts; 111 112 if(p0>f->nchars || sp==ep || f->b==nil) 113 return; 114 n0 = _frfindbox(f, 0, 0, p0); 115 cn0 = p0; 116 nn0 = n0; 117 pt0 = _frptofcharnb(f, p0, n0); 118 ppt0 = pt0; 119 opt0 = pt0; 120 pt1 = bxscan(f, sp, ep, &ppt0); 121 ppt1 = pt1; 122 if(n0 < f->nbox){ 123 _frcklinewrap(f, &pt0, b = &f->box[n0]); /* for frdrawsel() */ 124 _frcklinewrap0(f, &ppt1, b); 125 } 126 f->modified = 1; 127 /* 128 * ppt0 and ppt1 are start and end of insertion as they will appear when 129 * insertion is complete. pt0 is current location of insertion position 130 * (p0); pt1 is terminal point (without line wrap) of insertion. 131 */ 132 if(f->p0 == f->p1) 133 frtick(f, frptofchar(f, f->p0), 0); 134 135 /* 136 * Find point where old and new x's line up 137 * Invariants: 138 * pt0 is where the next box (b, n0) is now 139 * pt1 is where it will be after the insertion 140 * If pt1 goes off the rectangle, we can toss everything from there on 141 */ 142 for(b = &f->box[n0],npts=0; 143 pt1.x!=pt0.x && pt1.y!=f->r.max.y && n0<f->nbox; b++,n0++,npts++){ 144 _frcklinewrap(f, &pt0, b); 145 _frcklinewrap0(f, &pt1, b); 146 if(b->nrune > 0){ 147 n = _frcanfit(f, pt1, b); 148 if(n == 0) 149 drawerror(f->display, "_frcanfit==0"); 150 if(n != b->nrune){ 151 _frsplitbox(f, n0, n); 152 b = &f->box[n0]; 153 } 154 } 155 if(npts == nalloc){ 156 pts = realloc(pts, (npts+DELTA)*sizeof(pts[0])); 157 nalloc += DELTA; 158 b = &f->box[n0]; 159 } 160 pts[npts].pt0 = pt0; 161 pts[npts].pt1 = pt1; 162 /* has a text box overflowed off the frame? */ 163 if(pt1.y == f->r.max.y) 164 break; 165 _fradvance(f, &pt0, b); 166 pt1.x += _frnewwid(f, pt1, b); 167 cn0 += NRUNE(b); 168 } 169 if(pt1.y > f->r.max.y) 170 drawerror(f->display, "frinsert pt1 too far"); 171 if(pt1.y==f->r.max.y && n0<f->nbox){ 172 f->nchars -= _frstrlen(f, n0); 173 _frdelbox(f, n0, f->nbox-1); 174 } 175 if(n0 == f->nbox) 176 f->nlines = (pt1.y-f->r.min.y)/f->font->height+(pt1.x>f->r.min.x); 177 else if(pt1.y!=pt0.y){ 178 int q0, q1; 179 180 y = f->r.max.y; 181 q0 = pt0.y+f->font->height; 182 q1 = pt1.y+f->font->height; 183 f->nlines += (q1-q0)/f->font->height; 184 if(f->nlines > f->maxlines) 185 chopframe(f, ppt1, p0, nn0); 186 if(pt1.y < y){ 187 r = f->r; 188 r.min.y = q1; 189 r.max.y = y; 190 if(q1 < y) 191 draw(f->b, r, f->b, nil, Pt(f->r.min.x, q0)); 192 r.min = pt1; 193 r.max.x = pt1.x+(f->r.max.x-pt0.x); 194 r.max.y = q1; 195 draw(f->b, r, f->b, nil, pt0); 196 } 197 } 198 /* 199 * Move the old stuff down to make room. The loop will move the stuff 200 * between the insertion and the point where the x's lined up. 201 * The draw()s above moved everything down after the point they lined up. 202 */ 203 for((y=pt1.y==f->r.max.y?pt1.y:0),b = &f->box[n0-1]; --npts>=0; --b){ 204 pt = pts[npts].pt1; 205 if(b->nrune > 0){ 206 r.min = pt; 207 r.max = r.min; 208 r.max.x += b->wid; 209 r.max.y += f->font->height; 210 draw(f->b, r, f->b, nil, pts[npts].pt0); 211 /* clear bit hanging off right */ 212 if(npts==0 && pt.y>pt0.y){ 213 /* 214 * first new char is bigger than first char we're 215 * displacing, causing line wrap. ugly special case. 216 */ 217 r.min = opt0; 218 r.max = opt0; 219 r.max.x = f->r.max.x; 220 r.max.y += f->font->height; 221 if(f->p0<=cn0 && cn0<f->p1) /* b+1 is inside selection */ 222 col = f->cols[HIGH]; 223 else 224 col = f->cols[BACK]; 225 draw(f->b, r, col, nil, r.min); 226 }else if(pt.y < y){ 227 r.min = pt; 228 r.max = pt; 229 r.min.x += b->wid; 230 r.max.x = f->r.max.x; 231 r.max.y += f->font->height; 232 if(f->p0<=cn0 && cn0<f->p1) /* b+1 is inside selection */ 233 col = f->cols[HIGH]; 234 else 235 col = f->cols[BACK]; 236 draw(f->b, r, col, nil, r.min); 237 } 238 y = pt.y; 239 cn0 -= b->nrune; 240 }else{ 241 r.min = pt; 242 r.max = pt; 243 r.max.x += b->wid; 244 r.max.y += f->font->height; 245 if(r.max.x >= f->r.max.x) 246 r.max.x = f->r.max.x; 247 cn0--; 248 if(f->p0<=cn0 && cn0<f->p1){ /* b is inside selection */ 249 col = f->cols[HIGH]; 250 tcol = f->cols[HTEXT]; 251 }else{ 252 col = f->cols[BACK]; 253 tcol = f->cols[TEXT]; 254 } 255 draw(f->b, r, col, nil, r.min); 256 y = 0; 257 if(pt.x == f->r.min.x) 258 y = pt.y; 259 } 260 } 261 /* insertion can extend the selection, so the condition here is different */ 262 if(f->p0<p0 && p0<=f->p1){ 263 col = f->cols[HIGH]; 264 tcol = f->cols[HTEXT]; 265 }else{ 266 col = f->cols[BACK]; 267 tcol = f->cols[TEXT]; 268 } 269 frselectpaint(f, ppt0, ppt1, col); 270 _frdrawtext(&frame, ppt0, tcol, col); 271 _fraddbox(f, nn0, frame.nbox); 272 for(n=0; n<frame.nbox; n++) 273 f->box[nn0+n] = frame.box[n]; 274 if(nn0>0 && f->box[nn0-1].nrune>=0 && ppt0.x-f->box[nn0-1].wid>=f->r.min.x){ 275 --nn0; 276 ppt0.x -= f->box[nn0].wid; 277 } 278 n0 += frame.nbox; 279 _frclean(f, ppt0, nn0, n0<f->nbox-1? n0+1 : n0); 280 f->nchars += frame.nchars; 281 if(f->p0 >= p0) 282 f->p0 += frame.nchars; 283 if(f->p0 > f->nchars) 284 f->p0 = f->nchars; 285 if(f->p1 >= p0) 286 f->p1 += frame.nchars; 287 if(f->p1 > f->nchars) 288 f->p1 = f->nchars; 289 if(f->p0 == f->p1) 290 frtick(f, frptofchar(f, f->p0), 1); 291 }