Blob


1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <mouse.h>
5 #include <frame.h>
7 #define DELTA 25
8 #define TMPSIZE 256
9 static Frame frame;
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;
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 }
76 static
77 void
78 chopframe(Frame *f, Point pt, ulong p, int bn)
79 {
80 Frbox *b;
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 }
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;
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);
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);
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];
155 if(npts == nalloc){
156 pts = realloc(pts, (npts+DELTA)*sizeof(pts[0]));
157 nalloc += DELTA;
158 b = &f->box[n0];
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);
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);
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;
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);
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);
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];
255 draw(f->b, r, col, nil, r.min);
256 y = 0;
257 if(pt.x == f->r.min.x)
258 y = pt.y;
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];
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;
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);