Subversion Repositories planix.SVN

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 - 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;
82
 
83
	for(b = &f->box[bn]; ; b++){
84
		if(b >= &f->box[f->nbox])
85
			drawerror(f->display, "endofframe");
86
		_frcklinewrap(f, &pt, b);
87
		if(pt.y >= f->r.max.y)
88
			break;
89
		p += NRUNE(b);
90
		_fradvance(f, &pt, b);
91
	}
92
	f->nchars = p;
93
	f->nlines = f->maxlines;
94
	if(b<&f->box[f->nbox])				/* BUG */
95
		_frdelbox(f, (int)(b-f->box), f->nbox-1);
96
}
97
 
98
void
99
frinsert(Frame *f, Rune *sp, Rune *ep, ulong p0)
100
{
101
	Point pt0, pt1, opt0, ppt0, ppt1, pt;
102
	Frbox *b;
103
	int n, n0, nn0, y;
104
	ulong cn0;
105
	Image *col;
106
	Rectangle r;
107
	static struct{
108
		Point pt0, pt1;
109
	}*pts;
110
	static int nalloc=0;
111
	int npts;
112
 
113
	if(p0>f->nchars || sp==ep || f->b==nil)
114
		return;
115
	n0 = _frfindbox(f, 0, 0, p0);
116
	cn0 = p0;
117
	nn0 = n0;
118
	pt0 = _frptofcharnb(f, p0, n0);
119
	ppt0 = pt0;
120
	opt0 = pt0;
121
	pt1 = bxscan(f, sp, ep, &ppt0);
122
	ppt1 = pt1;
123
	if(n0 < f->nbox){
124
		_frcklinewrap(f, &pt0, b = &f->box[n0]);	/* for frdrawsel() */
125
		_frcklinewrap0(f, &ppt1, b);
126
	}
127
	f->modified = 1;
128
	/*
129
	 * ppt0 and ppt1 are start and end of insertion as they will appear when
130
	 * insertion is complete. pt0 is current location of insertion position
131
	 * (p0); pt1 is terminal point (without line wrap) of insertion.
132
	 */
133
	if(f->p0 == f->p1)
134
		frtick(f, frptofchar(f, f->p0), 0);
135
 
136
	/*
137
	 * Find point where old and new x's line up
138
	 * Invariants:
139
	 *	pt0 is where the next box (b, n0) is now
140
	 *	pt1 is where it will be after the insertion
141
	 * If pt1 goes off the rectangle, we can toss everything from there on
142
	 */
143
	for(b = &f->box[n0],npts=0;
144
	     pt1.x!=pt0.x && pt1.y!=f->r.max.y && n0<f->nbox; b++,n0++,npts++){
145
		_frcklinewrap(f, &pt0, b);
146
		_frcklinewrap0(f, &pt1, b);
147
		if(b->nrune > 0){
148
			n = _frcanfit(f, pt1, b);
149
			if(n == 0)
150
				drawerror(f->display, "_frcanfit==0");
151
			if(n != b->nrune){
152
				_frsplitbox(f, n0, n);
153
				b = &f->box[n0];
154
			}
155
		}
156
		if(npts == nalloc){
157
			pts = realloc(pts, (npts+DELTA)*sizeof(pts[0]));
158
			nalloc += DELTA;
159
			b = &f->box[n0];
160
		}
161
		pts[npts].pt0 = pt0;
162
		pts[npts].pt1 = pt1;
163
		/* has a text box overflowed off the frame? */
164
		if(pt1.y == f->r.max.y)
165
			break;
166
		_fradvance(f, &pt0, b);
167
		pt1.x += _frnewwid(f, pt1, b);
168
		cn0 += NRUNE(b);
169
	}
170
	if(pt1.y > f->r.max.y)
171
		drawerror(f->display, "frinsert pt1 too far");
172
	if(pt1.y==f->r.max.y && n0<f->nbox){
173
		f->nchars -= _frstrlen(f, n0);
174
		_frdelbox(f, n0, f->nbox-1);
175
	}
176
	if(n0 == f->nbox)
177
		f->nlines = (pt1.y-f->r.min.y)/f->font->height+(pt1.x>f->r.min.x);
178
	else if(pt1.y!=pt0.y){
179
		int q0, q1;
180
 
181
		y = f->r.max.y;
182
		q0 = pt0.y+f->font->height;
183
		q1 = pt1.y+f->font->height;
184
		f->nlines += (q1-q0)/f->font->height;
185
		if(f->nlines > f->maxlines)
186
			chopframe(f, ppt1, p0, nn0);
187
		if(pt1.y < y){
188
			r = f->r;
189
			r.min.y = q1;
190
			r.max.y = y;
191
			if(q1 < y)
192
				draw(f->b, r, f->b, nil, Pt(f->r.min.x, q0));
193
			r.min = pt1;
194
			r.max.x = pt1.x+(f->r.max.x-pt0.x);
195
			r.max.y = q1;
196
			draw(f->b, r, f->b, nil, pt0);
197
		}
198
	}
199
	/*
200
	 * Move the old stuff down to make room.  The loop will move the stuff
201
	 * between the insertion and the point where the x's lined up.
202
	 * The draw()s above moved everything down after the point they lined up.
203
	 */
204
	for((y=pt1.y==f->r.max.y?pt1.y:0),b = &f->box[n0-1]; --npts>=0; --b){
205
		pt = pts[npts].pt1;
206
		if(b->nrune > 0){
207
			r.min = pt;
208
			r.max = r.min;
209
			r.max.x += b->wid;
210
			r.max.y += f->font->height;
211
			draw(f->b, r, f->b, nil, pts[npts].pt0);
212
			/* clear bit hanging off right */
213
			if(npts==0 && pt.y>pt0.y){
214
				/*
215
				 * first new char is bigger than first char we're
216
				 * displacing, causing line wrap. ugly special case.
217
				 */
218
				r.min = opt0;
219
				r.max = opt0;
220
				r.max.x = f->r.max.x;
221
				r.max.y += f->font->height;
222
				if(f->p0<=cn0 && cn0<f->p1)	/* b+1 is inside selection */
223
					col = f->cols[HIGH];
224
				else
225
					col = f->cols[BACK];
226
				draw(f->b, r, col, nil, r.min);
227
			}else if(pt.y < y){
228
				r.min = pt;
229
				r.max = pt;
230
				r.min.x += b->wid;
231
				r.max.x = f->r.max.x;
232
				r.max.y += f->font->height;
233
				if(f->p0<=cn0 && cn0<f->p1)	/* b+1 is inside selection */
234
					col = f->cols[HIGH];
235
				else
236
					col = f->cols[BACK];
237
				draw(f->b, r, col, nil, r.min);
238
			}
239
			y = pt.y;
240
			cn0 -= b->nrune;
241
		}else{
242
			r.min = pt;
243
			r.max = pt;
244
			r.max.x += b->wid;
245
			r.max.y += f->font->height;
246
			if(r.max.x >= f->r.max.x)
247
				r.max.x = f->r.max.x;
248
			cn0--;
249
			if(f->p0<=cn0 && cn0<f->p1)	/* b is inside selection */
250
				col = f->cols[HIGH];
251
			else
252
				col = f->cols[BACK];
253
			draw(f->b, r, col, nil, r.min);
254
			y = 0;
255
			if(pt.x == f->r.min.x)
256
				y = pt.y;
257
		}
258
	}
259
	/* insertion can extend the selection, so the condition here is different */
260
	if(f->p0<p0 && p0<=f->p1)
261
		col = f->cols[HIGH];
262
	else
263
		col = f->cols[BACK];
264
	frselectpaint(f, ppt0, ppt1, col);
265
	_frdrawtext(&frame, ppt0, f->cols[TEXT], col);
266
	_fraddbox(f, nn0, frame.nbox);
267
	for(n=0; n<frame.nbox; n++)
268
		f->box[nn0+n] = frame.box[n];
269
	if(nn0>0 && f->box[nn0-1].nrune>=0 && ppt0.x-f->box[nn0-1].wid>=f->r.min.x){
270
		--nn0;
271
		ppt0.x -= f->box[nn0].wid;
272
	}
273
	n0 += frame.nbox;
274
	_frclean(f, ppt0, nn0, n0<f->nbox-1? n0+1 : n0);
275
	f->nchars += frame.nchars;
276
	if(f->p0 >= p0)
277
		f->p0 += frame.nchars;
278
	if(f->p0 > f->nchars)
279
		f->p0 = f->nchars;
280
	if(f->p1 >= p0)
281
		f->p1 += frame.nchars;
282
	if(f->p1 > f->nchars)
283
		f->p1 = f->nchars;
284
	if(f->p0 == f->p1)
285
		frtick(f, frptofchar(f, f->p0), 1);
286
}