ncurses 4.1
[ncurses.git] / menu / m_global.c
1 /*-----------------------------------------------------------------------------+
2 |           The ncurses menu library is  Copyright (C) 1995-1997               |
3 |             by Juergen Pfeifer <Juergen.Pfeifer@T-Online.de>                 |
4 |                          All Rights Reserved.                                |
5 |                                                                              |
6 | Permission to use, copy, modify, and distribute this software and its        |
7 | documentation for any purpose and without fee is hereby granted, provided    |
8 | that the above copyright notice appear in all copies and that both that      |
9 | copyright notice and this permission notice appear in supporting             |
10 | documentation, and that the name of the above listed copyright holder(s) not |
11 | be used in advertising or publicity pertaining to distribution of the        |
12 | software without specific, written prior permission.                         | 
13 |                                                                              |
14 | THE ABOVE LISTED COPYRIGHT HOLDER(S) DISCLAIM ALL WARRANTIES WITH REGARD TO  |
15 | THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FIT-  |
16 | NESS, IN NO EVENT SHALL THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR   |
17 | ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RE- |
18 | SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, |
19 | NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH    |
20 | THE USE OR PERFORMANCE OF THIS SOFTWARE.                                     |
21 +-----------------------------------------------------------------------------*/
22
23 /***************************************************************************
24 * Module menu_global                                                       *
25 * Globally used internal routines and the default menu and item structures *
26 ***************************************************************************/
27
28 #include "menu.priv.h"
29
30 MODULE_ID("$Id: m_global.c,v 1.7 1997/05/01 16:47:26 juergen Exp $")
31
32 MENU _nc_Default_Menu = {
33   16,                             /* Nr. of chars high */
34   1,                              /* Nr. of chars wide */
35   16,                             /* Nr. of items high */
36   1,                              /* Nr. of items wide */
37   16,                             /* Nr. of formatted items high */
38   1,                              /* Nr. of formatted items wide */
39   16,                             /* Nr. of items high (actual) */
40   0,                              /* length of widest name */
41   0,                              /* length of widest description */
42   1,                              /* length of mark */
43   1,                              /* length of one item */
44   1,                              /* Spacing for descriptor */ 
45   1,                              /* Spacing for columns */
46   1,                              /* Spacing for rows */
47   (char *)0,                      /* buffer used to store match chars */
48   0,                              /* Index into pattern buffer */
49   (WINDOW *)0,                    /* Window containing entire menu */
50   (WINDOW *)0,                    /* Portion of menu displayed */
51   (WINDOW *)0,                    /* User's window */
52   (WINDOW *)0,                    /* User's subwindow */
53   (ITEM **)0,                     /* List of items */
54   0,                              /* Total Nr. of items in menu */
55   (ITEM *)0,                      /* Current item */
56   0,                              /* Top row of menu */
57   (chtype)A_REVERSE,              /* Attribute for selection */
58   (chtype)A_NORMAL,               /* Attribute for nonselection */
59   (chtype)A_UNDERLINE,            /* Attribute for inactive */  
60   ' ',                            /* Pad character */
61   (Menu_Hook)0,                   /* Menu init */
62   (Menu_Hook)0,                   /* Menu term */
63   (Menu_Hook)0,                   /* Item init */
64   (Menu_Hook)0,                   /* Item term */
65   (void *)0,                      /* userptr */
66   "-",                            /* mark */
67   ALL_MENU_OPTS,                  /* options */
68   0                               /* status */      
69 };
70
71 ITEM _nc_Default_Item = {
72   { (char *)0, 0 },               /* name */
73   { (char *)0, 0 },               /* description */
74   (MENU *)0,                      /* Pointer to parent menu */
75   (char *)0,                      /* Userpointer */
76   ALL_ITEM_OPTS,                  /* options */
77   0,                              /* Item Nr. */
78   0,                              /* y */
79   0,                              /* x */
80   FALSE,                          /* value */
81   (ITEM *)0,                      /* left */
82   (ITEM *)0,                      /* right */
83   (ITEM *)0,                      /* up */
84   (ITEM *)0                       /* down */
85   };
86
87 /*---------------------------------------------------------------------------
88 |   Facility      :  libnmenu  
89 |   Function      :  static void ComputeMaximum_NameDesc_Lenths(MENU *menu)
90 |   
91 |   Description   :  Calculates the maximum name and description lengths
92 |                    of the items connected to the menu
93 |
94 |   Return Values :  -
95 +--------------------------------------------------------------------------*/
96 INLINE static void ComputeMaximum_NameDesc_Lengths(MENU * menu)
97 {
98   unsigned MaximumNameLength        = 0;
99   unsigned MaximumDescriptionLength = 0;
100   ITEM **items;
101   
102   assert(menu && menu->items);
103   for( items = menu->items; *items ; items++ )
104     {
105       if (items[0]->name.length > MaximumNameLength )
106         MaximumNameLength  = items[0]->name.length;
107       
108       if (items[0]->description.length > MaximumDescriptionLength)
109         MaximumDescriptionLength = items[0]->description.length;
110     }
111   
112   menu->namelen = MaximumNameLength;
113   menu->desclen = MaximumDescriptionLength;
114 }
115
116 /*---------------------------------------------------------------------------
117 |   Facility      :  libnmenu  
118 |   Function      :  static void ResetConnectionInfo(MENU *, ITEM **)
119 |   
120 |   Description   :  Reset all informations in the menu and the items in
121 |                    the item array that indicates a connection
122 |
123 |   Return Values :  -
124 +--------------------------------------------------------------------------*/
125 INLINE static void ResetConnectionInfo(MENU *menu, ITEM **items)
126 {
127   ITEM **item;
128   
129   assert(menu && items);
130   for(item=items; *item; item++)
131     {
132       (*item)->index = 0;
133       (*item)->imenu = (MENU *)0;               
134     }
135   if (menu->pattern)
136     free(menu->pattern);
137   menu->pattern = (char *)0;
138   menu->pindex  = 0;
139   menu->items   = (ITEM **)0;
140   menu->nitems  = 0;
141 }
142
143 /*---------------------------------------------------------------------------
144 |   Facility      :  libnmenu  
145 |   Function      :  bool _nc_Connect_Items(MENU *menu, ITEM **items)
146 |
147 |   Description   :  Connect the items in the item array to the menu.
148 |                    Decorate all the items with a number and a backward
149 |                    pointer to the menu.
150 |
151 |   Return Values :  TRUE       - successfull connection
152 |                    FALSE      - connection failed
153 +--------------------------------------------------------------------------*/
154 bool _nc_Connect_Items(MENU *menu, ITEM **items)
155 {
156   ITEM **item;
157   unsigned int ItemCount = 0;
158   
159   if ( menu && items )
160     {    
161       for(item=items; *item ; item++)
162         {
163           if ( (*item)->imenu )
164             {
165               /* if a item is already connected, reject connection */
166               break;
167             }
168         }
169       if (! (*item) )           
170         /* we reached the end, so there was no connected item */
171         {
172           for(item=items; *item ; item++)
173             {
174               if (menu->opt & O_ONEVALUE)
175                 {
176                   (*item)->value = FALSE;
177                 }
178               (*item)->index = ItemCount++;
179               (*item)->imenu = menu;
180             }                   
181         }
182     }
183   else
184     return(FALSE);
185   
186   if (ItemCount != 0)
187     {
188       menu->items  = items;
189       menu->nitems = ItemCount;
190       ComputeMaximum_NameDesc_Lengths(menu);
191       if ( (menu->pattern = (char *)malloc( (unsigned)(1 + menu->namelen))) )
192         {
193           Reset_Pattern(menu);  
194           set_menu_format(menu,menu->frows,menu->fcols);
195           menu->curitem = *items;
196           menu->toprow = 0;
197           return(TRUE);
198         }
199     }
200   
201   /* If we fall through to this point, we have to reset all items connection 
202      and inform about a reject connection */
203   ResetConnectionInfo( menu, items );
204   return(FALSE);
205 }
206
207 /*---------------------------------------------------------------------------
208 |   Facility      :  libnmenu  
209 |   Function      :  void _nc_Disconnect_Items(MENU *menu)
210 |   
211 |   Description   :  Disconnect the menus item array from the menu
212 |
213 |   Return Values :  -
214 +--------------------------------------------------------------------------*/
215 void _nc_Disconnect_Items(MENU * menu)
216 {
217   if (menu && menu->items)
218     ResetConnectionInfo( menu, menu->items );
219 }
220
221 /*---------------------------------------------------------------------------
222 |   Facility      :  libnmenu  
223 |   Function      :  void _nc_Calculate_Item_Length_and_Width(MENU *menu)
224 |   
225 |   Description   :  Calculate the length of an item and the width of the
226 |                    whole menu.
227 |
228 |   Return Values :  -
229 +--------------------------------------------------------------------------*/
230 void _nc_Calculate_Item_Length_and_Width(MENU * menu)
231 {
232   int l;
233   
234   assert(menu);
235
236   menu->height  = 1 + menu->spc_rows * (menu->arows - 1);
237
238   l = menu->namelen + menu->marklen;
239   if ( (menu->opt & O_SHOWDESC) && (menu->desclen > 0) )
240     l += (menu->desclen + menu->spc_desc);
241   
242   menu->itemlen = l;
243   l *= menu->cols;
244   l += (menu->cols-1)*menu->spc_cols; /* for the padding between the columns */
245   menu->width = l;
246 }  
247
248 /*---------------------------------------------------------------------------
249 |   Facility      :  libnmenu  
250 |   Function      :  void _nc_Link_Item(MENU *menu)
251 |   
252 |   Description   :  Statically calculate for every item its four neighbours.
253 |                    This depends on the orientation of the menu. This
254 |                    static aproach simplifies navigation in the menu a lot.
255 |
256 |   Return Values :  -
257 +--------------------------------------------------------------------------*/
258 void _nc_Link_Items(MENU * menu)
259 {
260   if (menu && menu->items && *(menu->items))
261     {
262       int i,j;
263       ITEM *item;
264       int Number_Of_Items = menu->nitems;
265       int col = 0, row = 0;
266       int Last_in_Row;
267       int Last_in_Column;
268       bool cycle = (menu->opt & O_NONCYCLIC) ? FALSE : TRUE;
269       
270       menu->status &= ~_LINK_NEEDED;
271       
272       if (menu->opt & O_ROWMAJOR)
273         {
274           int Number_Of_Columns = menu->cols;
275           
276           for(i=0; i < Number_Of_Items; i++)
277             {
278               item = menu->items[i];
279               
280               Last_in_Row = row * Number_Of_Columns + (Number_Of_Columns-1);
281               
282               item->left  = (col) ? 
283                 /* if we are not in the leftmost column, we can use the
284                    predecessor in the items array */
285                 menu->items[i-1] :
286                   (cycle ? menu->items[(Last_in_Row>=Number_Of_Items) ? 
287                                        Number_Of_Items-1:
288                                        Last_in_Row] : 
289                    (ITEM *)0 );
290               
291               item->right = (  (col < (Number_Of_Columns-1)) && 
292                              ((i+1) < Number_Of_Items) 
293                              ) ? 
294                                menu->items[i+1] :
295                                  ( cycle ? menu->items[row * Number_Of_Columns] : 
296                                   (ITEM *)0
297                                   );
298               
299               Last_in_Column = (menu->rows-1) * Number_Of_Columns + col;
300               
301               item->up    = (row) ? menu->items[i-Number_Of_Columns] :
302                 (cycle ? menu->items[(Last_in_Column>=Number_Of_Items) ?
303                                      Number_Of_Items-1 : 
304                                      Last_in_Column] : 
305                  (ITEM *)0);
306               
307               item->down  = ( (i+Number_Of_Columns) < Number_Of_Items ) 
308                 ? 
309                   menu->items[i + Number_Of_Columns] :
310                     (cycle ? menu->items[(row+1)<menu->rows ?
311                                          Number_Of_Items-1:col] : 
312                      (ITEM *)0);
313               item->x = col;
314               item->y = row;
315               if ( ++col == Number_Of_Columns )
316                 {
317                   row++;
318                   col = 0;
319                 }
320             }
321         }
322       else
323         {
324           int Number_Of_Rows = menu->rows;
325           
326           for(j=0; j<Number_Of_Items; j++)
327             {
328               item = menu->items[i=(col * Number_Of_Rows + row)];
329               
330               Last_in_Column = (menu->cols-1) * Number_Of_Rows + row;
331               
332               item->left  = (col) ? 
333                 menu->items[i - Number_Of_Rows] :
334                   (cycle ? (Last_in_Column >= Number_Of_Items ) ? 
335                    menu->items[Last_in_Column-Number_Of_Rows] : 
336                    menu->items[Last_in_Column] : 
337                    (ITEM *)0 );
338               
339               item->right = ((i + Number_Of_Rows) <Number_Of_Items) 
340                 ? 
341                   menu->items[i + Number_Of_Rows] :
342                     (cycle ? menu->items[row] : (ITEM *)0);
343               
344               Last_in_Row = col * Number_Of_Rows + (Number_Of_Rows - 1);
345               
346               item->up    = (row) ? 
347                 menu->items[i-1] :
348                   (cycle ?
349                    menu->items[(Last_in_Row>=Number_Of_Items) ? 
350                                Number_Of_Items-1:
351                                Last_in_Row] :
352                    (ITEM *)0);
353               
354               item->down  = (row < (Number_Of_Rows-1)) 
355                 ? 
356                   (menu->items[((i+1)<Number_Of_Items) ? 
357                                i+1 :
358                                (col-1)*Number_Of_Rows + row + 1]) :
359                                  (cycle ?
360                                   menu->items[col * Number_Of_Rows] :
361                                   (ITEM *)0
362                                   );
363               
364               item->x = col;
365               item->y = row;
366               if ( (++row) == Number_Of_Rows )
367                 {
368                   col++;
369                   row = 0;
370                 }
371             }
372         }
373     }
374 }
375
376 /*---------------------------------------------------------------------------
377 |   Facility      :  libnmenu  
378 |   Function      :  void _nc_Show_Menu(const MENU *menu)
379 |   
380 |   Description   :  Update the window that is associated with the menu
381 |
382 |   Return Values :  -
383 +--------------------------------------------------------------------------*/
384 void _nc_Show_Menu(const MENU *menu)
385 {
386   WINDOW *win;
387   int maxy, maxx;
388   
389   assert(menu);
390   if ( (menu->status & _POSTED) && !(menu->status & _IN_DRIVER) )
391     {
392       /* adjust the internal subwindow to start on the current top */
393       assert(menu->sub);
394       mvderwin(menu->sub,menu->spc_rows * menu->toprow,0);
395       win = Get_Menu_Window(menu);
396       
397       maxy = getmaxy(win);
398       maxx = getmaxx(win);       
399       
400       if (menu->height < maxy) 
401         maxy = menu->height;
402       if (menu->width < maxx) 
403         maxx = menu->width;
404       
405       copywin(menu->sub,win,0,0,0,0,maxy-1,maxx-1,0);
406       pos_menu_cursor(menu);
407     }   
408 }       
409
410 /*---------------------------------------------------------------------------
411 |   Facility      :  libnmenu  
412 |   Function      :  void _nc_New_TopRow_and_CurrentItem(
413 |                            MENU *menu, 
414 |                            int new_toprow, 
415 |                            ITEM *new_current_item)
416 |   
417 |   Description   :  Redisplay the menu so that the given row becomes the
418 |                    top row and the given item becomes the new current
419 |                    item.
420 |
421 |   Return Values :  -
422 +--------------------------------------------------------------------------*/
423 void _nc_New_TopRow_and_CurrentItem(MENU *menu, int new_toprow,
424                                     ITEM *new_current_item)
425 {
426   ITEM *cur_item;
427   bool mterm_called = FALSE;
428   bool iterm_called = FALSE;
429   
430   assert(menu);
431   if (menu->status & _POSTED)
432     {
433       if (new_current_item != menu->curitem)
434         {
435           Call_Hook(menu,itemterm);
436           iterm_called = TRUE;
437         }
438       if (new_toprow != menu->toprow)
439         {
440           Call_Hook(menu,menuterm);
441           mterm_called = TRUE;
442         }                       
443       
444       cur_item  = menu->curitem;
445       assert(cur_item);
446       menu->toprow  = new_toprow;
447       menu->curitem = new_current_item;                 
448       
449       if (mterm_called)
450         {
451           Call_Hook(menu,menuinit);
452         }
453       if (iterm_called)
454         {
455           /* this means, move from the old current_item to the new one... */
456           Move_To_Current_Item( menu, cur_item );
457           Call_Hook(menu,iteminit);
458         }
459       if (mterm_called || iterm_called)
460         {
461           _nc_Show_Menu(menu);
462         }
463       else
464         pos_menu_cursor(menu);
465     }
466   else
467     { /* if we are not posted, this is quite simple */
468       menu->toprow  = new_toprow;
469       menu->curitem = new_current_item;
470     }
471 }
472
473 /* m_global.c ends here */