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