Subversion Repositories planix.SVN

Rev

Rev 2 | Details | Compare with Previous | 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 <cursor.h>
6
#include <mouse.h>
7
#include <keyboard.h>
8
#include <frame.h>
9
#include <fcall.h>
10
#include <plumb.h>
11
#include "dat.h"
12
#include "fns.h"
13
 
14
int	winid;
15
 
16
void
17
wininit(Window *w, Window *clone, Rectangle r)
18
{
19
	Rectangle r1, br;
20
	File *f;
21
	Reffont *rf;
22
	Rune *rp;
23
	int nc;
24
 
25
	w->tag.w = w;
26
	w->body.w = w;
27
	w->id = ++winid;
28
	incref(w);
29
	if(globalincref)
30
		incref(w);
31
	w->ctlfid = ~0;
32
	w->utflastqid = -1;
33
	r1 = r;
34
	r1.max.y = r1.min.y + font->height;
35
	incref(&reffont);
36
	f = fileaddtext(nil, &w->tag);
37
	textinit(&w->tag, f, r1, &reffont, tagcols);
38
	w->tag.what = Tag;
39
	/* tag is a copy of the contents, not a tracked image */
40
	if(clone){
41
		textdelete(&w->tag, 0, w->tag.file->nc, TRUE);
42
		nc = clone->tag.file->nc;
43
		rp = runemalloc(nc);
44
		bufread(clone->tag.file, 0, rp, nc);
45
		textinsert(&w->tag, 0, rp, nc, TRUE);
46
		free(rp);
47
		filereset(w->tag.file);
48
		textsetselect(&w->tag, nc, nc);
49
	}
50
	r1 = r;
51
	r1.min.y += font->height + 1;
52
	if(r1.max.y < r1.min.y)
53
		r1.max.y = r1.min.y;
54
	f = nil;
55
	if(clone){
56
		f = clone->body.file;
57
		w->body.org = clone->body.org;
58
		w->isscratch = clone->isscratch;
59
		rf = rfget(FALSE, FALSE, FALSE, clone->body.reffont->f->name);
60
	}else
61
		rf = rfget(FALSE, FALSE, FALSE, nil);
62
	f = fileaddtext(f, &w->body);
63
	w->body.what = Body;
64
	textinit(&w->body, f, r1, rf, textcols);
65
	r1.min.y -= 1;
66
	r1.max.y = r1.min.y+1;
67
	draw(screen, r1, tagcols[BORD], nil, ZP);
68
	textscrdraw(&w->body);
69
	w->r = r;
70
	w->r.max.y = w->body.r.max.y;
71
	br.min = w->tag.scrollr.min;
72
	br.max.x = br.min.x + Dx(button->r);
73
	br.max.y = br.min.y + Dy(button->r);
74
	draw(screen, br, button, nil, button->r.min);
75
	w->filemenu = TRUE;
76
	w->maxlines = w->body.maxlines;
77
	w->autoindent = globalautoindent;
78
	if(clone){
79
		w->dirty = clone->dirty;
80
		textsetselect(&w->body, clone->body.q0, clone->body.q1);
81
		winsettag(w);
82
		w->autoindent = clone->autoindent;
83
	}
84
}
85
 
86
int
87
delrunepos(Window *w)
88
{
89
	int n;
90
	Rune rune;
91
 
92
	for(n=0; n<w->tag.file->nc; n++) {
93
		bufread(w->tag.file, n, &rune, 1);
94
		if(rune == ' ')
95
			break;
96
	}
97
	n += 2;
98
	if(n >= w->tag.file->nc)
99
		return -1;
100
	return n;
101
}
102
 
103
void
104
movetodel(Window *w)
105
{
106
	int n;
107
 
108
	n = delrunepos(w);
109
	if(n < 0)
110
		return;
111
	moveto(mousectl, addpt(frptofchar(&w->tag, n), Pt(4, w->tag.font->height-4)));
112
}
113
 
114
int
115
winresize(Window *w, Rectangle r, int safe)
116
{
117
	Rectangle r1;
118
	int y;
119
	Image *b;
120
	Rectangle br;
121
 
122
	r1 = r;
123
	r1.max.y = r1.min.y + font->height;
124
	y = r1.max.y;
125
	if(!safe || !eqrect(w->tag.r, r1)){
126
		y = textresize(&w->tag, r1);
127
		b = button;
128
		if(w->body.file->mod && !w->isdir && !w->isscratch)
129
			b = modbutton;
130
		br.min = w->tag.scrollr.min;
131
		br.max.x = br.min.x + Dx(b->r);
132
		br.max.y = br.min.y + Dy(b->r);
133
		draw(screen, br, b, nil, b->r.min);
134
	}
135
	if(!safe || !eqrect(w->body.r, r1)){
136
		if(y+1+font->height > r.max.y){		/* no body */
137
			r1.min.y = y;
138
			r1.max.y = y;
139
			textresize(&w->body, r1);
140
			w->r = r;
141
			w->r.max.y = y;
142
			return y;
143
		}
144
		r1 = r;
145
		r1.min.y = y;
146
		r1.max.y = y + 1;
147
		draw(screen, r1, tagcols[BORD], nil, ZP);
148
		r1.min.y = y + 1;
149
		r1.max.y = r.max.y;
150
		y = textresize(&w->body, r1);
151
		w->r = r;
152
		w->r.max.y = y;
153
		textscrdraw(&w->body);
154
	}
155
	w->maxlines = min(w->body.nlines, max(w->maxlines, w->body.maxlines));
156
	return w->r.max.y;
157
}
158
 
159
void
160
winlock1(Window *w, int owner)
161
{
162
	incref(w);
163
	qlock(w);
164
	w->owner = owner;
165
}
166
 
167
void
168
winlock(Window *w, int owner)
169
{
170
	int i;
171
	File *f;
172
 
173
	f = w->body.file;
174
	for(i=0; i<f->ntext; i++)
175
		winlock1(f->text[i]->w, owner);
176
}
177
 
178
void
179
winunlock(Window *w)
180
{
181
	int i;
182
	File *f;
183
 
184
	/*
185
	 * subtle: loop runs backwards to avoid tripping over
186
	 * winclose indirectly editing f->text and freeing f
187
	 * on the last iteration of the loop.
188
	 */
189
	f = w->body.file;
190
	for(i=f->ntext-1; i>=0; i--){
191
		w = f->text[i]->w;
192
		w->owner = 0;
193
		qunlock(w);
194
		winclose(w);
195
	}
196
}
197
 
198
void
199
winmousebut(Window *w)
200
{
201
	moveto(mousectl, divpt(addpt(w->tag.scrollr.min, w->tag.scrollr.max), 2));
202
}
203
 
204
void
205
windirfree(Window *w)
206
{
207
	int i;
208
	Dirlist *dl;
209
 
210
	if(w->isdir){
211
		for(i=0; i<w->ndl; i++){
212
			dl = w->dlp[i];
213
			free(dl->r);
214
			free(dl);
215
		}
216
		free(w->dlp);
217
	}
218
	w->dlp = nil;
219
	w->ndl = 0;
220
}
221
 
222
void
223
winclose(Window *w)
224
{
225
	int i;
226
 
227
	if(decref(w) == 0){
228
		windirfree(w);
229
		textclose(&w->tag);
230
		textclose(&w->body);
231
		if(activewin == w)
232
			activewin = nil;
233
		for(i=0; i<w->nincl; i++)
234
			free(w->incl[i]);
235
		free(w->incl);
236
		free(w->events);
237
		free(w);
238
	}
239
}
240
 
241
void
242
windelete(Window *w)
243
{
244
	Xfid *x;
245
 
246
	x = w->eventx;
247
	if(x){
248
		w->nevents = 0;
249
		free(w->events);
250
		w->events = nil;
251
		w->eventx = nil;
252
		sendp(x->c, nil);	/* wake him up */
253
	}
254
}
255
 
256
void
257
winundo(Window *w, int isundo)
258
{
259
	Text *body;
260
	int i;
261
	File *f;
262
	Window *v;
263
 
264
	w->utflastqid = -1;
265
	body = &w->body;
266
	fileundo(body->file, isundo, &body->q0, &body->q1);
267
	textshow(body, body->q0, body->q1, 1);
268
	f = body->file;
269
	for(i=0; i<f->ntext; i++){
270
		v = f->text[i]->w;
271
		v->dirty = (f->seq != v->putseq);
272
		if(v != w){
273
			v->body.q0 = v->body.p0+v->body.org;
274
			v->body.q1 = v->body.p1+v->body.org;
275
		}
276
	}
277
	winsettag(w);
278
}
279
 
280
void
281
winsetname(Window *w, Rune *name, int n)
282
{
283
	Text *t;
284
	Window *v;
285
	int i;
286
 
287
	t = &w->body;
288
	if(runeeq(t->file->name, t->file->nname, name, n) == TRUE)
289
		return;
290
	w->isscratch = FALSE;
291
	if(n>=6 && runeeq(L"/guide", 6, name+(n-6), 6))
292
		w->isscratch = TRUE;
293
	else if(n>=7 && runeeq(L"+Errors", 7, name+(n-7), 7))
294
		w->isscratch = TRUE;
295
	filesetname(t->file, name, n);
296
	for(i=0; i<t->file->ntext; i++){
297
		v = t->file->text[i]->w;
298
		winsettag(v);
299
		v->isscratch = w->isscratch;
300
	}
301
}
302
 
303
void
304
wintype(Window *w, Text *t, Rune r)
305
{
306
	int i;
307
 
308
	texttype(t, r);
309
	if(t->what == Body)
310
		for(i=0; i<t->file->ntext; i++)
311
			textscrdraw(t->file->text[i]);
312
	winsettag(w);
313
}
314
 
315
void
316
wincleartag(Window *w)
317
{
318
	int i, n;
319
	Rune *r;
320
 
321
	/* w must be committed */
322
	n = w->tag.file->nc;
323
	r = runemalloc(n);
324
	bufread(w->tag.file, 0, r, n);
325
	for(i=0; i<n; i++)
326
		if(r[i]==' ' || r[i]=='\t')
327
			break;
328
	for(; i<n; i++)
329
		if(r[i] == '|')
330
			break;
331
	if(i == n)
332
		return;
333
	i++;
334
	textdelete(&w->tag, i, n, TRUE);
335
	free(r);
336
	w->tag.file->mod = FALSE;
337
	if(w->tag.q0 > i)
338
		w->tag.q0 = i;
339
	if(w->tag.q1 > i)
340
		w->tag.q1 = i;
341
	textsetselect(&w->tag, w->tag.q0, w->tag.q1);
342
}
343
 
344
void
345
winsettag1(Window *w)
346
{
347
	int i, j, k, n, bar, dirty;
348
	Rune *new, *old, *r;
349
	Image *b;
350
	uint q0, q1;
351
	Rectangle br;
352
 
353
	/* there are races that get us here with stuff in the tag cache, so we take extra care to sync it */
354
	if(w->tag.ncache!=0 || w->tag.file->mod)
355
		wincommit(w, &w->tag);	/* check file name; also guarantees we can modify tag contents */
356
	old = runemalloc(w->tag.file->nc+1);
357
	bufread(w->tag.file, 0, old, w->tag.file->nc);
358
	old[w->tag.file->nc] = '\0';
359
	for(i=0; i<w->tag.file->nc; i++)
360
		if(old[i]==' ' || old[i]=='\t')
361
			break;
362
	if(runeeq(old, i, w->body.file->name, w->body.file->nname) == FALSE){
363
		textdelete(&w->tag, 0, i, TRUE);
364
		textinsert(&w->tag, 0, w->body.file->name, w->body.file->nname, TRUE);
365
		free(old);
366
		old = runemalloc(w->tag.file->nc+1);
367
		bufread(w->tag.file, 0, old, w->tag.file->nc);
368
		old[w->tag.file->nc] = '\0';
369
	}
370
	new = runemalloc(w->body.file->nname+100);
371
	i = 0;
372
	runemove(new+i, w->body.file->name, w->body.file->nname);
373
	i += w->body.file->nname;
374
	runemove(new+i, L" Del Snarf", 10);
375
	i += 10;
376
	if(w->filemenu){
377
		if(w->body.file->delta.nc>0 || w->body.ncache){
378
			runemove(new+i, L" Undo", 5);
379
			i += 5;
380
		}
381
		if(w->body.file->epsilon.nc > 0){
382
			runemove(new+i, L" Redo", 5);
383
			i += 5;
384
		}
385
		dirty = w->body.file->nname && (w->body.ncache || w->body.file->seq!=w->putseq);
386
		if(!w->isdir && dirty){
387
			runemove(new+i, L" Put", 4);
388
			i += 4;
389
		}
390
	}
391
	if(w->isdir){
392
		runemove(new+i, L" Get", 4);
393
		i += 4;
394
	}
395
	runemove(new+i, L" |", 2);
396
	i += 2;
397
	r = runestrchr(old, '|');
398
	if(r)
399
		k = r-old+1;
400
	else{
401
		k = w->tag.file->nc;
402
		if(w->body.file->seq == 0){
403
			runemove(new+i, L" Look ", 6);
404
			i += 6;
405
		}
406
	}
407
	if(runeeq(new, i, old, k) == FALSE){
408
		n = k;
409
		if(n > i)
410
			n = i;
411
		for(j=0; j<n; j++)
412
			if(old[j] != new[j])
413
				break;
414
		q0 = w->tag.q0;
415
		q1 = w->tag.q1;
416
		textdelete(&w->tag, j, k, TRUE);
417
		textinsert(&w->tag, j, new+j, i-j, TRUE);
418
		/* try to preserve user selection */
419
		r = runestrchr(old, '|');
420
		if(r){
421
			bar = r-old;
422
			if(q0 > bar){
423
				bar = (runestrchr(new, '|')-new)-bar;
424
				w->tag.q0 = q0+bar;
425
				w->tag.q1 = q1+bar;
426
			}
427
		}
428
	}
429
	free(old);
430
	free(new);
431
	w->tag.file->mod = FALSE;
432
	n = w->tag.file->nc+w->tag.ncache;
433
	if(w->tag.q0 > n)
434
		w->tag.q0 = n;
435
	if(w->tag.q1 > n)
436
		w->tag.q1 = n;
437
	textsetselect(&w->tag, w->tag.q0, w->tag.q1);
438
	b = button;
439
	if(!w->isdir && !w->isscratch && (w->body.file->mod || w->body.ncache))
440
		b = modbutton;
441
	br.min = w->tag.scrollr.min;
442
	br.max.x = br.min.x + Dx(b->r);
443
	br.max.y = br.min.y + Dy(b->r);
444
	draw(screen, br, b, nil, b->r.min);
445
}
446
 
447
void
448
winsettag(Window *w)
449
{
450
	int i;
451
	File *f;
452
	Window *v;
453
 
454
	f = w->body.file;
455
	for(i=0; i<f->ntext; i++){
456
		v = f->text[i]->w;
457
		if(v->col->safe || v->body.maxlines>0)
458
			winsettag1(v);
459
	}
460
}
461
 
462
void
463
wincommit(Window *w, Text *t)
464
{
465
	Rune *r;
466
	int i;
467
	File *f;
468
 
469
	textcommit(t, TRUE);
470
	f = t->file;
471
	if(f->ntext > 1)
472
		for(i=0; i<f->ntext; i++)
473
			textcommit(f->text[i], FALSE);	/* no-op for t */
474
	if(t->what == Body)
475
		return;
476
	r = runemalloc(w->tag.file->nc);
477
	bufread(w->tag.file, 0, r, w->tag.file->nc);
478
	for(i=0; i<w->tag.file->nc; i++)
479
		if(r[i]==' ' || r[i]=='\t')
480
			break;
481
	if(runeeq(r, i, w->body.file->name, w->body.file->nname) == FALSE){
482
		seq++;
483
		filemark(w->body.file);
484
		w->body.file->mod = TRUE;
485
		w->dirty = TRUE;
486
		winsetname(w, r, i);
487
		winsettag(w);
488
	}
489
	free(r);
490
}
491
 
492
void
493
winaddincl(Window *w, Rune *r, int n)
494
{
495
	char *a;
496
	Dir *d;
497
	Runestr rs;
498
 
499
	a = runetobyte(r, n);
500
	d = dirstat(a);
501
	if(d == nil){
502
		if(a[0] == '/')
503
			goto Rescue;
504
		rs = dirname(&w->body, r, n);
505
		r = rs.r;
506
		n = rs.nr;
507
		free(a);
508
		a = runetobyte(r, n);
509
		d = dirstat(a);
510
		if(d == nil)
511
			goto Rescue;
512
		r = runerealloc(r, n+1);
513
		r[n] = 0;
514
	}
515
	free(a);
516
	if((d->qid.type&QTDIR) == 0){
517
		free(d);
518
		warning(nil, "%s: not a directory\n", a);
519
		free(r);
520
		return;
521
	}
522
	free(d);
523
	w->nincl++;
524
	w->incl = realloc(w->incl, w->nincl*sizeof(Rune*));
525
	memmove(w->incl+1, w->incl, (w->nincl-1)*sizeof(Rune*));
526
	w->incl[0] = runemalloc(n+1);
527
	runemove(w->incl[0], r, n);
528
	free(r);
529
	return;
530
 
531
Rescue:
532
	warning(nil, "%s: %r\n", a);
533
	free(r);
534
	free(a);
535
	return;
536
}
537
 
538
int
539
winclean(Window *w, int conservative)	/* as it stands, conservative is always TRUE */
540
{
541
	if(w->isscratch || w->isdir)	/* don't whine if it's a guide file, error window, etc. */
542
		return TRUE;
543
	if(!conservative && w->nopen[QWevent]>0)
544
		return TRUE;
545
	if(w->dirty){
546
		if(w->body.file->nname)
547
			warning(nil, "%.*S modified\n", w->body.file->nname, w->body.file->name);
548
		else{
549
			if(w->body.file->nc < 100)	/* don't whine if it's too small */
550
				return TRUE;
551
			warning(nil, "unnamed file modified\n");
552
		}
553
		w->dirty = FALSE;
554
		return FALSE;
555
	}
556
	return TRUE;
557
}
558
 
559
char*
560
winctlprint(Window *w, char *buf, int fonts)
561
{
562
	sprint(buf, "%11d %11d %11d %11d %11d ", w->id, w->tag.file->nc,
563
		w->body.file->nc, w->isdir, w->dirty);
564
	if(fonts)
565
		return smprint("%s%11d %q %11d " , buf, Dx(w->body.r), 
566
			w->body.reffont->f->name, w->body.maxtab);
567
	return buf;
568
}
569
 
570
void
571
winevent(Window *w, char *fmt, ...)
572
{
573
	int n;
574
	char *b;
575
	Xfid *x;
576
	va_list arg;
577
 
578
	if(w->nopen[QWevent] == 0)
579
		return;
580
	if(w->owner == 0)
581
		error("no window owner");
582
	va_start(arg, fmt);
583
	b = vsmprint(fmt, arg);
584
	va_end(arg);
585
	if(b == nil)
586
		error("vsmprint failed");
587
	n = strlen(b);
588
	w->events = realloc(w->events, w->nevents+1+n);
589
	w->events[w->nevents++] = w->owner;
590
	memmove(w->events+w->nevents, b, n);
591
	free(b);
592
	w->nevents += n;
593
	x = w->eventx;
594
	if(x){
595
		w->eventx = nil;
596
		sendp(x->c, nil);
597
	}
598
}