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