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 ⦥ /* 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 ⦥
638 #else /* !HIGHLIGHTING */
639 return YES; /* modeline always stands out */
640 #endif /* !HIGHLIGHTING */
641 }
642