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