1 /************************************************************************
2  * This program is Copyright (C) 1986-1996 by Jonathan Payne.  JOVE is  *
3  * provided to you without charge, and with no warranty.  You may give  *
4  * away copies of JOVE, including sources, provided that this notice is *
5  * included in all the files.                                           *
6  ************************************************************************/
7 
8 /* This creates/deletes/divides/grows/shrinks windows.  */
9 
10 #include "jove.h"
11 #include "chars.h"
12 #include "disp.h"
13 #include "ask.h"
14 #include "extend.h"
15 #include "commands.h"	/* for FindCmd and ExecCmd */
16 #include "mac.h"
17 #include "reapp.h"
18 #include "wind.h"
19 #include "screen.h"
20 
21 private const char
22 	onlyone[] = "You only have one window!",
23 	toosmall[] = "Resulting window would be too small.";
24 
25 #ifdef HIGHLIGHTING
26 bool	ScrollBar = NO;	/* VAR: whether the scrollbar is used */
27 #endif
28 
29 Window
30 	*curwind,
31 	*fwind = NULL;
32 
33 /* First line in a Window */
34 
35 int
FLine(w)36 FLine(w)
37 register Window	*w;
38 {
39 	register Window	*wp = fwind;
40 	register int	lineno = -1;
41 
42 	while (wp != w) {
43 		lineno += wp->w_height;
44 		wp = wp->w_next;
45 		if (wp == fwind)
46 			complain("window?");
47 	}
48 	return lineno + 1;
49 }
50 
51 /* Delete `wp' from the screen.  If it is the only window left
52    on the screen, then complain.  It gives its body
53    to the next window if there is one, otherwise the previous
54    window gets the body.  */
55 
56 void
del_wind(wp)57 del_wind(wp)
58 register Window	*wp;
59 {
60 	register Window
61 		*prev = wp->w_prev,
62 		*heir = prev;	/* default: previous window inherits space */
63 
64 	if (one_windp())
65 		complain(onlyone);
66 
67 	prev->w_next = wp->w_next;
68 	wp->w_next->w_prev = prev;
69 
70 	if (fwind == wp) {
71 		heir = fwind = wp->w_next;	/* no window above: next inherits */
72 		/* Here try to do something intelligent for redisplay() */
73 		SetTop(fwind, prev_line(fwind->w_top, wp->w_height));
74 	}
75 	heir->w_height += wp->w_height;
76 	if (curwind == wp)
77 		SetWind(heir);
78 #ifdef MAC
79 	RemoveScrollBar(wp);
80 	Windchange = YES;
81 #endif
82 	free((UnivPtr) wp);
83 }
84 
85 /* Divide the window WP N times, or at least once.  Complains if WP is too
86    small to be split into that many pieces.  It returns the new window. */
87 
88 Window *
div_wind(wp,n)89 div_wind(wp, n)
90 register Window	*wp;
91 int	n;
92 {
93 	Window	*latest = wp;
94 	int	amt;
95 
96 	if (n < 1)
97 		n = 1;
98 	amt = wp->w_height / (n + 1);
99 	if (amt < 2)
100 		complain(toosmall);
101 
102 	do {
103 		register Window	*new = (Window *) emalloc(sizeof (Window));
104 
105 		new->w_flags = 0;
106 		new->w_LRscroll = 0;
107 
108 		new->w_height = amt;
109 		wp->w_height -= amt;
110 
111 		/* set the lines such that w_line is the center in
112 		   each Window */
113 		new->w_line = wp->w_line;
114 		new->w_char = wp->w_char;
115 		new->w_bufp = wp->w_bufp;
116 		new->w_top = prev_line(new->w_line, WSIZE(new)/2);
117 
118 		/* Link the new window into the list */
119 		new->w_prev = latest;
120 		new->w_next = latest->w_next;
121 		new->w_next->w_prev = latest->w_next = new;
122 		latest = new;
123 #ifdef MAC
124 		new->w_control = NULL;
125 #endif
126 	} while (--n > 0);
127 #ifdef MAC
128 	Windchange = YES;
129 #endif
130 	return latest;
131 }
132 
133 /* Initialze the first window setting the bounds to the size of the
134    screen.  There is no buffer with this window.  See parse for the
135    setting of this window. */
136 
137 void
winit()138 winit()
139 {
140 	register Window	*w;
141 
142 	w = curwind = fwind = (Window *) emalloc(sizeof (Window));
143 	w->w_line = w->w_top = NULL;
144 	w->w_LRscroll = 0;
145 	w->w_flags = 0;
146 	w->w_char = 0;
147 	w->w_next = w->w_prev = fwind;
148 	w->w_height = ILI;
149 	w->w_bufp = NULL;
150 #ifdef MAC
151 	w->w_control = NULL;
152 	Windchange = YES;
153 #endif
154 }
155 
156 void
tiewind(w,bp)157 tiewind(w, bp)
158 register Window	*w;
159 register Buffer	*bp;
160 {
161 	bool	not_tied = (w->w_bufp != bp);
162 
163 	UpdModLine = YES;	/* kludge ... but speeds things up considerably */
164 	w->w_line = bp->b_dot;
165 	w->w_char = bp->b_char;
166 	w->w_bufp = bp;
167 	if (not_tied)
168 		CalcWind(w);	/* ah, this has been missing since the
169 				   beginning of time! */
170 }
171 
172 /* Change to previous window. */
173 
174 void
PrevWindow()175 PrevWindow()
176 {
177 	register Window	*new = curwind->w_prev;
178 
179 	if (Asking)
180 		complain((char *)NULL);
181 	if (one_windp())
182 		complain(onlyone);
183 	SetWind(new);
184 }
185 
186 /* Make NEW the current Window */
187 
188 void
SetWind(new)189 SetWind(new)
190 register Window	*new;
191 {
192 	if (!Asking && curbuf!=NULL) {		/* can you say kludge? */
193 		curwind->w_line = curline;
194 		curwind->w_char = curchar;
195 		curwind->w_bufp = curbuf;
196 	}
197 	if (new == curwind)
198 		return;
199 	SetBuf(new->w_bufp);
200 	if (!inlist(new->w_bufp->b_first, new->w_line)) {
201 		new->w_line = curline;
202 		new->w_char = curchar;
203 	}
204 	DotTo(new->w_line, new->w_char);
205 	if (curchar > (int)strlen(linebuf))
206 		new->w_char = curchar = strlen(linebuf);
207 	curwind = new;
208 }
209 
210 /* delete the current window if it isn't the only one left */
211 
212 void
DelCurWindow()213 DelCurWindow()
214 {
215 	SetABuf(curwind->w_bufp);
216 	del_wind(curwind);
217 }
218 
219 /* put the current line of `w' in the middle of the window */
220 
221 void
CentWind(w)222 CentWind(w)
223 register Window	*w;
224 {
225 	SetTop(w, prev_line(w->w_line, WSIZE(w)/2));
226 }
227 
228 int	ScrollStep = 0;	/* VAR: how should we scroll (full scrolling) */
229 
230 /* Calculate the new topline of the window.  If ScrollStep == 0
231    it means we should center the current line in the window. */
232 
233 void
CalcWind(w)234 CalcWind(w)
235 register Window	*w;
236 {
237 	register int	up;
238 	int	scr_step;
239 	LinePtr	newtop;
240 
241 	if (ScrollStep == 0) {	/* Means just center it */
242 		CentWind(w);
243 	} else {
244 		up = inorder(w->w_line, 0, w->w_top, 0);
245 		if (up == -1) {
246 			CentWind(w);
247 			return;
248 		}
249 		scr_step = (ScrollStep < 0) ? WSIZE(w) + ScrollStep :
250 			   ScrollStep - 1;
251 		/* up: point is above the screen */
252 		newtop = prev_line(w->w_line, up?
253 			scr_step : (WSIZE(w) - 1 - scr_step));
254 		if (LineDist(newtop, w->w_top) >= WSIZE(w) - 1)
255 			CentWind(w);
256 		else
257 			SetTop(w, newtop);
258 	}
259 }
260 
261 /* This is bound to ^X 4 [BTF].  To make the screen stay the
262    same we have to remember various things, like the current
263    top line in the current window.  It's sorta gross, but it's
264    necessary because of the way this is implemented (i.e., in
265    terms of do_find(), do_select() which manipulate the windows. */
266 
267 void
WindFind()268 WindFind()
269 {
270 	register Buffer
271 		*obuf = curbuf,
272 		*nbuf;
273 	LinePtr	ltop = curwind->w_top;
274 	Bufpos
275 		odot,
276 		ndot;
277 	void	(*cmd) ptrproto((void));
278 
279 	DOTsave(&odot);
280 
281 	switch (waitchar()) {
282 	case 't':
283 	case 'T':
284 		cmd = FindTag;
285 		break;
286 
287 	case CTL('T'):
288 		cmd = FDotTag;
289 		break;
290 
291 	case 'b':
292 	case 'B':
293 		cmd = BufSelect;
294 		break;
295 
296 	case 'f':
297 	case 'F':
298 		cmd = FindFile;
299 		break;
300 
301 	default:
302 		cmd = NULL;	/* avoid uninitialized complaint from gcc -W */
303 		complain("T: find-tag, ^T: find-tag-at-point, F: find-file, B: select-buffer.");
304 		/*NOTREACHED*/
305 	}
306 	ExecCmd((data_obj *) FindCmd(cmd));
307 
308 	nbuf = curbuf;
309 	DOTsave(&ndot);
310 	SetBuf(obuf);
311 	SetDot(&odot);
312 	SetTop(curwind, ltop);	/* there! it's as if we did nothing */
313 
314 	if (one_windp())
315 		(void) div_wind(curwind, 1);
316 
317 	tiewind(curwind->w_next, nbuf);
318 	SetWind(curwind->w_next);
319 	SetDot(&ndot);
320 }
321 
322 /* Go into one window mode by deleting all the other windows */
323 
324 void
OneWindow()325 OneWindow()
326 {
327 	while (curwind->w_next != curwind)
328 		del_wind(curwind->w_next);
329 }
330 
331 Window *
windbp(bp)332 windbp(bp)
333 register Buffer	*bp;
334 {
335 
336 	register Window	*wp = fwind;
337 
338 	if (bp == NULL)
339 		return NULL;
340 	do {
341 		if (wp->w_bufp == bp)
342 			return wp;
343 		wp = wp->w_next;
344 	} while (wp != fwind);
345 	return NULL;
346 }
347 
348 /* Change window into the next window.  Curwind becomes the new window. */
349 
350 void
NextWindow()351 NextWindow()
352 {
353 	register Window	*new = curwind->w_next;
354 
355 	if (Asking)
356 		complain((char *)NULL);
357 	if (one_windp())
358 		complain(onlyone);
359 	SetWind(new);
360 }
361 
362 /* Scroll the next Window */
363 
364 void
PageNWind()365 PageNWind()
366 {
367 	if (one_windp())
368 		complain(onlyone);
369 	NextWindow();
370 	NextPage();
371 	PrevWindow();
372 }
373 
374 private Window *
w_nam_typ(name,type)375 w_nam_typ(name, type)
376 register char	*name;
377 int	type;
378 {
379 	register Window *w;
380 	register Buffer	*b;
381 
382 	b = buf_exists(name);
383 	w = fwind;
384 	if (b != NULL) {
385 		do {
386 			if (w->w_bufp == b)
387 				return w;
388 		} while ((w = w->w_next) != fwind);
389 	}
390 
391 	w = fwind;
392 	do {
393 		if (w->w_bufp->b_type == type)
394 			return w;
395 	} while ((w = w->w_next) != fwind);
396 
397 	return NULL;
398 }
399 
400 /* Put a window with the buffer `name' in it.  Erase the buffer if
401    `clobber' is YES. */
402 
403 void
pop_wind(name,clobber,btype)404 pop_wind(name, clobber, btype)
405 register char	*name;
406 bool	clobber;
407 int	btype;
408 {
409 	register Window	*wp;
410 	register Buffer	*newb;
411 
412 	if ((newb = buf_exists(name)) != NULL)
413 		btype = -1;	/* if the buffer exists, don't change
414 				   it's type */
415 	if ((wp = w_nam_typ(name, btype)) == NULL) {
416 		if (one_windp())
417 			SetWind(div_wind(curwind, 1));
418 		else
419 			PrevWindow();
420 	} else
421 		SetWind(wp);
422 
423 	newb = do_select((Window *)NULL, name);
424 	if (clobber)
425 		buf_clear(newb);
426 	tiewind(curwind, newb);
427 	if (btype != -1)
428 		newb->b_type = btype;
429 	SetBuf(newb);
430 }
431 
432 void
GrowWindowCmd()433 GrowWindowCmd()
434 {
435 	WindSize(curwind, abs(arg_value()));
436 }
437 
438 void
ShrWindow()439 ShrWindow()
440 {
441 	WindSize(curwind, -abs(arg_value()));
442 }
443 
444 /* Change the size of the window by inc.  First arg is the window,
445    second is the increment. */
446 
447 void
WindSize(w,inc)448 WindSize(w, inc)
449 register Window	*w;
450 register int	inc;
451 {
452 	if (one_windp())
453 		complain(onlyone);
454 
455 	if (inc == 0)
456 		return;
457 	else if (inc < 0) {	/* Shrinking this Window. */
458 		if (w->w_height + inc < 2)
459 			complain(toosmall);
460 		w->w_height += inc;
461 		w->w_prev->w_height -= inc;
462 	} else {		/* Growing the window. */
463 		/* Change made from original code so that growing a window
464 		   exactly offsets effect of shrinking a window, i.e.
465 		   doing either followed by the other restores original
466 		   sizes of all affected windows. */
467 		if (w->w_prev->w_height - inc < 2)
468 			complain(toosmall);
469 		w->w_height += inc;
470 		w->w_prev->w_height -= inc;
471 	}
472 #ifdef MAC
473 	Windchange = YES;
474 #endif
475 }
476 
477 /* Set the topline of the window, calculating its number in the buffer.
478    This is for numbering the lines only. */
479 
480 void
SetTop(w,line)481 SetTop(w, line)
482 Window	*w;
483 register LinePtr	line;
484 {
485 #ifdef HIGHLIGHTING
486 	if (ScrollBar)
487 		UpdModLine = YES;
488 #endif
489 	w->w_top = line;
490 	if (w->w_flags & W_NUMLINES)
491 		w->w_topnum = LinesTo(w->w_bufp->b_first, line) + 1;
492 }
493 
494 void
WNumLines()495 WNumLines()
496 {
497 	curwind->w_flags ^= W_NUMLINES;
498 	SetTop(curwind, curwind->w_top);
499 }
500 
501 void
WVisSpace()502 WVisSpace()
503 {
504 	curwind->w_flags ^= W_VISSPACE;
505 	ClAndRedraw();
506 }
507 
508 /* If `line' is in `windes', return its screen line number;
509    otherwise return -1. */
510 
511 int
in_window(windes,line)512 in_window(windes, line)
513 register Window	*windes;
514 register LinePtr	line;
515 {
516 	register int	i;
517 	register LinePtr	lp = windes->w_top;
518 
519 	for (i = 0; lp != NULL && i < windes->w_height - 1; i++, lp = lp->l_next)
520 		if (lp == line)
521 			return FLine(windes) + i;
522 	return -1;
523 }
524 
525 void
SplitWind()526 SplitWind()
527 {
528 	SetWind(div_wind(curwind, arg_or_default(2) - 1));
529 }
530 
531 /* Goto the window with the named buffer.  If no such window
532    exists, pop one and attach the buffer to it. */
533 void
GotoWind()534 GotoWind()
535 {
536 	char	*bname = ask_buf(lastbuf, ALLOW_OLD | ALLOW_INDEX | ALLOW_NEW);
537 	Window	*w;
538 
539 	w = curwind->w_next;
540 	do {
541 		if (w->w_bufp->b_name == bname) {
542 			SetABuf(curbuf);
543 			SetWind(w);
544 			return;
545 		}
546 		w = w->w_next;
547 	} while (w != curwind);
548 	SetABuf(curbuf);
549 	pop_wind(bname, NO, -1);
550 }
551 
552 void
ScrollRight()553 ScrollRight()
554 {
555 	int	amt = arg_or_default(ScrollWidth);
556 
557 	if (curwind->w_LRscroll - amt < 0)
558 		curwind->w_LRscroll = 0;
559 	else
560 		curwind->w_LRscroll -= amt;
561 	UpdModLine = YES;
562 }
563 
564 void
ScrollLeft()565 ScrollLeft()
566 {
567 	int	amt = arg_or_default(ScrollWidth);
568 
569 	curwind->w_LRscroll += amt;
570 	UpdModLine = YES;
571 }
572 
573 LineEffects
WindowRange(w)574 WindowRange(w)
575 Window *w;
576 {
577 #ifdef HIGHLIGHTING
578 	static struct LErange range = {0-0, 0-0, SO_effect, US_effect};
579 
580 	range.start = range.width = 0;	/* default: no highlighting */
581 	if (ScrollBar) {
582 		register int	/* line counts of various portions -- slow! */
583 			above = LinesTo(w->w_bufp->b_first, w->w_top),
584 			below = LinesTo(w->w_top, (LinePtr)NULL),
585 			total = above + below,
586 			in = min(below, WSIZE(w));
587 
588 		if (above == -1 || below == -1)
589 			return &range;	/* something fishy */
590 		below -= in;	/* correction */
591 		if (in != total) {
592 			/* Window shows only part of the buffer: highlight "thumb".
593 			 *
594 			 * Required properties:
595 			 * - proportionality of "below", "in", and "above" segments
596 			 * - monotonicity and smoothness of representation
597 			 * - a segment vanishes iff it is empty (but "in" is never empty)
598 			 * - extreme L & R ends of modeline must indicate presence/absence
599 			 *   of first/last line of buffer; hence some non-linearity
600 			 *   thereabouts.
601 			 *
602 			 * Implementation:
603 			 * - Use unsigned long to prevent overflow.
604 			 * - Allocate space to "above" and "below", rounding to nearest
605 			 *   for best proportionality.
606 			 * - Allocate the rest to "in".
607 			 * - Ensure "in" not empty by ensuring total space allocated
608 			 *   to above and below must leave at least one col.  This
609 			 *   is done fiddling the rounding term when "in" is small.
610 			 *
611 			 * - The first (last) char in the modeline represents the
612 			 *   first (last) line in the buffer.
613 			 *
614 			 * - That leaves the rest of the modeline to represent
615 			 *   (linearly) the remaining (total-2) lines of the buffer,
616 			 *   of which (above-1) should be represented by highlighting
617 			 *   from char position 2 onwards and (below-1) should be
618 			 *   represented by highlighting from (totalcols-1) backwards.
619 			 *
620 			 * - Rounding is applied in this region, and is fiddled to
621 			 *   ensure that the white bit in the middle never shrinks to
622 			 *   zero.
623 			 */
624 			int
625 				totalcols_2 = CO - 1 - (4 * SG) - 2,
626 				total_2 = total - 2,
627 				rounding = (in < total_2/totalcols_2) ? (totalcols_2*in)/2 : total_2/2,
628 				abovecols = (above == 0) ? 0 :
629 					1 + ((long)(above-1)*totalcols_2 + rounding) / total_2,
630 				belowcols = (below == 0) ? 0 :
631 					1 + ((long)(below-1)*totalcols_2 + rounding) / total_2;
632 
633 			range.start = abovecols;
634 			range.width = totalcols_2 + 2 - abovecols - belowcols;
635 		}
636 	}
637 	return &range;
638 #else /* !HIGHLIGHTING */
639 	return YES;	/*  modeline always stands out */
640 #endif /* !HIGHLIGHTING */
641 }
642