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
41extern "C" {
42#  include <menu.h>
43}
44//
45// -------------------------------------------------------------------------
46// This wraps the ITEM type of <menu.h>
47// -------------------------------------------------------------------------
48//
49class NCURSES_IMPEXP NCursesMenuItem
50{
51  friend class NCursesMenu;
52
53protected:
54  ITEM *item;
55
56  inline void OnError (int err) const THROWS(NCursesMenuException) {
57    if (err != E_OK)
58      THROW(new NCursesMenuException (err));
59  }
60
61public:
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
82  NCursesMenuItem(const NCursesMenuItem& rhs)
83    : item(0)
84  {
85  }
86
87  virtual ~NCursesMenuItem ();
88  // Release the items memory
89
90  inline const char* name () const {
91    return ::item_name (item);
92  }
93  // Name of the item
94
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
105  inline void options_on (Item_Options opts) {
106    OnError (::item_opts_on (item, opts));
107  }
108  // Switch on the items options
109
110  inline void options_off (Item_Options opts) {
111    OnError (::item_opts_off (item, opts));
112  }
113  // Switch off the item's option
114
115  inline Item_Options options () const {
116    return ::item_opts (item);
117  }
118  // Retrieve the items options
119
120  inline void set_options (Item_Options opts) {
121    OnError (::set_item_opts (item, opts));
122  }
123  // Set the items options
124
125  inline void set_value (bool f) {
126    OnError (::set_item_value (item,f));
127  }
128  // Set/Reset the items selection state
129
130  inline bool value () const {
131    return ::item_value (item);
132  }
133  // Retrieve the items selection state
134
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.
149typedef 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.
154class NCURSES_IMPEXP NCursesMenuCallbackItem : public NCursesMenuItem
155{
156private:
157  ITEMCALLBACK* p_fct;
158
159public:
160  NCursesMenuCallbackItem(ITEMCALLBACK* fct       = NULL,
161			  const char* p_name      = NULL,
162			  const char* p_descript  = NULL )
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
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.
189extern "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//
201class NCURSES_IMPEXP NCursesMenu : public NCursesPanel
202{
203protected:
204  MENU *menu;
205
206private:
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
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
237protected:
238  // internal routines
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
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
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)
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
279public:
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?
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?
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
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
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
346  inline void setItems(NCursesMenuItem* Items[]) {
347    OnError(::set_menu_items(menu,mapItems(Items)));
348  }
349
350  // Remove the menu from the screen
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
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
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
371  inline void menu_format(int& rows,int& ncols) {
372    ::menu_format(menu,&rows,&ncols);
373  }
374
375  // Items of the menu
376  inline NCursesMenuItem* items() const {
377    return *my_items;
378  }
379
380  // Get the number of items in this menu
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)
386  inline NCursesMenuItem* current_item() const {
387    return my_items[::item_index(::current_item(menu))];
388  }
389
390  // Get the marker string
391  inline const char* mark() const {
392    return ::menu_mark(menu);
393  }
394
395  // Set the marker string
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
401  inline static const char* request_name(int c) {
402    return ::menu_request_name(c);
403  }
404
405  // Get the current 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
418  inline chtype back() const {
419    return ::menu_back(menu);
420  }
421
422  // Get the menus foreground attributes
423  inline chtype fore() const {
424    return ::menu_fore(menu);
425  }
426
427  // Get the menus grey attributes (used for unselectable items)
428  inline chtype grey() const {
429    return ::menu_grey(menu);
430  }
431
432  // Set the menus background attributes
433  inline chtype set_background(chtype a) {
434    return ::set_menu_back(menu,a);
435  }
436
437  // Set the menus foreground attributes
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)
443  inline chtype set_grey(chtype a) {
444    return ::set_menu_grey(menu,a);
445  }
446
447  inline void options_on (Menu_Options opts) {
448    OnError (::menu_opts_on (menu,opts));
449  }
450
451  inline void options_off(Menu_Options opts) {
452    OnError (::menu_opts_off(menu,opts));
453  }
454
455  inline Menu_Options options() const {
456    return ::menu_opts(menu);
457  }
458
459  inline void set_options (Menu_Options opts) {
460    OnError (::set_menu_opts (menu,opts));
461  }
462
463  inline int pad() const {
464    return ::menu_pad(menu);
465  }
466
467  inline void set_pad (int padch) {
468    OnError (::set_menu_pad (menu, padch));
469  }
470
471  // Position the cursor to the current item
472  inline void position_cursor () const {
473    OnError (::pos_menu_cursor (menu));
474  }
475
476  // Set the current item
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
482  inline int top_row (void) const {
483    return ::top_row (menu);
484  }
485
486  // Set the current top row of the menu
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
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
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
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//
597template<class T> class NCURSES_IMPEXP NCursesUserItem : public NCursesMenuItem
598{
599public:
600  NCursesUserItem (const char* p_name,
601		   const char* p_descript = NULL,
602		   const T* p_UserData    = STATIC_CAST(T*)(0))
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
608  virtual ~NCursesUserItem() {}
609
610  inline const T* UserData (void) const {
611    return reinterpret_cast<const T*>(::item_userptr (item));
612  };
613
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//
624template<class T> class NCURSES_IMPEXP NCursesUserMenu : public NCursesMenu
625{
626protected:
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))
632    : NCursesMenu(nlines,ncols,begin_y,begin_x) {
633      if (menu)
634	set_user (const_cast<void *>(p_UserData));
635  }
636
637public:
638  NCursesUserMenu (NCursesMenuItem Items[],
639		   const T* p_UserData = STATIC_CAST(T*)(0),
640		   bool with_frame=FALSE,
641		   bool autoDelete_Items=FALSE)
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)
654    : NCursesMenu (Items, nlines, ncols, begin_y, begin_x, with_frame) {
655      if (menu)
656	set_user (const_cast<void *>(p_UserData));
657  };
658
659  virtual ~NCursesUserMenu() {
660  };
661
662  inline T* UserData (void) const {
663    return reinterpret_cast<T*>(get_user ());
664  };
665
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