1 // * This makes emacs happy -*-Mode: C++;-*-
2 /****************************************************************************
3  * Copyright (c) 1998-2003,2005 Free Software Foundation, Inc.              *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29 
30 /****************************************************************************
31  *   Author: Juergen Pfeifer, 1997                                          *
32  ****************************************************************************/
33 
34 // $Id: cursesm.h,v 1.25 2005/08/13 18:10:36 tom Exp $
35 
36 #ifndef NCURSES_CURSESM_H_incl
37 #define NCURSES_CURSESM_H_incl 1
38 
39 #include <cursesp.h>
40 
41 extern "C" {
42 #  include <menu.h>
43 }
44 //
45 // -------------------------------------------------------------------------
46 // This wraps the ITEM type of <menu.h>
47 // -------------------------------------------------------------------------
48 //
49 class NCURSES_IMPEXP NCursesMenuItem
50 {
51   friend class NCursesMenu;
52 
53 protected:
54   ITEM *item;
55 
OnError(int err)56   inline void OnError (int err) const THROWS(NCursesMenuException) {
57     if (err != E_OK)
58       THROW(new NCursesMenuException (err));
59   }
60 
61 public:
62   NCursesMenuItem (const char* p_name     = NULL,
63 		   const char* p_descript = NULL)
64     : item(0)
65   {
66     item = p_name ? ::new_item (p_name, p_descript) : STATIC_CAST(ITEM*)(0);
67     if (p_name && !item)
68       OnError (E_SYSTEM_ERROR);
69   }
70   // Create an item. If you pass both parameters as NULL, a delimiting
71   // item is constructed which can be used to terminate a list of
72   // NCursesMenu objects.
73 
74   NCursesMenuItem& operator=(const NCursesMenuItem& rhs)
75   {
76     if (this != &rhs) {
77       *this = rhs;
78     }
79     return *this;
80   }
81 
NCursesMenuItem(const NCursesMenuItem & rhs)82   NCursesMenuItem(const NCursesMenuItem& rhs)
83     : item(0)
84   {
85   }
86 
87   virtual ~NCursesMenuItem ();
88   // Release the items memory
89 
name()90   inline const char* name () const {
91     return ::item_name (item);
92   }
93   // Name of the item
94 
description()95   inline const char* description () const {
96     return ::item_description (item);
97   }
98   // Description of the item
99 
100   inline int (index) (void) const {
101     return ::item_index (item);
102   }
103   // Index of the item in an item array (or -1)
104 
options_on(Item_Options opts)105   inline void options_on (Item_Options opts) {
106     OnError (::item_opts_on (item, opts));
107   }
108   // Switch on the items options
109 
options_off(Item_Options opts)110   inline void options_off (Item_Options opts) {
111     OnError (::item_opts_off (item, opts));
112   }
113   // Switch off the item's option
114 
options()115   inline Item_Options options () const {
116     return ::item_opts (item);
117   }
118   // Retrieve the items options
119 
set_options(Item_Options opts)120   inline void set_options (Item_Options opts) {
121     OnError (::set_item_opts (item, opts));
122   }
123   // Set the items options
124 
set_value(bool f)125   inline void set_value (bool f) {
126     OnError (::set_item_value (item,f));
127   }
128   // Set/Reset the items selection state
129 
value()130   inline bool value () const {
131     return ::item_value (item);
132   }
133   // Retrieve the items selection state
134 
visible()135   inline bool visible () const {
136     return ::item_visible (item);
137   }
138   // Retrieve visibility of the item
139 
140   virtual bool action();
141   // Perform an action associated with this item; you may use this in an
142   // user supplied driver for a menu; you may derive from this class and
143   // overload action() to supply items with different actions.
144   // If an action returns true, the menu will be exited. The default action
145   // is to do nothing.
146 };
147 
148 // Prototype for an items callback function.
149 typedef bool ITEMCALLBACK(NCursesMenuItem&);
150 
151 // If you don't like to create a child class for individual items to
152 // overload action(), you may use this class and provide a callback
153 // function pointer for items.
154 class NCURSES_IMPEXP NCursesMenuCallbackItem : public NCursesMenuItem
155 {
156 private:
157   ITEMCALLBACK* p_fct;
158 
159 public:
160   NCursesMenuCallbackItem(ITEMCALLBACK* fct       = NULL,
161 			  const char* p_name      = NULL,
162 			  const char* p_descript  = NULL )
NCursesMenuItem(p_name,p_descript)163     : NCursesMenuItem (p_name, p_descript),
164       p_fct (fct) {
165   }
166 
167   NCursesMenuCallbackItem& operator=(const NCursesMenuCallbackItem& rhs)
168   {
169     if (this != &rhs) {
170       *this = rhs;
171     }
172     return *this;
173   }
174 
NCursesMenuCallbackItem(const NCursesMenuCallbackItem & rhs)175   NCursesMenuCallbackItem(const NCursesMenuCallbackItem& rhs)
176     : NCursesMenuItem(rhs),
177       p_fct(0)
178   {
179   }
180 
181   virtual ~NCursesMenuCallbackItem();
182 
183   bool action();
184 };
185 
186   // This are the built-in hook functions in this C++ binding. In C++ we use
187   // virtual member functions (see below On_..._Init and On_..._Termination)
188   // to provide this functionality in an object oriented manner.
189 extern "C" {
190   void _nc_xx_mnu_init(MENU *);
191   void _nc_xx_mnu_term(MENU *);
192   void _nc_xx_itm_init(MENU *);
193   void _nc_xx_itm_term(MENU *);
194 }
195 
196 //
197 // -------------------------------------------------------------------------
198 // This wraps the MENU type of <menu.h>
199 // -------------------------------------------------------------------------
200 //
201 class NCURSES_IMPEXP NCursesMenu : public NCursesPanel
202 {
203 protected:
204   MENU *menu;
205 
206 private:
207   NCursesWindow* sub;   // the subwindow object
208   bool b_sub_owner;     // is this our own subwindow?
209   bool b_framed;        // has the menu a border?
210   bool b_autoDelete;    // Delete items when deleting menu?
211 
212   NCursesMenuItem** my_items; // The array of items for this menu
213 
214   // This structure is used for the menu's user data field to link the
215   // MENU* to the C++ object and to provide extra space for a user pointer.
216   typedef struct {
217     void*              m_user;      // the pointer for the user's data
218     const NCursesMenu* m_back;      // backward pointer to C++ object
219     const MENU*        m_owner;
220   } UserHook;
221 
222   // Get the backward pointer to the C++ object from a MENU
getHook(const MENU * m)223   static inline NCursesMenu* getHook(const MENU *m) {
224     UserHook* hook = STATIC_CAST(UserHook*)(::menu_userptr(m));
225     assert(hook != 0 && hook->m_owner==m);
226     return const_cast<NCursesMenu*>(hook->m_back);
227   }
228 
229   friend void _nc_xx_mnu_init(MENU *);
230   friend void _nc_xx_mnu_term(MENU *);
231   friend void _nc_xx_itm_init(MENU *);
232   friend void _nc_xx_itm_term(MENU *);
233 
234   // Calculate ITEM* array for the menu
235   ITEM** mapItems(NCursesMenuItem* nitems[]);
236 
237 protected:
238   // internal routines
set_user(void * user)239   inline void set_user(void *user) {
240     UserHook* uptr = STATIC_CAST(UserHook*)(::menu_userptr (menu));
241     assert (uptr != 0 && uptr->m_back==this && uptr->m_owner==menu);
242     uptr->m_user = user;
243   }
244 
get_user()245   inline void *get_user() {
246     UserHook* uptr = STATIC_CAST(UserHook*)(::menu_userptr (menu));
247     assert (uptr != 0 && uptr->m_back==this && uptr->m_owner==menu);
248     return uptr->m_user;
249   }
250 
251   void InitMenu (NCursesMenuItem* menu[],
252 		 bool with_frame,
253 		 bool autoDeleteItems);
254 
OnError(int err)255   inline void OnError (int err) const THROWS(NCursesMenuException) {
256     if (err != E_OK)
257       THROW(new NCursesMenuException (this, err));
258   }
259 
260   // this wraps the menu_driver call.
261   virtual int driver (int c) ;
262 
263   // 'Internal' constructor to create a menu without association to
264   // an array of items.
265   NCursesMenu( int  nlines,
266 	       int  ncols,
267 	       int  begin_y = 0,
268 	       int  begin_x = 0)
NCursesPanel(nlines,ncols,begin_y,begin_x)269     : NCursesPanel(nlines,ncols,begin_y,begin_x),
270       menu (STATIC_CAST(MENU*)(0)),
271       sub(0),
272       b_sub_owner(0),
273       b_framed(0),
274       b_autoDelete(0),
275       my_items(0)
276   {
277   }
278 
279 public:
280   // Make a full window size menu
281   NCursesMenu (NCursesMenuItem* Items[],
282 	       bool with_frame=FALSE,        // Reserve space for a frame?
283 	       bool autoDelete_Items=FALSE)  // Autocleanup of Items?
NCursesPanel()284     : NCursesPanel(),
285       menu(0),
286       sub(0),
287       b_sub_owner(0),
288       b_framed(0),
289       b_autoDelete(0),
290       my_items(0)
291   {
292       InitMenu(Items, with_frame, autoDelete_Items);
293   }
294 
295   // Make a menu with a window of this size.
296   NCursesMenu (NCursesMenuItem* Items[],
297 	       int  nlines,
298 	       int  ncols,
299 	       int  begin_y = 0,
300 	       int  begin_x = 0,
301 	       bool with_frame=FALSE,        // Reserve space for a frame?
302 	       bool autoDelete_Items=FALSE)  // Autocleanup of Items?
NCursesPanel(nlines,ncols,begin_y,begin_x)303     : NCursesPanel(nlines, ncols, begin_y, begin_x),
304       menu(0),
305       sub(0),
306       b_sub_owner(0),
307       b_framed(0),
308       b_autoDelete(0),
309       my_items(0)
310   {
311       InitMenu(Items, with_frame, autoDelete_Items);
312   }
313 
314   NCursesMenu& operator=(const NCursesMenu& rhs)
315   {
316     if (this != &rhs) {
317       *this = rhs;
318       NCursesPanel::operator=(rhs);
319     }
320     return *this;
321   }
322 
NCursesMenu(const NCursesMenu & rhs)323   NCursesMenu(const NCursesMenu& rhs)
324     : NCursesPanel(rhs),
325       menu(rhs.menu),
326       sub(rhs.sub),
327       b_sub_owner(rhs.b_sub_owner),
328       b_framed(rhs.b_framed),
329       b_autoDelete(rhs.b_autoDelete),
330       my_items(rhs.my_items)
331   {
332   }
333 
334   virtual ~NCursesMenu ();
335 
336   // Retrieve the menus subwindow
subWindow()337   inline NCursesWindow& subWindow() const {
338     assert(sub!=NULL);
339     return *sub;
340   }
341 
342   // Set the menus subwindow
343   void setSubWindow(NCursesWindow& sub);
344 
345   // Set these items for the menu
setItems(NCursesMenuItem * Items[])346   inline void setItems(NCursesMenuItem* Items[]) {
347     OnError(::set_menu_items(menu,mapItems(Items)));
348   }
349 
350   // Remove the menu from the screen
unpost(void)351   inline void unpost (void) {
352     OnError (::unpost_menu (menu));
353   }
354 
355   // Post the menu to the screen if flag is true, unpost it otherwise
356   inline void post(bool flag = TRUE) {
357     flag ? OnError (::post_menu(menu)) : OnError (::unpost_menu (menu));
358   }
359 
360   // Get the numer of rows and columns for this menu
scale(int & mrows,int & mcols)361   inline void scale (int& mrows, int& mcols) const  {
362     OnError (::scale_menu (menu, &mrows, &mcols));
363   }
364 
365   // Set the format of this menu
set_format(int mrows,int mcols)366   inline void set_format(int mrows, int mcols) {
367     OnError (::set_menu_format(menu, mrows, mcols));
368   }
369 
370   // Get the format of this menu
menu_format(int & rows,int & ncols)371   inline void menu_format(int& rows,int& ncols) {
372     ::menu_format(menu,&rows,&ncols);
373   }
374 
375   // Items of the menu
items()376   inline NCursesMenuItem* items() const {
377     return *my_items;
378   }
379 
380   // Get the number of items in this menu
count()381   inline int count() const {
382     return ::item_count(menu);
383   }
384 
385   // Get the current item (i.e. the one the cursor is located)
current_item()386   inline NCursesMenuItem* current_item() const {
387     return my_items[::item_index(::current_item(menu))];
388   }
389 
390   // Get the marker string
mark()391   inline const char* mark() const {
392     return ::menu_mark(menu);
393   }
394 
395   // Set the marker string
set_mark(const char * marker)396   inline void set_mark(const char *marker) {
397     OnError (::set_menu_mark (menu, marker));
398   }
399 
400   // Get the name of the request code c
request_name(int c)401   inline static const char* request_name(int c) {
402     return ::menu_request_name(c);
403   }
404 
405   // Get the current pattern
pattern()406   inline char* pattern() const {
407     return ::menu_pattern(menu);
408   }
409 
410   // true if there is a pattern match, false otherwise.
411   bool set_pattern (const char *pat);
412 
413   // set the default attributes for the menu
414   // i.e. set fore, back and grey attribute
415   virtual void setDefaultAttributes();
416 
417   // Get the menus background attributes
back()418   inline chtype back() const {
419     return ::menu_back(menu);
420   }
421 
422   // Get the menus foreground attributes
fore()423   inline chtype fore() const {
424     return ::menu_fore(menu);
425   }
426 
427   // Get the menus grey attributes (used for unselectable items)
grey()428   inline chtype grey() const {
429     return ::menu_grey(menu);
430   }
431 
432   // Set the menus background attributes
set_background(chtype a)433   inline chtype set_background(chtype a) {
434     return ::set_menu_back(menu,a);
435   }
436 
437   // Set the menus foreground attributes
set_foreground(chtype a)438   inline chtype set_foreground(chtype a) {
439     return ::set_menu_fore(menu,a);
440   }
441 
442   // Set the menus grey attributes (used for unselectable items)
set_grey(chtype a)443   inline chtype set_grey(chtype a) {
444     return ::set_menu_grey(menu,a);
445   }
446 
options_on(Menu_Options opts)447   inline void options_on (Menu_Options opts) {
448     OnError (::menu_opts_on (menu,opts));
449   }
450 
options_off(Menu_Options opts)451   inline void options_off(Menu_Options opts) {
452     OnError (::menu_opts_off(menu,opts));
453   }
454 
options()455   inline Menu_Options options() const {
456     return ::menu_opts(menu);
457   }
458 
set_options(Menu_Options opts)459   inline void set_options (Menu_Options opts) {
460     OnError (::set_menu_opts (menu,opts));
461   }
462 
pad()463   inline int pad() const {
464     return ::menu_pad(menu);
465   }
466 
set_pad(int padch)467   inline void set_pad (int padch) {
468     OnError (::set_menu_pad (menu, padch));
469   }
470 
471   // Position the cursor to the current item
position_cursor()472   inline void position_cursor () const {
473     OnError (::pos_menu_cursor (menu));
474   }
475 
476   // Set the current item
set_current(NCursesMenuItem & I)477   inline void set_current(NCursesMenuItem& I) {
478     OnError (::set_current_item(menu, I.item));
479   }
480 
481   // Get the current top row of the menu
top_row(void)482   inline int top_row (void) const {
483     return ::top_row (menu);
484   }
485 
486   // Set the current top row of the menu
set_top_row(int row)487   inline void set_top_row (int row) {
488     OnError (::set_top_row (menu, row));
489   }
490 
491   // spacing control
492   // Set the spacing for the menu
setSpacing(int spc_description,int spc_rows,int spc_columns)493   inline void setSpacing(int spc_description,
494 			 int spc_rows,
495 			 int spc_columns) {
496     OnError(::set_menu_spacing(menu,
497 			       spc_description,
498 			       spc_rows,
499 			       spc_columns));
500   }
501 
502   // Get the spacing info for the menu
Spacing(int & spc_description,int & spc_rows,int & spc_columns)503   inline void Spacing(int& spc_description,
504 		      int& spc_rows,
505 		      int& spc_columns) const {
506     OnError(::menu_spacing(menu,
507 			   &spc_description,
508 			   &spc_rows,
509 			   &spc_columns));
510   }
511 
512   // Decorations
513   inline void frame(const char *title=NULL, const char* btitle=NULL) {
514     if (b_framed)
515       NCursesPanel::frame(title,btitle);
516     else
517       OnError(E_SYSTEM_ERROR);
518   }
519 
520   inline void boldframe(const char *title=NULL, const char* btitle=NULL) {
521     if (b_framed)
522       NCursesPanel::boldframe(title,btitle);
523     else
524       OnError(E_SYSTEM_ERROR);
525   }
526 
label(const char * topLabel,const char * bottomLabel)527   inline void label(const char *topLabel, const char *bottomLabel) {
528     if (b_framed)
529       NCursesPanel::label(topLabel,bottomLabel);
530     else
531       OnError(E_SYSTEM_ERROR);
532   }
533 
534   // -----
535   // Hooks
536   // -----
537 
538   // Called after the menu gets repositioned in its window.
539   // This is especially true if the menu is posted.
540   virtual void On_Menu_Init();
541 
542   // Called before the menu gets repositioned in its window.
543   // This is especially true if the menu is unposted.
544   virtual void On_Menu_Termination();
545 
546   // Called after the item became the current item
547   virtual void On_Item_Init(NCursesMenuItem& item);
548 
549   // Called before this item is left as current item.
550   virtual void On_Item_Termination(NCursesMenuItem& item);
551 
552   // Provide a default key virtualization. Translate the keyboard
553   // code c into a menu request code.
554   // The default implementation provides a hopefully straightforward
555   // mapping for the most common keystrokes and menu requests.
556   virtual int virtualize(int c);
557 
558 
559   // Operators
560   inline NCursesMenuItem* operator[](int i) const {
561     if ( (i < 0) || (i >= ::item_count (menu)) )
562       OnError (E_BAD_ARGUMENT);
563     return (my_items[i]);
564   }
565 
566   // Perform the menu's operation
567   // Return the item where you left the selection mark for a single
568   // selection menu, or NULL for a multivalued menu.
569   virtual NCursesMenuItem* operator()(void);
570 
571   // --------------------
572   // Exception handlers
573   // Called by operator()
574   // --------------------
575 
576   // Called if the request is denied
577   virtual void On_Request_Denied(int c) const;
578 
579   // Called if the item is not selectable
580   virtual void On_Not_Selectable(int c) const;
581 
582   // Called if pattern doesn't match
583   virtual void On_No_Match(int c) const;
584 
585   // Called if the command is unknown
586   virtual void On_Unknown_Command(int c) const;
587 
588 };
589 //
590 // -------------------------------------------------------------------------
591 // This is the typical C++ typesafe way to allow to attach
592 // user data to an item of a menu. Its assumed that the user
593 // data belongs to some class T. Use T as template argument
594 // to create a UserItem.
595 // -------------------------------------------------------------------------
596 //
597 template<class T> class NCURSES_IMPEXP NCursesUserItem : public NCursesMenuItem
598 {
599 public:
600   NCursesUserItem (const char* p_name,
601 		   const char* p_descript = NULL,
602 		   const T* p_UserData    = STATIC_CAST(T*)(0))
NCursesMenuItem(p_name,p_descript)603     : NCursesMenuItem (p_name, p_descript) {
604       if (item)
605 	OnError (::set_item_userptr (item, const_cast<void *>(reinterpret_cast<const void*>(p_UserData))));
606   }
607 
~NCursesUserItem()608   virtual ~NCursesUserItem() {}
609 
UserData(void)610   inline const T* UserData (void) const {
611     return reinterpret_cast<const T*>(::item_userptr (item));
612   };
613 
setUserData(const T * p_UserData)614   inline virtual void setUserData(const T* p_UserData) {
615     if (item)
616       OnError (::set_item_userptr (item, const_cast<void *>(reinterpret_cast<const void *>(p_UserData))));
617   }
618 };
619 //
620 // -------------------------------------------------------------------------
621 // The same mechanism is used to attach user data to a menu
622 // -------------------------------------------------------------------------
623 //
624 template<class T> class NCURSES_IMPEXP NCursesUserMenu : public NCursesMenu
625 {
626 protected:
627   NCursesUserMenu( int  nlines,
628 		   int  ncols,
629 		   int  begin_y = 0,
630 		   int  begin_x = 0,
631 		   const T* p_UserData = STATIC_CAST(T*)(0))
NCursesMenu(nlines,ncols,begin_y,begin_x)632     : NCursesMenu(nlines,ncols,begin_y,begin_x) {
633       if (menu)
634 	set_user (const_cast<void *>(p_UserData));
635   }
636 
637 public:
638   NCursesUserMenu (NCursesMenuItem Items[],
639 		   const T* p_UserData = STATIC_CAST(T*)(0),
640 		   bool with_frame=FALSE,
641 		   bool autoDelete_Items=FALSE)
NCursesMenu(Items,with_frame,autoDelete_Items)642     : NCursesMenu (Items, with_frame, autoDelete_Items) {
643       if (menu)
644 	set_user (const_cast<void *>(p_UserData));
645   };
646 
647   NCursesUserMenu (NCursesMenuItem Items[],
648 		   int nlines,
649 		   int ncols,
650 		   int begin_y = 0,
651 		   int begin_x = 0,
652 		   const T* p_UserData = STATIC_CAST(T*)(0),
653 		   bool with_frame=FALSE)
NCursesMenu(Items,nlines,ncols,begin_y,begin_x,with_frame)654     : NCursesMenu (Items, nlines, ncols, begin_y, begin_x, with_frame) {
655       if (menu)
656 	set_user (const_cast<void *>(p_UserData));
657   };
658 
~NCursesUserMenu()659   virtual ~NCursesUserMenu() {
660   };
661 
UserData(void)662   inline T* UserData (void) const {
663     return reinterpret_cast<T*>(get_user ());
664   };
665 
setUserData(const T * p_UserData)666   inline virtual void setUserData (const T* p_UserData) {
667     if (menu)
668       set_user (const_cast<void *>(p_UserData));
669   }
670 };
671 
672 #endif /* NCURSES_CURSESM_H_incl */
673