ncurses 4.1
[ncurses.git] / panel / panel.c
1
2 /***************************************************************************
3 *                            COPYRIGHT NOTICE                              *
4 ****************************************************************************
5 *                     panels is copyright (C) 1995                         *
6 *                          Zeyd M. Ben-Halim                               *
7 *                          zmbenhal@netcom.com                             *
8 *                          Eric S. Raymond                                 *
9 *                          esr@snark.thyrsus.com                           *
10 *                                                                          *
11 *             All praise to the original author, Warren Tucker.            *
12 *                                                                          *
13 *        Permission is hereby granted to reproduce and distribute panels   *
14 *        by any means and for any fee, whether alone or as part of a       *
15 *        larger distribution, in source or in binary form, PROVIDED        *
16 *        this notice is included with any such distribution, and is not    *
17 *        removed from any of its header files. Mention of panels in any    *
18 *        applications linked with it is highly appreciated.                *
19 *                                                                          *
20 *        panels comes AS IS with no warranty, implied or expressed.        *
21 *                                                                          *
22 ***************************************************************************/
23
24 /* panel.c -- implementation of panels library */
25 #include "panel.priv.h"
26
27 MODULE_ID("$Id: panel.c,v 1.10 1997/05/05 09:42:11 tom Exp $")
28
29 #ifdef TRACE
30 extern char *_nc_visbuf(const char *);
31 #ifdef TRACE_TXT
32 #define USER_PTR(ptr) _nc_visbuf((const char *)ptr)
33 #else
34 static char *my_nc_visbuf(const void *ptr)
35 {
36         char temp[20];
37         if (ptr != 0)
38                 sprintf(temp, "ptr:%p", ptr);
39         else
40                 strcpy(temp, "<null>");
41         return _nc_visbuf(temp);
42 }
43 #define USER_PTR(ptr) my_nc_visbuf((const char *)ptr)
44 #endif
45 #endif
46
47 static PANEL *__bottom_panel = (PANEL *)0;
48 static PANEL *__top_panel    = (PANEL *)0;
49
50 static PANEL __stdscr_pseudo_panel = { (WINDOW *)0,
51                                        0,0,0,0,
52                                        (PANEL *)0, (PANEL *)0,
53                                        (void *)0,
54                                        (PANELCONS *)0 };
55
56 /* Prototypes */
57 static void __panel_link_bottom(PANEL *pan);
58
59 /*+-------------------------------------------------------------------------
60         dPanel(text,pan)
61 --------------------------------------------------------------------------*/
62 #ifdef TRACE
63 static void
64 dPanel(const char *text, const PANEL *pan)
65 {
66         _tracef("%s id=%s b=%s a=%s y=%d x=%d",
67                 text, USER_PTR(pan->user),
68                 (pan->below) ?  USER_PTR(pan->below->user) : "--",
69                 (pan->above) ?  USER_PTR(pan->above->user) : "--",
70                 pan->wstarty, pan->wstartx);
71 } /* end of dPanel */
72 #else
73 #  define dPanel(text,pan)
74 #endif
75
76 /*+-------------------------------------------------------------------------
77         dStack(fmt,num,pan)
78 --------------------------------------------------------------------------*/
79 #ifdef TRACE
80 static void
81 dStack(const char *fmt, int num, const PANEL *pan)
82 {
83   char s80[80];
84
85   sprintf(s80,fmt,num,pan);
86   _tracef("%s b=%s t=%s",s80,
87           (__bottom_panel) ?  USER_PTR(__bottom_panel->user) : "--",
88           (__top_panel)    ?  USER_PTR(__top_panel->user)    : "--");
89   if(pan)
90     _tracef("pan id=%s", USER_PTR(pan->user));
91   pan = __bottom_panel;
92   while(pan)
93     {
94       dPanel("stk",pan);
95       pan = pan->above;
96     }
97 } /* end of dStack */
98 #else
99 #  define dStack(fmt,num,pan)
100 #endif
101
102 /*+-------------------------------------------------------------------------
103         Wnoutrefresh(pan) - debugging hook for wnoutrefresh
104 --------------------------------------------------------------------------*/
105 #ifdef TRACE
106 static void
107 Wnoutrefresh(const PANEL *pan)
108 {
109   dPanel("wnoutrefresh",pan);
110   wnoutrefresh(pan->win);
111 } /* end of Wnoutrefresh */
112 #else
113 #  define Wnoutrefresh(pan) wnoutrefresh((pan)->win)
114 #endif
115
116 /*+-------------------------------------------------------------------------
117         Touchpan(pan)
118 --------------------------------------------------------------------------*/
119 #ifdef TRACE
120 static void
121 Touchpan(const PANEL *pan)
122 {
123   dPanel("Touchpan",pan);
124   touchwin(pan->win);
125 } /* end of Touchpan */
126 #else
127 #  define Touchpan(pan) touchwin((pan)->win)
128 #endif
129
130 /*+-------------------------------------------------------------------------
131         Touchline(pan,start,count)
132 --------------------------------------------------------------------------*/
133 #ifdef TRACE
134 static void
135 Touchline(const PANEL *pan, int start, int count)
136 {
137   char s80[80];
138   sprintf(s80,"Touchline s=%d c=%d",start,count);
139   dPanel(s80,pan);
140   touchline(pan->win,start,count);
141 } /* end of Touchline */
142 #else
143 #  define Touchline(pan,start,count) touchline((pan)->win,start,count)
144 #endif
145
146 /*+-------------------------------------------------------------------------
147         __panels_overlapped(pan1,pan2) - check panel overlapped
148 --------------------------------------------------------------------------*/
149 static INLINE bool
150 __panels_overlapped(register const PANEL *pan1, register const PANEL *pan2)
151 {
152   if(!pan1 || !pan2)
153     return(FALSE);
154   dBug(("__panels_overlapped %s %s", USER_PTR(pan1->user), USER_PTR(pan2->user)));
155   /* pan1 intersects with pan2 ? */
156   if((pan1->wstarty >= pan2->wstarty) && (pan1->wstarty < pan2->wendy) &&
157      (pan1->wstartx >= pan2->wstartx) && (pan1->wstartx < pan2->wendx))
158     return(TRUE);
159   /* or vice versa test */
160   if((pan2->wstarty >= pan1->wstarty) && (pan2->wstarty < pan1->wendy) &&
161      (pan2->wstartx >= pan1->wstartx) && (pan2->wstartx < pan1->wendx))
162     return(TRUE);
163   dBug(("  no"));
164   return(FALSE);
165 } /* end of __panels_overlapped */
166
167 /*+-------------------------------------------------------------------------
168         __free_obscure(pan)
169 --------------------------------------------------------------------------*/
170 static INLINE void
171 __free_obscure(PANEL *pan)
172 {
173   PANELCONS *tobs = pan->obscure;                       /* "this" one */
174   PANELCONS *nobs;                                      /* "next" one */
175
176   while(tobs)
177     {
178       nobs = tobs->above;
179       free((char *)tobs);
180       tobs = nobs;
181     }
182   pan->obscure = (PANELCONS *)0;
183 } /* end of __free_obscure */
184
185 /*+-------------------------------------------------------------------------
186   Get root (i.e. stdscr's) panel.
187   Establish the pseudo panel for stdscr if necessary.
188 --------------------------------------------------------------------------*/
189 static PANEL*
190 __root_panel(void)
191 {
192   if(!__stdscr_pseudo_panel.win)
193     { /* initialize those fields not already statically initialized */
194       assert(stdscr && !__bottom_panel && !__top_panel);
195       __stdscr_pseudo_panel.win = stdscr;
196       __stdscr_pseudo_panel.wendy = LINES;
197       __stdscr_pseudo_panel.wendx = COLS;
198 #ifdef TRACE
199       __stdscr_pseudo_panel.user = "stdscr";
200 #endif
201       __panel_link_bottom(&__stdscr_pseudo_panel);
202     }
203   return &__stdscr_pseudo_panel;
204 }
205
206 /*+-------------------------------------------------------------------------
207         __override(pan,show)
208 --------------------------------------------------------------------------*/
209 static void
210 __override(const PANEL *pan, int show)
211 {
212   int y;
213   PANEL *pan2;
214   PANELCONS *tobs = pan->obscure;                          /* "this" one */
215
216   dBug(("__override %s,%d", USER_PTR(pan->user),show));
217
218   switch (show)
219     {
220     case P_TOUCH:
221       Touchpan(pan);
222       /* The following while loop will now mark all panel window lines
223        * obscured by use or obscuring us as touched, so they will be
224        * updated.
225        */
226       break;
227     case P_UPDATE:
228       while(tobs && (tobs->pan != pan))
229         tobs = tobs->above;
230       /* The next loop will now only go through the panels obscuring pan;
231        * it updates all the lines in the obscuring panels in sync. with
232        * the lines touched in pan itself. This is called in update_panels()
233        * in a loop from the bottom_panel to the top_panel, resulting in
234        * the desired update effect.
235        */
236       break;
237     default:
238       return;
239     }
240
241   while(tobs)
242     {
243       if((pan2 = tobs->pan) != pan) {
244         dBug(("test obs pan=%s pan2=%s", USER_PTR(pan->user), USER_PTR(pan2->user)));
245         for(y = pan->wstarty; y < pan->wendy; y++) {
246           if( (y >= pan2->wstarty) && (y < pan2->wendy) &&
247               ((is_linetouched(pan->win,y - pan->wstarty) == TRUE)) )
248             Touchline(pan2,y - pan2->wstarty,1);
249         }
250       }
251       tobs = tobs->above;
252     }
253 } /* end of __override */
254
255 /*+-------------------------------------------------------------------------
256         __calculate_obscure()
257 --------------------------------------------------------------------------*/
258 static void
259 __calculate_obscure(void)
260 {
261   PANEL *pan;
262   PANEL *pan2;
263   PANELCONS *tobs;                      /* "this" one */
264   PANELCONS *lobs = (PANELCONS *)0;     /* last one */
265
266   pan = __bottom_panel;
267   while(pan)
268     {
269       if(pan->obscure)
270         __free_obscure(pan);
271       dBug(("--> __calculate_obscure %s", USER_PTR(pan->user)));
272       lobs = (PANELCONS *)0;            /* last one */
273       pan2 = __bottom_panel;
274       /* This loop builds a list of panels obsured by pan or obscuring
275          pan; pan itself is in the list; all panels before pan are
276          obscured by pan, all panels after pan are obscuring pan. */
277       while(pan2)
278         {
279           if(__panels_overlapped(pan,pan2))
280             {
281               if(!(tobs = (PANELCONS *)malloc(sizeof(PANELCONS))))
282                 return;
283               tobs->pan = pan2;
284               dPanel("obscured",pan2);
285               tobs->above = (PANELCONS *)0;
286               if(lobs)
287                 lobs->above = tobs;
288               else
289                 pan->obscure = tobs;
290               lobs  = tobs;
291             }
292           pan2 = pan2->above;
293         }
294       __override(pan,P_TOUCH);
295       pan = pan->above;
296     }
297 } /* end of __calculate_obscure */
298
299 /*+-------------------------------------------------------------------------
300         __panel_is_linked(pan) - check to see if panel is in the stack
301 --------------------------------------------------------------------------*/
302 static INLINE bool
303 __panel_is_linked(const PANEL *pan)
304 {
305   /* This works! The only case where it would fail is, when the list has
306      only one element. But this could only be the pseudo panel at the bottom */
307   return ( ((pan->above!=(PANEL *)0) ||
308             (pan->below!=(PANEL *)0) ||
309             (pan==__bottom_panel)) ? TRUE : FALSE );
310 } /* end of __panel_is_linked */
311
312 /*+-------------------------------------------------------------------------
313         __panel_link_top(pan) - link panel into stack at top
314 --------------------------------------------------------------------------*/
315 static void
316 __panel_link_top(PANEL *pan)
317 {
318 #ifdef TRACE
319   dStack("<lt%d>",1,pan);
320   if(__panel_is_linked(pan))
321     return;
322 #endif
323
324   pan->above = (PANEL *)0;
325   pan->below = (PANEL *)0;
326   if(__top_panel)
327     {
328       __top_panel->above = pan;
329       pan->below = __top_panel;
330     }
331   __top_panel = pan;
332   if(!__bottom_panel)
333     __bottom_panel = pan;
334   __calculate_obscure();
335   dStack("<lt%d>",9,pan);
336
337 } /* end of __panel_link_top */
338
339 /*+-------------------------------------------------------------------------
340         __panel_link_bottom(pan) - link panel into stack at bottom
341 --------------------------------------------------------------------------*/
342 static void
343 __panel_link_bottom(PANEL *pan)
344 {
345 #ifdef TRACE
346   dStack("<lb%d>",1,pan);
347   if(__panel_is_linked(pan))
348     return;
349 #endif
350
351   pan->above = (PANEL *)0;
352   pan->below = (PANEL *)0;
353   if(__bottom_panel)
354     { /* the stdscr pseudo panel always stays real bottom;
355          so we insert after bottom panel*/
356       pan->below = __bottom_panel;
357       pan->above = __bottom_panel->above;
358       if (pan->above)
359         pan->above->below = pan;
360       __bottom_panel->above = pan;
361     }
362   else
363     __bottom_panel = pan;
364   if(!__top_panel)
365     __top_panel = pan;
366   assert(__bottom_panel == &__stdscr_pseudo_panel);
367   __calculate_obscure();
368   dStack("<lb%d>",9,pan);
369 } /* end of __panel_link_bottom */
370
371 /*+-------------------------------------------------------------------------
372         __panel_unlink(pan) - unlink panel from stack
373 --------------------------------------------------------------------------*/
374 static void
375 __panel_unlink(PANEL *pan)
376 {
377   PANEL *prev;
378   PANEL *next;
379
380 #ifdef TRACE
381   dStack("<u%d>",1,pan);
382   if(!__panel_is_linked(pan))
383     return;
384 #endif
385
386   __override(pan,P_TOUCH);
387   __free_obscure(pan);
388
389   prev = pan->below;
390   next = pan->above;
391
392   if(prev)
393     { /* if non-zero, we will not update the list head */
394       prev->above = next;
395       if(next)
396         next->below = prev;
397     }
398   else if(next)
399     next->below = prev;
400   if(pan == __bottom_panel)
401     __bottom_panel = next;
402   if(pan == __top_panel)
403     __top_panel = prev;
404
405   __calculate_obscure();
406
407   pan->above = (PANEL *)0;
408   pan->below = (PANEL *)0;
409   dStack("<u%d>",9,pan);
410 } /* end of __panel_unlink */
411
412 /*+-------------------------------------------------------------------------
413         panel_window(pan) - get window associated with panel
414 --------------------------------------------------------------------------*/
415 WINDOW *
416 panel_window(const PANEL *pan)
417 {
418   return(pan ? pan->win : (WINDOW *)0);
419 } /* end of panel_window */
420
421 /*+-------------------------------------------------------------------------
422         update_panels() - wnoutrefresh windows in an orderly fashion
423 --------------------------------------------------------------------------*/
424 void
425 update_panels(void)
426 {
427   PANEL *pan;
428
429   dBug(("--> update_panels"));
430   pan = __bottom_panel;
431   while(pan)
432     {
433       __override(pan,P_UPDATE);
434       pan = pan->above;
435     }
436
437   pan = __bottom_panel;
438   while (pan)
439     {
440       if(is_wintouched(pan->win))
441         Wnoutrefresh(pan);
442       pan = pan->above;
443     }
444 } /* end of update_panels */
445
446 /*+-------------------------------------------------------------------------
447         hide_panel(pan) - remove a panel from stack
448 --------------------------------------------------------------------------*/
449 int
450 hide_panel(register PANEL *pan)
451 {
452   if(!pan)
453     return(ERR);
454
455   dBug(("--> hide_panel %s", USER_PTR(pan->user)));
456
457   if(!__panel_is_linked(pan))
458     {
459       pan->above = (PANEL *)0;
460       pan->below = (PANEL *)0;
461       return(ERR);
462     }
463
464   __panel_unlink(pan);
465   return(OK);
466 } /* end of hide_panel */
467
468 /*+-------------------------------------------------------------------------
469         show_panel(pan) - place a panel on top of stack
470 may already be in stack
471 --------------------------------------------------------------------------*/
472 int
473 show_panel(register PANEL *pan)
474 {
475   if(!pan)
476     return(ERR);
477   if(pan == __top_panel)
478     return(OK);
479   dBug(("--> show_panel %s", USER_PTR(pan->user)));
480   if(__panel_is_linked(pan))
481     (void)hide_panel(pan);
482   __panel_link_top(pan);
483   return(OK);
484 } /* end of show_panel */
485
486 /*+-------------------------------------------------------------------------
487         top_panel(pan) - place a panel on top of stack
488 --------------------------------------------------------------------------*/
489 int
490 top_panel(register PANEL *pan)
491 {
492   return(show_panel(pan));
493 } /* end of top_panel */
494
495 /*+-------------------------------------------------------------------------
496         del_panel(pan) - remove a panel from stack, if in it, and free struct
497 --------------------------------------------------------------------------*/
498 int
499 del_panel(register PANEL *pan)
500 {
501   if(pan)
502     {
503       dBug(("--> del_panel %s", USER_PTR(pan->user)));
504       if(__panel_is_linked(pan))
505         (void)hide_panel(pan);
506       free((void *)pan);
507       return(OK);
508     }
509   return(ERR);
510 } /* end of del_panel */
511
512 /*+-------------------------------------------------------------------------
513         bottom_panel(pan) - place a panel on bottom of stack
514 may already be in stack
515 --------------------------------------------------------------------------*/
516 int
517 bottom_panel(register PANEL *pan)
518 {
519   if(!pan)
520     return(ERR);
521   if(pan == __bottom_panel)
522     return(OK);
523   dBug(("--> bottom_panel %s", USER_PTR(pan->user)));
524   if(__panel_is_linked(pan))
525     (void)hide_panel(pan);
526   __panel_link_bottom(pan);
527   return(OK);
528 } /* end of bottom_panel */
529
530 /*+-------------------------------------------------------------------------
531         new_panel(win) - create a panel and place on top of stack
532 --------------------------------------------------------------------------*/
533 PANEL *
534 new_panel(WINDOW *win)
535 {
536   PANEL *pan = (PANEL *)malloc(sizeof(PANEL));
537
538   (void)__root_panel();
539
540   if(pan)
541     {
542       pan->win = win;
543       pan->above = (PANEL *)0;
544       pan->below = (PANEL *)0;
545       getbegyx(win, pan->wstarty, pan->wstartx);
546       pan->wendy = pan->wstarty + getmaxy(win);
547       pan->wendx = pan->wstartx + getmaxx(win);
548 #ifdef TRACE
549       pan->user = "new";
550 #else
551       pan->user = (char *)0;
552 #endif
553       pan->obscure = (PANELCONS *)0;
554       (void)show_panel(pan);
555     }
556   return(pan);
557 } /* end of new_panel */
558
559 /*+-------------------------------------------------------------------------
560         panel_above(pan)
561 --------------------------------------------------------------------------*/
562 PANEL *
563 panel_above(const PANEL *pan)
564 {
565   if(!pan)
566     {
567       /* if top and bottom are equal, we have no or only the pseudo panel;
568          if not, we return the panel above the pseudo panel */
569       return(__bottom_panel==__top_panel ? (PANEL*)0 : __bottom_panel->above);
570     }
571   else
572     return(pan->above);
573 } /* end of panel_above */
574
575 /*+-------------------------------------------------------------------------
576         panel_below(pan)
577 --------------------------------------------------------------------------*/
578 PANEL *
579 panel_below(const PANEL *pan)
580 {
581   if(!pan)
582     {
583       /* if top and bottom are equal, we have no or only the pseudo panel */
584       return(__top_panel==__bottom_panel ? (PANEL*)0 : __top_panel);
585     }
586   else
587     {
588       /* we must not return the pseudo panel */
589       return(pan->below==__bottom_panel ? (PANEL*) 0 : pan->below);
590     }
591 } /* end of panel_below */
592
593 /*+-------------------------------------------------------------------------
594         set_panel_userptr(pan,uptr)
595 --------------------------------------------------------------------------*/
596 int
597 set_panel_userptr(PANEL *pan, const void *uptr)
598 {
599   if(!pan)
600     return(ERR);
601   pan->user = uptr;
602   return(OK);
603 } /* end of set_panel_userptr */
604
605 /*+-------------------------------------------------------------------------
606         panel_userptr(pan)
607 --------------------------------------------------------------------------*/
608 const void*
609 panel_userptr(const PANEL *pan)
610 {
611   return(pan ? pan->user : (void *)0);
612 } /* end of panel_userptr */
613
614 /*+-------------------------------------------------------------------------
615         move_panel(pan,starty,startx)
616 --------------------------------------------------------------------------*/
617 int
618 move_panel(PANEL *pan, int starty, int startx)
619 {
620   WINDOW *win;
621
622   if(!pan)
623     return(ERR);
624   if(__panel_is_linked(pan))
625     __override(pan,P_TOUCH);
626   win = pan->win;
627   if(mvwin(win,starty,startx))
628     return(ERR);
629   getbegyx(win, pan->wstarty, pan->wstartx);
630   pan->wendy = pan->wstarty + getmaxy(win);
631   pan->wendx = pan->wstartx + getmaxx(win);
632   if(__panel_is_linked(pan))
633     __calculate_obscure();
634   return(OK);
635 } /* end of move_panel */
636
637 /*+-------------------------------------------------------------------------
638         replace_panel(pan,win)
639 --------------------------------------------------------------------------*/
640 int
641 replace_panel(PANEL *pan, WINDOW *win)
642 {
643   if(!pan)
644     return(ERR);
645   if(__panel_is_linked(pan))
646     __override(pan,P_TOUCH);
647   pan->win = win;
648   if(__panel_is_linked(pan))
649     __calculate_obscure();
650   return(OK);
651 } /* end of replace_panel */
652
653 /*+-------------------------------------------------------------------------
654         panel_hidden(pan)
655 --------------------------------------------------------------------------*/
656 int
657 panel_hidden(const PANEL *pan)
658 {
659   if(!pan)
660     return(ERR);
661   return(__panel_is_linked(pan) ? TRUE : FALSE);
662 } /* end of panel_hidden */
663
664 /* end of panel.c */