]> git.lizzy.rs Git - plan9front.git/blob - sys/src/libframe/frinsert.c
vt: implement snarf support
[plan9front.git] / sys / src / libframe / frinsert.c
1 #include <u.h>
2 #include <libc.h>
3 #include <draw.h>
4 #include <thread.h>
5 #include <mouse.h>
6 #include <frame.h>
7
8 #define DELTA   25
9 #define TMPSIZE 256
10 static Frame            frame;
11
12 static
13 Point
14 bxscan(Frame *f, Rune *sp, Rune *ep, Point *ppt)
15 {
16         int w, c, nb, delta, nl, nr, rw;
17         Frbox *b;
18         char *s, tmp[TMPSIZE+3];        /* +3 for rune overflow */
19         uchar *p;
20
21         frame.r = f->r;
22         frame.b = f->b;
23         frame.font = f->font;
24         frame.maxtab = f->maxtab;
25         frame.nbox = 0;
26         frame.nchars = 0;
27         memmove(frame.cols, f->cols, sizeof frame.cols);
28         delta = DELTA;
29         nl = 0;
30         for(nb=0; sp<ep && nl<=f->maxlines; nb++,frame.nbox++){
31                 if(nb == frame.nalloc){
32                         _frgrowbox(&frame, delta);
33                         if(delta < 10000)
34                                 delta *= 2;
35                 }
36                 b = &frame.box[nb];
37                 c = *sp;
38                 if(c=='\t' || c=='\n'){
39                         b->bc = c;
40                         b->wid = 5000;
41                         b->minwid = (c=='\n')? 0 : stringwidth(frame.font, " ");
42                         b->nrune = -1;
43                         if(c=='\n')
44                                 nl++;
45                         frame.nchars++;
46                         sp++;
47                 }else{
48                         s = tmp;
49                         nr = 0;
50                         w = 0;
51                         while(sp < ep){
52                                 c = *sp;
53                                 if(c=='\t' || c=='\n')
54                                         break;
55                                 rw = runetochar(s, sp);
56                                 if(s+rw >= tmp+TMPSIZE)
57                                         break;
58                                 w += runestringnwidth(frame.font, sp, 1);
59                                 sp++;
60                                 s += rw;
61                                 nr++;
62                         }
63                         *s++ = 0;
64                         p = _frallocstr(f, s-tmp);
65                         b = &frame.box[nb];
66                         b->ptr = p;
67                         memmove(p, tmp, s-tmp);
68                         b->wid = w;
69                         b->nrune = nr;
70                         frame.nchars += nr;
71                 }
72         }
73         _frcklinewrap0(f, ppt, &frame.box[0]);
74         return _frdraw(&frame, *ppt);
75 }
76
77 static
78 void
79 chopframe(Frame *f, Point pt, ulong p, int bn)
80 {
81         Frbox *b, *eb;
82
83         eb = &f->box[f->nbox];
84         for(b = &f->box[bn]; b < eb; b++){
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 < eb)                              /* 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 *back, *text;
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                                         back = f->cols[HIGH];
223                                 else
224                                         back = f->cols[BACK];
225                                 draw(f->b, r, back, 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                                         back = f->cols[HIGH];
234                                 else
235                                         back = f->cols[BACK];
236                                 draw(f->b, r, back, 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                                 back = f->cols[HIGH];
250                         else
251                                 back = f->cols[BACK];
252                         draw(f->b, r, back, nil, r.min);
253                         y = 0;
254                         if(pt.x == f->r.min.x)
255                                 y = pt.y;
256                 }
257         }
258         /* insertion can extend the selection, so the condition here is different */
259         if(f->p0<p0 && p0<=f->p1){
260                 text = f->cols[HTEXT];
261                 back = f->cols[HIGH];
262         }
263         else {
264                 text = f->cols[TEXT];
265                 back = f->cols[BACK];
266         }
267         frselectpaint(f, ppt0, ppt1, back);
268         _frdrawtext(&frame, ppt0, text, back);
269         _fraddbox(f, nn0, frame.nbox);
270         for(n=0; n<frame.nbox; n++)
271                 f->box[nn0+n] = frame.box[n];
272         if(nn0>0 && f->box[nn0-1].nrune>=0 && ppt0.x-f->box[nn0-1].wid>=f->r.min.x){
273                 --nn0;
274                 ppt0.x -= f->box[nn0].wid;
275         }
276         n0 += frame.nbox;
277         _frclean(f, ppt0, nn0, n0<f->nbox-1? n0+1 : n0);
278         f->nchars += frame.nchars;
279         if(f->p0 >= p0)
280                 f->p0 += frame.nchars;
281         if(f->p0 > f->nchars)
282                 f->p0 = f->nchars;
283         if(f->p1 >= p0)
284                 f->p1 += frame.nchars;
285         if(f->p1 > f->nchars)
286                 f->p1 = f->nchars;
287         if(f->p0 == f->p1)
288                 frtick(f, frptofchar(f, f->p0), 1);
289 }