1 /*
2  * $Id: window.c,v 1.11 2001/02/13 23:38:06 danny Exp $
3  *
4  * Copyright � 1992, 1993, 1999, 2001 Free Software Foundation, Inc.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this software; see the file COPYING.  If not, write to
18  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19  */
20 
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24 
25 #ifdef	WITH_DMALLOC
26 #include <dmalloc.h>
27 #endif
28 
29 #include <stdio.h>
30 #include "global.h"
31 #include "window.h"
32 #include "io-generic.h"
33 #include "io-abstract.h"
34 #include "io-utils.h"
35 #include "io-term.h"
36 #include "cmd.h"
37 #include "lists.h"
38 #include "regions.h"
39 
40 /* Low level window operators. */
41 
42 #define MIN_WIN_HEIGHT(W) (W->bottom_edge_r \
43 			   + label_rows * (W->flags & WIN_EDGES ? 2 : 1))
44 
45 #define MIN_WIN_WIDTH(W) (W->right_edge_c \
46 			  + label_emcols * (W->flags & WIN_EDGES ? 6 : 1))
47 #define MIN_CWIN_HEIGHT  MIN_WIN_HEIGHT(cwin)
48 #define MIN_CWIN_WIDTH  MIN_WIN_WIDTH(cwin)
49 
50 
51 static void
do_close_window(int num)52 do_close_window (int num)
53 {
54   int n;
55   struct window *win, *kwin;
56   int nlf, nrt, nup, nbl;
57   int klo, kho, kld, khd;
58   int lo, ho, ld, hd;
59   struct tmp
60     {
61       int l, r, u, b;
62     }
63    *tmpptr;
64 
65   if (nwin == 1)
66     {
67       io_error_msg ("Attempt to delete sole ordinary window.");
68       return;
69     }
70   tmpptr = ck_malloc (sizeof (struct tmp) * nwin);
71 
72   kwin = &wins[num];
73   nlf = nrt = nup = nbl = 0;
74   klo = kwin->win_over - kwin->lh_wid;
75   kho = kwin->win_over + kwin->numc + kwin->right_edge_c - 1;
76   kld = kwin->win_down - (kwin->lh_wid ? label_rows : 0);
77   khd = kwin->win_down + kwin->numr + kwin->bottom_edge_r - 1;
78 
79   for (win = wins; win < &wins[nwin]; win++)
80     {
81       lo = win->win_over - win->lh_wid;
82       ho = win->win_over + win->numc + win->right_edge_c - 1;
83       ld = win->win_down - (win->lh_wid ? label_rows : 0);
84       hd = win->win_down + win->numr + win->bottom_edge_r - 1;
85 
86       /* Match to the left ? */
87       if (lo == kho + 1)
88 	{
89 	  if (ld >= kld && hd <= khd)
90 	    tmpptr[nrt++].r = win - wins;
91 	  else if (hd >= kld && ld <= khd)
92 	    nrt = nwin;
93 	}
94       else if (ho == klo - 1)
95 	{
96 	  if (ld >= kld && hd <= khd)
97 	    tmpptr[nlf++].l = win - wins;
98 	  else if (hd >= kld && ld <= khd)
99 	    nlf = nwin;
100 	}
101       else if (ld == khd + 1)
102 	{
103 	  if (lo >= klo && ho <= kho)
104 	    tmpptr[nbl++].b = win - wins;
105 	  else if (ho >= kho && lo <= kho)
106 	    nbl = nwin;
107 	}
108       else if (hd == kld - 1)
109 	{
110 	  if (lo >= klo && ho <= kho)
111 	    tmpptr[nup++].u = win - wins;
112 	  else if (ho >= kho && lo <= kho)
113 	    nup = nwin;
114 	}
115 
116     }
117   if (nrt == 0)
118     nrt = nwin;
119   if (nlf == 0)
120     nlf = nwin;
121   if (nbl == 0)
122     nbl = nwin;
123   if (nup == 0)
124     nup = nwin;
125   if (nrt <= nlf && nrt <= nbl && nrt <= nup)
126     for (n = 0; n < nrt; n++)
127       {
128 	wins[tmpptr[n].r].numc
129 	  += kwin->lh_wid + kwin->numc + kwin->right_edge_c;
130 	wins[tmpptr[n].r].win_over
131 	  -= kwin->lh_wid + kwin->numc + kwin->right_edge_c;
132       }
133   else if (nlf <= nbl && nlf <= nup)
134     for (n = 0; n < nlf; n++)
135       wins[tmpptr[n].l].numc
136 	+= kwin->lh_wid + kwin->numc + kwin->right_edge_c;
137   else if (nbl <= nup)
138     for (n = 0; n < nbl; n++)
139       {
140 	wins[tmpptr[n].b].numr
141 	  += kwin->numr + (kwin->lh_wid ? 1 : 0) * label_rows
142 	  + kwin->bottom_edge_r;
143 
144 	wins[tmpptr[n].b].win_down
145 	  -= kwin->numr + (kwin->lh_wid ? 1 : 0) * label_rows
146 	  + kwin->bottom_edge_r;
147       }
148   else
149     for (n = 0; n < nup; n++)
150       wins[tmpptr[n].u].numr
151 	+= kwin->numr + (kwin->lh_wid ? 1 : 0) * label_rows;
152 
153   if (kwin == cwin && kwin != wins)
154     --cwin;
155   if (cwin == &wins[nwin - 1])
156     --cwin;
157   while (kwin < &wins[nwin])
158     {
159       *kwin = kwin[1];
160       kwin++;
161     }
162   --nwin;
163   io_recenter_all_win ();
164   return;
165 }
166 
167 int
win_label_cols(struct window * win,CELLREF hr)168 win_label_cols (struct window * win, CELLREF hr)
169 {
170   int lh;
171 
172   if ((win->flags & WIN_EDGES) == 0)
173     lh = 0;
174 #if BITS_PER_CELLREF>8
175   else if ((win->flags & WIN_PAG_HZ) || hr >= 10000)
176     lh = 7;
177   else if (hr >= 1000)
178     lh = 6;
179   else if (hr >= 100)
180     lh = 5;
181 #else
182   else if ((win->flags & WIN_PAG_HZ) || hr >= 100)
183     lh = 5;
184 #endif
185   else if (hr > 10)
186     lh = 4;
187   else
188     lh = 3;
189   lh *= label_emcols;
190   return lh;
191 }
192 
193 int
win_label_rows(struct window * win)194 win_label_rows (struct window * win)
195 {
196   return (win->flags & WIN_EDGES) ? label_rows : 0;
197 }
198 
199 static void
set_numcols(struct window * win,CELLREF hr)200 set_numcols (struct window *win, CELLREF hr)
201 {
202   int lh = win_label_cols (win, hr);
203   win->win_over -= win->lh_wid - lh;
204   win->numc += win->lh_wid - lh;
205   win->lh_wid = lh;
206 }
207 
208 
209 static void
page_axis(CELLREF cur,int (* get)(CELLREF),int total,CELLREF * loP,CELLREF * hiP)210 page_axis (CELLREF cur, int (*get) (CELLREF), int total,
211 	   CELLREF *loP, CELLREF *hiP)
212 {
213   CELLREF lo, hi;
214   int w, ww;
215 
216   lo = hi = MIN_ROW;
217   w = (*get) (hi);
218   for (;;)
219     {
220       ww = (*get) (hi + 1);
221       while (w + ww <= total && hi < MAX_ROW)
222 	{
223 	  hi++;
224 	  w += ww;
225 	  ww = (*get) (hi + 1);
226 	}
227       if (hi >= cur)
228 	break;
229       hi++;
230       lo = hi;
231       w = ww;
232     }
233   if (lo > cur || hi > MAX_ROW)
234     io_error_msg ("Can't find a non-zero-sized cell page_axis");
235   *loP = lo;
236   *hiP = hi;
237 }
238 
239 
240 static void
recenter_axis(CELLREF cur,int (* get)(CELLREF),int total,CELLREF * loP,CELLREF * hiP)241 recenter_axis (CELLREF cur, int (*get) (CELLREF), int total,
242 	       CELLREF *loP, CELLREF *hiP)
243 {
244   CELLREF lo, hi;
245   int tot;
246   int n;
247   int more;
248 
249   lo = hi = cur;
250   n = tot = (*get) (cur);
251   do
252     {
253       if (lo > MIN_ROW && tot + (n = (*get) (lo - 1)) <= total)
254 	{
255 	  --lo;
256 	  tot += n;
257 	  more = 1;
258 	}
259       else
260 	more = 0;
261       if (hi < MAX_ROW && tot + (n = (*get) (hi + 1)) <= total)
262 	{
263 	  hi++;
264 	  tot += n;
265 	  more++;
266 	}
267     }
268   while (more);
269   *loP = lo;
270   *hiP = hi;
271 }
272 
273 static void
recenter_window(struct window * win)274 recenter_window (struct window *win)
275 {
276   if (win->flags & WIN_PAG_VT)
277     page_axis (win->win_curow, get_scaled_height, win->numr,
278 	       &(win->screen.lr), &(win->screen.hr));
279   else
280     recenter_axis (win->win_curow, get_scaled_height, win->numr,
281 		   &(win->screen.lr), &(win->screen.hr));
282   set_numcols (win, win->screen.hr);
283   if (win->flags & WIN_PAG_HZ)
284     page_axis (win->win_cucol, get_scaled_width, win->numc,
285 	       &(win->screen.lc), &(win->screen.hc));
286   else
287     recenter_axis (win->win_cucol, get_scaled_width, win->numc,
288 		   &(win->screen.lc), &(win->screen.hc));
289 }
290 
291 
292 /*
293  * RESIZE_SCREEN adjusts the windows list after a screen size change.
294  * It presumes that Global->scr_lines and Global->scr_cols are the new values.  DR and DC
295  * are the changes that just occured to those values.
296  */
297 static void
resize_screen(int dr,int dc)298 resize_screen (int dr, int dc)
299 {
300   int x, n;
301   int lines;
302   int firstln;
303   int ncols;
304   int firstcol;
305   int old_lines;
306 
307   if (!nwin)
308     return;
309 
310   lines = Global->scr_lines - (!!user_status * status_rows) - input_rows;
311   old_lines = lines - dr;
312   firstln = (user_input > 0) * input_rows + (user_status > 0) * status_rows;
313 
314   /* First, delete windows that will shrink too much. */
315   cwin->win_curow = curow;
316   cwin->win_cucol = cucol;
317   if (dr < 0)
318     for (x = 0; x < nwin; x++)
319       {
320 	int rlow =
321 	(wins[x].win_down - (wins[x].lh_wid ? label_rows : 0) - firstln);
322 	int rhi = ((wins[x].win_down + wins[x].numr + wins[x].bottom_edge_r)
323 		   - firstln);
324 	int sqbelow = dr * rlow;
325 	int sqtohere = dr * rhi;
326 	sqbelow /= old_lines;
327 	sqtohere /= old_lines;
328 	if (wins[x].numr <= sqbelow - sqtohere)
329 	  {
330 	    do_close_window (x);
331 	    x--;
332 	  }
333       }
334   for (x = 0; x < nwin; ++x)
335     {
336       int rlow =
337       (wins[x].win_down - (wins[x].lh_wid ? label_rows : 0) - firstln);
338       int rhi = ((wins[x].win_down + wins[x].numr + wins[x].bottom_edge_r)
339 		 - firstln);
340       int sqbelow = dr * rlow;
341       int sqtohere = dr * rhi;
342       sqbelow /= old_lines;
343       sqtohere /= old_lines;
344       wins[x].win_down += sqbelow;
345       wins[x].numr += sqtohere - sqbelow;
346     }
347 
348   /* then columns */
349   firstcol = 0;
350   ncols = Global->scr_cols;
351   ncols -= dc;
352 
353   /* First, delete windows that will shrink too much. */
354   if (dc < 0)
355     for (x = 0; x < nwin; x++)
356       {
357 	int clow = (wins[x].win_over - wins[x].lh_wid) - firstcol;
358 	int chi = (wins[x].win_over + wins[x].numc + wins[x].right_edge_c
359 		   - firstcol);
360 	int sqbelow = dc * clow;
361 	int sqtohere = dc * chi;
362 	sqbelow /= ncols;
363 	sqtohere /= ncols;
364 	if (wins[x].numc <= sqbelow - sqtohere)
365 	  {
366 	    do_close_window (x);
367 	    x--;
368 	  }
369       }
370   for (x = 0; x < nwin; ++x)
371     {
372       int clow = (wins[x].win_over - wins[x].lh_wid) - firstcol;
373       int chi = (wins[x].win_over + wins[x].numc + wins[x].right_edge_c
374 		 - firstcol);
375       int sqbelow = dc * clow;
376       int sqtohere = dc * chi;
377       sqbelow /= ncols;
378       sqtohere /= ncols;
379       wins[x].win_over += sqbelow;
380       wins[x].numc += sqtohere - sqbelow;
381     }
382   for (n = 0; n < nwin; n++)
383     recenter_window (&wins[n]);
384   io_repaint ();
385 }
386 
387 static void
shift_linked_window(long dn,long ov)388 shift_linked_window (long dn, long ov)
389 {
390   struct window *win;
391 
392   win = cwin;
393   while (win->link != -1)
394     {
395       win = &wins[win->link];
396       if (win == cwin)		/* Loop check! */
397 	return;
398       if ((win->flags & WIN_LCK_VT) == 0)
399 	win->win_curow += dn;
400       if ((win->flags & WIN_LCK_HZ) == 0)
401 	win->win_cucol += ov;
402       if (win->win_curow < win->screen.lr || win->win_curow > win->screen.hr
403 	  || win->win_cucol < win->screen.lc || win->win_cucol > win->screen.hc)
404 	recenter_window (win);
405     }
406 }
407 
408 
409 static void
find_nonzero(CELLREF * curp,CELLREF lo,CELLREF hi,int (* get)(CELLREF))410 find_nonzero (CELLREF *curp, CELLREF lo, CELLREF hi,
411 	      int (*get) (CELLREF))
412 {
413   CELLREF cc;
414   int n;
415 
416   cc = *curp;
417 
418   if (cc < hi)
419     {
420       cc++;
421       while ((n = (*get) (cc)) == 0)
422 	{
423 	  if (cc == hi)
424 	    break;
425 	  cc++;
426 	}
427       if (n)
428 	{
429 	  *curp = cc;
430 	  return;
431 	}
432     }
433   if (cc > lo)
434     {
435       --cc;
436       while ((n = (*get) (cc)) == 0)
437 	{
438 	  if (cc == lo)
439 	    break;
440 	  --cc;
441 	}
442       if (n)
443 	{
444 	  *curp = cc;
445 	  return;
446 	}
447     }
448 }
449 
450 static int
scroll_axis(CELLREF cur,int over,int total,int (* get)(CELLREF),CELLREF * ret1,CELLREF * ret2,int * offp)451 scroll_axis (CELLREF cur, int over, int total, int (*get) (CELLREF),
452 	     CELLREF *ret1, CELLREF *ret2, int *offp)
453 {
454   int tot;
455 
456   int inc;
457   CELLREF fini;
458   int num;
459   CELLREF p1, p2;
460   int n;
461 
462   inc = (over > 0 ? 1 : -1);
463   fini = over > 0 ? MAX_ROW : MIN_ROW;
464   num = over > 0 ? over : -over;
465 
466   if (inc > 0 ? *ret2 == MAX_ROW : *ret1 == MIN_ROW)
467     return 1;
468   p1 = inc > 0 ? *ret2 + 1 : *ret1 - 1;
469   p2 = p1;
470   for (;;)
471     {
472       --num;
473       tot = (*get) (p1);
474       while (p2 != fini && tot + (n = (*get) (p2 + inc)) <= total)
475 	{
476 	  p2 += inc;
477 	  tot += n;
478 	}
479       if (!num || p2 == fini)
480 	break;
481     }
482   if (num)
483     return 1;
484   while (tot + (n = (*get) (p1 - inc)) <= total)
485     {
486       p1 -= inc;
487       tot += n;
488       if (inc > 0)
489 	(*offp)++;
490     }
491   if (p1 > p2)
492     {
493       *ret1 = p2;
494       *ret2 = p1;
495     }
496   else
497     {
498       *ret1 = p1;
499       *ret2 = p2;
500     }
501   return 0;
502 }
503 
504 static int
page_scroll_axis(CELLREF cur,int over,int total,int (* get)(CELLREF),CELLREF * ret1,CELLREF * ret2,int * offp)505 page_scroll_axis (CELLREF cur, int over, int total, int (*get) (CELLREF),
506 		  CELLREF *ret1, CELLREF *ret2, int *offp)
507 {
508   int n_over;
509   CELLREF lo, hi;
510   int tot;
511   int ww = 0;
512 
513   n_over = 0;
514   lo = hi = MIN_ROW;
515   tot = (*get) (hi);
516   for (;;)
517     {
518       while (hi < MAX_ROW && tot + (ww = (*get) (hi + 1)) <= total)
519 	{
520 	  hi++;
521 	  tot += ww;
522 	}
523       if (hi >= cur)
524 	break;
525       hi++;
526       n_over++;
527       lo = hi;
528       tot = ww;
529     }
530   n_over += over;
531   if (n_over < 0)
532     return 1;
533 
534   lo = hi = MIN_ROW;
535   tot = (*get) (hi);
536   for (;;)
537     {
538       while (hi < MAX_ROW && tot + (ww = (*get) (hi + 1)) <= total)
539 	{
540 	  hi++;
541 	  tot += ww;
542 	}
543       if (!n_over || hi == MAX_ROW)
544 	break;
545       --n_over;
546       hi++;
547       lo = hi;
548       tot = ww;
549     }
550   if (hi == MAX_ROW && n_over)
551     return 1;
552   *ret1 = lo;
553   *ret2 = hi;
554   return 0;
555 }
556 
557 
558 
559 /* External window interface */
560 
561 void
io_set_label_size(int r,int c)562 io_set_label_size (int r, int c)
563 {
564   /* fixme */
565 }
566 
567 void
io_set_scr_size(int lines,int cols)568 io_set_scr_size (int lines, int cols)
569 {
570   int dl = lines - Global->scr_lines;
571   int dc = cols - Global->scr_cols;
572 
573   Global->scr_lines = lines;
574   Global->scr_cols = cols;
575 
576   resize_screen (dl, dc);
577 }
578 
579 void
io_set_input_rows(int n)580 io_set_input_rows (int n)
581 {
582   input_rows = n;
583   io_set_input_status (user_input, user_status, 1);
584 }
585 
586 void
io_set_status_rows(int n)587 io_set_status_rows (int n)
588 {
589   status_rows = n;
590   io_set_input_status (user_input, user_status, 1);
591 }
592 
593 void
io_set_input_status(int inp,int stat,int redraw)594 io_set_input_status (int inp, int stat, int redraw)
595 {
596   int inpv = inp < 0 ? -inp : inp;
597   int inpsgn = inp == inpv ? 1 : -1;
598   int statv = stat < 0 ? -stat : stat;
599   int statsgn = stat == statv ? 1 : -1;
600   int new_ui;
601   int new_us;
602   int new_inp;
603   int new_stat;
604 
605   if (inpv == 0 || inpv > 2)
606     io_error_msg ("Bad input location %d; it should be +/- 1, or 2", inp);
607   else if (statv > 2)
608     io_error_msg ("Bad status location %d; it should be +/- 0, 1, or 2",
609 		  inp);
610   else
611     {
612       new_ui = inp;
613       new_us = stat;
614       if (inpsgn != statsgn)
615 	{
616 	  if (inpsgn > 0)
617 	    {
618 	      new_inp = 0;
619 	      new_stat = Global->scr_lines - status_rows;
620 	    }
621 	  else
622 	    {
623 	      new_inp = Global->scr_lines - input_rows;
624 	      new_stat = 0;
625 	    }
626 	}
627       else
628 	{
629 	  if (inpv > statv)
630 	    {
631 	      new_inp = new_us ? status_rows : 0;
632 	      new_stat = 0;
633 	    }
634 	  else
635 	    {
636 	      new_inp = 0;
637 	      new_stat = input_rows;
638 	    }
639 	  if (inpsgn < 0)
640 	    {
641 	      new_stat = Global->scr_lines - new_stat - status_rows;
642 	      new_inp = Global->scr_lines - new_inp - input_rows;
643 	    }
644 	}
645       if (redraw)
646 	{
647 	  int vchange =
648 	  (((new_ui > 0 ? input_rows : 0)
649 	    + (new_us > 0 ? status_rows : 0))
650 	   - ((user_input > 0 ? input_rows : 0)
651 	      + (user_status > 0 ? status_rows : 0)));
652 	  int grow = (user_status
653 		      ? (new_us ? 0 : status_rows)
654 		      : (new_us ? -status_rows : 0));
655 	  int cell_top =
656 	  ((user_status > 0 ? status_rows : 0)
657 	   + (user_input > 0 ? input_rows : 0));
658 
659 	  if (grow < 0)
660 	    {
661 	      int x;
662 	    re:
663 	      for (x = 0; x < nwin; ++x)
664 		{
665 		  int top = wins[x].win_down - win_label_rows(&wins[x]);
666 		  if (cell_top == top && (wins[x].numr <= -grow))
667 		    {
668 		      do_close_window (x);
669 		      goto re;
670 		    }
671 		}
672 	    }
673 
674 	  if (grow)
675 	    {
676 	      int x;
677 	      for (x = 0; x < nwin; ++x)
678 		{
679 		  int top =
680 		  wins[x].win_down - win_label_rows (&wins[x]);
681 		  if (cell_top == top)
682 		    wins[x].numr -= vchange;
683 		}
684 	    }
685 	  if (vchange)
686 	    {
687 	      int x;
688 	      for (x = 0; x < nwin; ++x)
689 		wins[x].win_down += vchange;
690 	    }
691 	  io_repaint ();
692 	}
693       user_input = new_ui;
694       user_status = new_us;
695       Global->input = new_inp;
696       Global->status = new_stat;
697     }
698 }
699 
700 void
io_set_cwin(struct window * win)701 io_set_cwin (struct window *win)
702 {
703   io_hide_cell_cursor ();
704   cwin->win_curow = curow;
705   cwin->win_cucol = cucol;
706   cwin = win;
707   curow = cwin->win_curow;
708   cucol = cwin->win_cucol;
709   io_display_cell_cursor ();
710 }
711 
712 
713 void
io_pr_cell(CELLREF r,CELLREF c,CELL * cp)714 io_pr_cell (CELLREF r, CELLREF c, CELL *cp)
715 {
716   struct window *win;
717 
718   for (win = wins; win < &wins[nwin]; win++)
719     {
720       if (r < win->screen.lr || r > win->screen.hr
721 	  || c < win->screen.lc || c > win->screen.hc)
722 	continue;
723       io_pr_cell_win (win, r, c, cp);
724     }
725 }
726 
727 void
io_redo_region(struct rng * rng)728 io_redo_region (struct rng * rng)
729 {
730   CELL * cp;
731   CELLREF r, c;
732   find_cells_in_range (rng);
733   cp = next_row_col_in_range (&r, &c);
734   while (cp)
735     {
736       io_pr_cell (r, c, cp);
737       cp = next_row_col_in_range (&r, &c);
738     }
739 }
740 
741 /* Create a new window by splitting the current one. */
742 void
io_win_open(int hv,int where)743 io_win_open (int hv, int where)
744 {
745   int tmp;
746   struct window *win;
747 
748   if (   (!hv
749 	  && (where < MIN_CWIN_WIDTH
750 	      || (cwin->numc + cwin->lh_wid + cwin->right_edge_c - where
751 		       < MIN_CWIN_WIDTH)))
752       || (hv
753 	  && (where < MIN_CWIN_HEIGHT
754 	      || (cwin->numr + cwin->bottom_edge_r
755 		  + (cwin->lh_wid ? label_rows : 0) - where
756 		  < MIN_CWIN_HEIGHT))))
757     {
758       io_error_msg ("Window won't fit!");
759       return;
760     }
761 
762   nwin++;
763   tmp = cwin - wins;
764   wins = ck_realloc (wins, nwin * sizeof (struct window));
765   win = &wins[nwin - 1];
766   cwin = &wins[tmp];
767   win->id = win_id++;
768   win->bottom_edge_r = cwin->bottom_edge_r;
769   win->right_edge_c = cwin->right_edge_c;
770   /* set_numcols will take care of fixing win_over if edges are on. */
771   win->win_over = cwin->win_over + (hv ? 0 : where) - cwin->lh_wid;
772   win->win_down = cwin->win_down + (hv ? where : 0);
773   win->flags = cwin->flags;
774   win->link = -1;
775   win->lh_wid = 0;
776   win->win_slops = 0;
777   win->numc = cwin->numc + cwin->lh_wid + (hv ? 0 : -where);
778   win->numr = cwin->numr + (hv ? -where : 0);
779   win->win_curow = curow;
780   win->win_cucol = cucol;
781   set_numcols (win, curow);
782   cwin->numc -= (hv ? 0 : win->numc + win->lh_wid + win->right_edge_c);
783   cwin->numr -=
784     (hv ? win->numr + (win->lh_wid ? label_rows : 0) + win->bottom_edge_r
785      : 0);
786   cwin->win_curow = curow;
787   cwin->win_cucol = cucol;
788   io_hide_cell_cursor ();
789   win = cwin;
790   cwin = &wins[nwin - 1];
791   recenter_window (cwin);
792   recenter_window (win);
793   io_display_cell_cursor ();
794   io_repaint ();
795 }
796 
797 void
io_win_close(struct window * win)798 io_win_close (struct window *win)
799 {
800   do_close_window (win - wins);
801 }
802 
803 void
io_move_cell_cursor(CELLREF rr,CELLREF cc)804 io_move_cell_cursor (CELLREF rr, CELLREF cc)
805 {
806   if (cwin->link != -1)
807     shift_linked_window ((long) rr - curow, (long) cc - cucol);
808   if (rr < cwin->screen.lr || rr > cwin->screen.hr
809       || cc < cwin->screen.lc || cc > cwin->screen.hc)
810     {
811       cwin->win_curow = curow = rr;
812       cwin->win_cucol = cucol = cc;
813       recenter_window (cwin);
814       io_repaint_win (cwin);
815       if (cwin->link > 0)
816 	io_repaint_win (&wins[cwin->link]);
817     }
818   else
819     {
820       io_hide_cell_cursor ();
821       curow = rr;
822       cucol = cc;
823       io_display_cell_cursor ();
824       io_update_status ();
825     }
826   if (get_scaled_width (cucol) == 0)
827     find_nonzero (&cucol, cwin->screen.lc, cwin->screen.hc, get_scaled_width);
828   if (get_scaled_height (curow) == 0)
829     find_nonzero (&curow, cwin->screen.lr, cwin->screen.hr, get_scaled_height);
830 }
831 
832 void
io_shift_cell_cursor(int dirn,int repeat)833 io_shift_cell_cursor (int dirn, int repeat)
834 {
835   CELLREF c;
836   CELLREF r;
837   int w = 0;
838   int over, down;
839 
840   over = colmagic[dirn] * repeat;
841   down = rowmagic[dirn] * repeat;
842   if (over > 0)
843     {
844       c = cucol;
845       while (c < MAX_COL && over-- > 0)
846 	{
847 	  c++;
848 	  while ((w = get_scaled_width (c)) == 0 && c < MAX_COL)
849 	    c++;
850 	}
851       if (over > 0 || c == cucol || w == 0)
852 	{
853 	  io_error_msg ("Can't go right");
854 	  return;
855 	}
856     }
857   else if (over < 0)
858     {
859       c = cucol;
860       while (c > MIN_COL && over++ < 0)
861 	{
862 	  --c;
863 	  while ((w = get_scaled_width (c)) == 0 && c > MIN_COL)
864 	    --c;
865 	}
866       if (over < 0 || c == cucol || w == 0)
867 	{
868 	  io_error_msg ("Can't go left");
869 	  return;
870 	}
871     }
872   else
873     c = cucol;
874 
875   if (down > 0)
876     {
877       r = curow;
878       while (r < MAX_ROW && down-- > 0)
879 	{
880 	  r++;
881 	  while ((w = get_scaled_height (r)) == 0 && r < MAX_ROW)
882 	    r++;
883 	}
884       if (down > 0 || r == curow || w == 0)
885 	{
886 	  io_error_msg ("Can't go down");
887 	  return;
888 	}
889     }
890   else if (down < 0)
891     {
892       r = curow;
893       while (r > MIN_ROW && down++ < 0)
894 	{
895 	  --r;
896 	  while ((w = get_scaled_height (r)) == 0 && r > MIN_ROW)
897 	    --r;
898 	}
899       if (down < 0 || r == curow || w == 0)
900 	{
901 	  io_error_msg ("Can't go up");
902 	  return;
903 	}
904     }
905   else
906     r = curow;
907 
908   io_move_cell_cursor (r, c);
909 }
910 
911 void
io_scroll_cell_cursor(int magic,int repeat)912 io_scroll_cell_cursor (int magic, int repeat)
913 {
914   int off_dn, off_rt;
915 
916   struct rng s;
917   CELLREF cr, cc;
918   int over, down;
919   int ret;
920 
921   over = colmagic[magic];
922   down = rowmagic[magic];
923 
924   s.lr = cwin->screen.lr;
925   s.hr = cwin->screen.hr;
926   if (down)
927     {
928       off_dn = curow - cwin->screen.lr;
929       if (cwin->flags & WIN_PAG_VT)
930 	ret = page_scroll_axis
931 	  (curow, down, cwin->numr, get_scaled_height, &(s.lr), &(s.hr), &off_dn);
932       else
933 	ret = scroll_axis
934 	  (curow, down, cwin->numr, get_scaled_height, &(s.lr), &(s.hr), &off_dn);
935       cr = (off_dn > s.hr - s.lr) ? s.hr : s.lr + off_dn;
936       if (ret)
937 	io_error_msg ("Can't scroll that far");
938       set_numcols (cwin, s.hr);
939     }
940   else
941     cr = curow;
942 
943   off_rt = cucol - cwin->screen.lc;
944 
945   s.lc = cwin->screen.lc;
946   s.hc = cwin->screen.hc;
947   if (over)
948     {
949       if (cwin->flags & WIN_PAG_HZ)
950 	ret = page_scroll_axis
951 	  (cucol, over, cwin->numc, get_scaled_width, &(s.lc), &(s.hc), &off_rt);
952       else
953 	ret = scroll_axis (cucol, over, cwin->numc, get_scaled_width, &(s.lc), &(s.hc), &off_rt);
954       if (ret)
955 	io_error_msg ("Can't scroll that far");
956       cc = (s.hc - s.lc < off_rt) ? s.hc : s.lc + off_rt;
957     }
958   else if ((cwin->flags & WIN_PAG_HZ) == 0)
959     /* ... */
960     cc = cucol;
961   else
962     cc = cucol;
963 
964   /*fixme The original has a big #if 0 here. */
965   if (cwin->link != -1)
966     shift_linked_window ((long) cr - curow, (long) cc - cucol);
967 
968   cwin->screen = s;
969   curow = cr;
970   cucol = cc;
971 
972   if (get_scaled_width (cucol) == 0)
973     find_nonzero (&cucol, cwin->screen.lc, cwin->screen.hc, get_scaled_width);
974   if (get_scaled_height (curow) == 0)
975     find_nonzero (&curow, cwin->screen.lr, cwin->screen.hr, get_scaled_height);
976 
977   io_repaint_win (cwin);
978   if (cwin->link > 0)
979     io_repaint_win (&wins[cwin->link]);
980 }
981 
982 void
io_recenter_cur_win(void)983 io_recenter_cur_win (void)
984 {
985   cwin->win_curow = curow;
986   cwin->win_cucol = cucol;
987   recenter_window (cwin);
988   io_repaint_win (cwin);
989   if (cwin->link > 0)
990     io_repaint_win (&wins[cwin->link]);
991 }
992 
993 void
io_recenter_all_win(void)994 io_recenter_all_win (void)
995 {
996   int n;
997   if (!nwin)
998     return;
999   cwin->win_curow = curow;
1000   cwin->win_cucol = cucol;
1001   for (n = 0; n < nwin; n++)
1002     recenter_window (&wins[n]);
1003   io_repaint ();
1004 }
1005 
1006 void
io_set_win_flags(struct window * w,int f)1007 io_set_win_flags (struct window *w, int f)
1008 {
1009   if ((f & WIN_EDGES) && !(w->flags & WIN_EDGES))
1010     {
1011       if (w->numr < 2 || w->numc < 6)
1012 	io_error_msg ("Edges wouldn't fit!");
1013       w->win_down++;
1014       w->numr--;
1015       set_numcols (w, w->screen.hr);
1016     }
1017   else if (!(f & WIN_EDGES) && (w->flags & WIN_EDGES))
1018     {
1019       w->win_over -= w->lh_wid;
1020       w->numc += w->lh_wid;
1021       w->lh_wid = 0;
1022       w->win_down--;
1023       w->numr++;
1024     }
1025   w->flags = f;
1026 }
1027 
1028 void
io_write_window_config(struct line * out)1029 io_write_window_config (struct line * out)
1030 {
1031   int n;
1032   char buf[90];
1033   struct line scratch;
1034   scratch.alloc = 0;
1035   scratch.buf = 0;
1036 
1037   cwin->win_curow = curow;
1038   cwin->win_cucol = cucol;
1039   sprint_line (out, "O;status %d\n", user_status);
1040   if (nwin > 1)
1041     {
1042       /* ... */ /* fixme ? */
1043     }
1044   for (n = 0; n < nwin; n++)
1045     {
1046       buf[0] = '\0';
1047       if (wins[n].flags & WIN_LCK_HZ)
1048 	strcat (buf, ",lockh");
1049       if (wins[n].flags & WIN_LCK_VT)
1050 	strcat (buf, ",lockv");
1051       if (wins[n].flags & WIN_PAG_HZ)
1052 	strcat (buf, ",pageh");
1053       if (wins[n].flags & WIN_PAG_VT)
1054 	strcat (buf, ",pagev");
1055       if (wins[n].flags & WIN_EDGE_REV)
1056 	strcat (buf, ",standout");
1057       if ((wins[n].flags & WIN_EDGES) == 0)
1058 	strcat (buf, ",noedges");
1059       scratch = *out;
1060       out->alloc = 0;
1061       out->buf = 0;
1062       sprint_line (out, "%sW;N%d;A%u %u;C%d %d %d;O%s\n",
1063 		   scratch.buf, n + 1, wins[n].win_curow, wins[n].win_cucol,
1064 		   7, 0, 7, buf + 1);
1065       free (scratch.buf);
1066     }
1067 }
1068 
1069 void
io_read_window_config(char * line)1070 io_read_window_config (char * line)
1071 {
1072   int wnum = 0;
1073   char *text;
1074   CELLREF nrow = NON_ROW, ncol = NON_COL;
1075   char *split = 0;
1076   char *opts = 0;
1077   struct window *win;
1078 
1079   text = line;
1080   for (;;)
1081     {
1082       switch (*text++)
1083 	{
1084 	  /* Window Number */
1085 	case 'N':
1086 	  wnum = astol (&text);
1087 	  break;
1088 	  /* Cursor At */
1089 	case 'A':
1090 	  nrow = astol (&text);
1091 	  ncol = astol (&text);
1092 	  break;
1093 	  /* JF: Window options */
1094 	case 'O':
1095 	  opts = text;
1096 	  while (*text && *text != ';')
1097 	    text++;
1098 	  break;
1099 	  /* Split into two windows */
1100 	case 'S':
1101 	  split = text;
1102 	  while (*text && *text != ';')
1103 	    text++;
1104 	  break;
1105 	  /* Set Colors NOT supported */
1106 	case 'C':
1107 	  while (*text && *text != ';')
1108 	    text++;
1109 	  break;
1110 	  /* Alternate border NOT supported. . . */
1111 	case 'B':
1112 	  break;
1113 	default:
1114 	  --text;
1115 	  break;
1116 	}
1117       if (*text == '\0' || *text == '\n')
1118 	break;
1119       if (*text != ';')
1120 	{
1121 	  char *bad;
1122 
1123 	  bad = text;
1124 	  while (*text && *text != ';')
1125 	    text++;
1126 	  if (*text)
1127 	    *text++ = '\0';
1128 	  io_error_msg ("Unknown SYLK window cmd: %s", bad);
1129 	  if (!*text)
1130 	    break;
1131 	}
1132       else
1133 	*text++ = '\0';
1134     }
1135   if (wnum < 1 || wnum > nwin)
1136     {
1137       io_error_msg ("Window %d out of range in SYLK line %s", wnum, line);
1138       return;
1139     }
1140   --wnum;
1141   win = &wins[wnum];
1142   if (nrow != NON_ROW)
1143     {
1144       win->win_curow = nrow;
1145       win->win_cucol = ncol;
1146       if (win == cwin)
1147 	{
1148 	  curow = nrow;
1149 	  cucol = ncol;
1150 	}
1151       recenter_window (win);
1152     }
1153   if (split)
1154     {
1155       int hv = 0;
1156       int where;
1157       int link;
1158       struct window *new;
1159 
1160       switch (*split++)
1161 	{
1162 	case 'H':
1163 	case 'h':
1164 	  hv = 1;
1165 	  break;
1166 	case 'v':
1167 	case 'V':
1168 	  hv = 0;
1169 	  break;
1170 	case 't':
1171 	case 'T':
1172 	  io_error_msg ("Window split titles not supported");
1173 	  return;
1174 	default:
1175 	  break;
1176 	}
1177       if (*split == 'L')
1178 	{
1179 	  link = wnum;
1180 	  split++;
1181 	}
1182       else
1183 	link = -1;
1184 
1185       where = astol (&split);
1186 
1187       if (hv ? where >= win->numr : where >= win->numc)
1188 	io_error_msg ("Can't split window: screen too small");
1189 
1190       nwin++;
1191       wins = ck_realloc (wins, nwin * sizeof (struct window));
1192       cwin = wins;
1193       win = &wins[wnum];
1194       new = &wins[nwin - 1];
1195 
1196       win->numc -= (hv ? 0 : where);
1197       win->numr -= (hv ? where : 0);
1198       win->win_curow = curow;
1199       win->win_cucol = cucol;
1200 
1201       new->flags = WIN_EDGES | WIN_EDGE_REV;	/* Mplan defaults */
1202       new->lh_wid = 0;		/* For now */
1203       new->link = link;
1204 
1205       new->win_over = win->win_over + (hv ? -win->lh_wid : win->numc);
1206       new->win_down = win->win_down + (hv ? win->numr + 1 : 0);
1207       new->numc = (hv ? win->numc + win->lh_wid : where);
1208       new->numr = (hv ? where - 1 : win->numr);
1209       new->win_curow = curow;
1210       new->win_cucol = cucol;
1211       set_numcols (new, curow);
1212       recenter_window (win);
1213       recenter_window (new);
1214     }
1215   if (opts)
1216     {
1217       char *np;
1218       while ((np =(char *) index (opts, ',')))
1219 	{
1220 	  *np = '\0';
1221 	  set_options (opts);
1222 	  *np++ = ';';
1223 	  opts = np;
1224 	}
1225       if ((np = (char *)rindex (opts, '\n')))
1226 	*np = '\0';
1227       set_options (opts);
1228     }
1229 }
1230 
1231 static void
init_mouse(void)1232 init_mouse (void)
1233 {
1234   Global->current_mouse = Global->free_mouse =
1235   (struct mouse_event *) ck_malloc (sizeof (struct mouse_event));
1236   Global->free_mouse->next = Global->free_mouse;
1237   Global->free_mouse->prev = Global->free_mouse;
1238 }
1239 
1240 static int mouse_location ();
1241 
1242 int
enqueue_mouse_event(int r,int c,int button,int downp)1243 enqueue_mouse_event (int r, int c, int button, int downp)
1244 {
1245   struct mouse_event *m = Global->free_mouse;
1246   if (m->next == Global->current_mouse)
1247     {
1248       m->next =
1249 	(struct mouse_event *) ck_malloc (sizeof (struct mouse_event));
1250       m->next->prev = m;
1251       m->next->next = Global->current_mouse;
1252       Global->current_mouse->prev = m->next;
1253       m->seq = Global->mouse_id++;
1254       if (m->seq > 255)
1255 	panic ("Too many mouse events enqueued.");
1256     }
1257   Global->free_mouse = m->next;
1258   m->row = r;
1259   m->col = c;
1260   m->button = button;
1261   m->downp = downp;
1262   m->location = mouse_location (&m->r, &m->c, m);
1263   return m->seq;
1264 }
1265 
1266 void
dequeue_mouse_event(struct mouse_event * out,int seq)1267 dequeue_mouse_event (struct mouse_event *out, int seq)
1268 {
1269   Global->free_mouse->seq = seq;
1270   while (Global->current_mouse->seq != seq)
1271     Global->current_mouse = Global->current_mouse->next;
1272   if (Global->current_mouse == Global->free_mouse)
1273     {
1274       out->seq = seq;
1275       out->button = MOUSE_QERROR;
1276       return;
1277     }
1278   *out = *Global->current_mouse;
1279   out->next = out->prev = 0;
1280   Global->current_mouse = Global->current_mouse->next;
1281 }
1282 
1283 
1284 
1285 void
io_init_windows(int sl,int sc,int ui,int us,int ir,int sr,int lr,int lc)1286 io_init_windows (int sl, int sc, int ui, int us, int ir, int sr,
1287 		 int lr, int lc)
1288 {
1289   print_width = 80;		/* default ascii print width */
1290   Global->scr_lines = sl;
1291   Global->scr_cols = sc;
1292   input_rows = ir;
1293   status_rows = sr;
1294   label_rows = lr;
1295   label_emcols = lc;
1296   io_set_input_status (ui, us, 0);
1297   nwin = 1;
1298   wins = cwin = ck_malloc (sizeof (struct window));
1299   wins->id = win_id++;
1300   wins->win_over = 0;		/* This will be fixed by a future set_numcols */
1301   wins->win_down = (label_rows
1302 		    + (user_status > 0) * status_rows
1303 		    + (user_input > 0) * input_rows);
1304   wins->flags = WIN_EDGES | WIN_EDGE_REV;
1305   wins->numr = (Global->scr_lines - label_rows - !!user_status * status_rows
1306 		- input_rows - default_bottom_border);
1307   wins->numc = Global->scr_cols - default_right_border;
1308   wins->bottom_edge_r = default_bottom_border;
1309   wins->right_edge_c = default_right_border;
1310   wins->link = -1;
1311   wins->lh_wid = 0;
1312   wins->win_curow = MIN_ROW;
1313   wins->win_cucol = MIN_COL;
1314   wins->win_slops = 0;
1315   init_mouse ();
1316 }
1317 
1318 static int
mouse_location(CELLREF * cr,CELLREF * cc,struct mouse_event * ev)1319 mouse_location (CELLREF *cr, CELLREF *cc, struct mouse_event *ev)
1320 {
1321   int n;
1322   if (ev->row >= Global->input && ev->row <= Global->input + input_rows)
1323     return MOUSE_ON_INPUT;
1324   if (user_status && ev->row >= Global->status
1325       && ev->row <= Global->status + status_rows)
1326     return MOUSE_ON_STATUS;
1327   for (n = 0; n < nwin; ++n)
1328     {
1329       struct window *w = &wins[n];
1330       if (ev->row >= w->win_down
1331 	  && ev->row < w->win_down + w->numr
1332 	  && ev->col < w->win_over + w->numc
1333 	  && ev->col >= w->win_over)
1334 	{
1335 	  int row_off = ev->row - w->win_down;
1336 	  int col_off = ev->col - w->win_over;
1337 	  int rh = 0;
1338 	  int cw = 0;
1339 	  CELLREF c, r;
1340 	  for (c = w->screen.lc; c <= w->screen.hc; ++c)
1341 	    if ((cw += get_scaled_width (c)) > col_off)
1342 	      break;
1343 	  *cc = c;
1344 	  for (r = w->screen.lr; r <= w->screen.hr; ++r)
1345 	    if ((rh += get_scaled_height (r)) > row_off)
1346 	      break;
1347 	  *cr = r;
1348 	  return n;
1349 	}
1350     }
1351   return MOUSE_ON_EDGE;
1352 }
1353 
1354