ncurses 4.1
[ncurses.git] / c++ / cursesm.cc
1 // * this is for making 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
24 #include "internal.h"
25
26 MODULE_ID("$Id: cursesm.cc,v 1.4 1997/05/05 20:27:32 tom Exp $")
27
28 #pragma implementation
29
30 #include "cursesm.h"
31
32 const int CMD_ACTION = MAX_COMMAND + 1;
33 const int CMD_QUIT   = MAX_COMMAND + 2;
34
35 unsigned long NCursesMenu::total_count = 0;
36
37 /* Internal hook functions. They will route the hook
38  * calls to virtual methods of the NCursesMenu class,
39  * so in C++ providing a hook is done simply by 
40  * implementing a virtual method in a derived class
41  */
42 void
43 NCursesMenu::mnu_init(MENU *m)
44 {
45   getHook(m)->On_Menu_Init();
46 }
47
48 void
49 NCursesMenu::mnu_term(MENU *m)
50 {
51   getHook(m)->On_Menu_Termination();
52 }
53
54 void
55 NCursesMenu::itm_init(MENU *m)
56 {
57   NCursesMenu* M = getHook(m);
58   M->On_Item_Init (M->current_item ());
59 }
60
61 void
62 NCursesMenu::itm_term(MENU *m)
63 {
64   NCursesMenu* M = getHook(m);
65   M->On_Item_Termination (M->current_item ());
66 }
67
68 /* Construct an ITEM* array from an array of NCursesMenuItem
69  * objects.
70  */
71 ITEM**
72 NCursesMenu::mapItems(NCursesMenuItem* nitems[]) {
73     int itemCount = 0,lcv;
74
75     for (lcv=0; nitems[lcv]->item; ++lcv)
76       ++itemCount;
77
78     ITEM** items = new ITEM*[itemCount + 1];
79
80     for (lcv=0;nitems[lcv]->item;++lcv) {
81       items[lcv] = nitems[lcv]->item;
82     }
83     items[lcv] = NULL;
84
85     my_items = nitems;
86
87     if (menu)
88       delete[] ::menu_items(menu);  
89     return items;
90 }
91
92
93 void
94 NCursesMenu::setItems(NCursesMenuItem* nitems[])
95 {
96   OnError(::set_menu_items(menu,mapItems(nitems)));
97 }
98
99 void
100 NCursesMenu::InitMenu(NCursesMenuItem* nitems[],
101                       bool with_frame) {
102   int mrows, mcols;
103   
104   if (total_count++==0) {
105     raw();
106     keypad(TRUE);
107   }
108
109   b_framed = with_frame;
110
111   menu = (MENU*)0;
112   menu = ::new_menu(mapItems(nitems));
113   if (!menu)
114     OnError (E_SYSTEM_ERROR);
115   
116   UserHook* hook = new UserHook;
117   hook->m_user   = NULL;
118   hook->m_back   = this;
119   hook->m_owner  = menu;
120   ::set_menu_userptr(menu,(const void*)hook);
121   
122   ::set_menu_init (menu, NCursesMenu::mnu_init);
123   ::set_menu_term (menu, NCursesMenu::mnu_term);
124   ::set_item_init (menu, NCursesMenu::itm_init);
125   ::set_item_term (menu, NCursesMenu::itm_term);
126   
127   scale(mrows, mcols);
128   ::set_menu_win(menu, w);
129   
130   if (with_frame) {
131     if ((mrows > height()-2) || (mcols > width()-2))
132       OnError(E_NO_ROOM);  
133     sub = new NCursesWindow(*this,mrows,mcols,1,1,'r');
134     ::set_menu_sub(menu, sub->w);
135     b_sub_owner = TRUE;
136   }
137   else {
138     sub = (NCursesWindow*)0;
139     b_sub_owner = FALSE;
140   }
141   setDefaultAttributes();
142 }
143
144 void
145 NCursesMenu::setDefaultAttributes() {
146   if (NumberOfColors() > 1) {
147     setcolor(1);
148     setpalette(COLOR_YELLOW,COLOR_BLUE);
149     setcolor(2);
150     setpalette(COLOR_CYAN,COLOR_BLUE);
151     setcolor(3);
152     setpalette(COLOR_WHITE,COLOR_CYAN);
153     ::set_menu_fore(menu, COLOR_PAIR(1));
154     ::set_menu_back(menu, COLOR_PAIR(2));
155     ::set_menu_grey(menu, COLOR_PAIR(3));
156   }
157   else {
158     ::set_menu_fore(menu, A_BOLD);
159     ::set_menu_back(menu, A_NORMAL);
160     ::set_menu_grey(menu, A_DIM);
161   }
162 }
163
164
165 NCursesMenu::NCursesMenu(NCursesMenuItem* menu[])
166   : NCursesPanel() {
167     InitMenu(menu);
168 }
169
170 NCursesMenu::NCursesMenu(NCursesMenuItem* menu[], 
171                          int lines,
172                          int cols,
173                          int begin_y,
174                          int begin_x,
175                          bool with_frame)
176   : NCursesPanel(lines, cols, begin_y, begin_x) {
177     InitMenu(menu,with_frame);
178 }
179
180 NCursesMenu::~NCursesMenu() {
181   UserHook* hook = (UserHook*)::menu_userptr(menu);
182   delete hook;
183   if (b_sub_owner) {
184     delete sub;
185     ::set_menu_sub(menu,(WINDOW *)0);
186   }
187   free_menu(menu);
188   
189   // It's essential to do this after free_menu()
190   delete[] ::menu_items(menu);  
191   --total_count;
192 }
193
194 void
195 NCursesMenu::setSubWindow(NCursesWindow& nsub)
196 {
197   if (!isDescendant(nsub))
198     OnError(E_SYSTEM_ERROR);
199   else {
200     if (b_sub_owner)
201       delete sub;
202     sub = &nsub;
203     ::set_menu_sub(menu,sub->w);
204   }
205 }
206
207 // call the menu driver and do basic error checking.
208 int 
209 NCursesMenu::driver (int c) {
210   int res = ::menu_driver (menu, c);
211   switch (res) {
212   case E_OK:
213   case E_REQUEST_DENIED:
214   case E_NOT_SELECTABLE:
215   case E_UNKNOWN_COMMAND:
216   case E_NO_MATCH:
217     break;
218   default:
219     OnError (res);
220   }
221   return (res);
222 }
223
224 bool
225 NCursesMenu::set_pattern (const char *pat) {
226   int res = ::set_menu_pattern (menu, pat);
227   switch(res) {
228   case E_OK:
229     break;
230   case E_NO_MATCH:
231     return FALSE;
232   default:
233     OnError (res);
234   }
235   return TRUE;
236 }
237
238 // Provide a default key virtualization. Translate the keyboard
239 // code c into a menu request code.
240 // The default implementation provides a hopefully straightforward
241 // mapping for the most common keystrokes and menu requests.
242 int 
243 NCursesMenu::virtualize(int c) {
244   switch(c) {
245   case CTRL('Q')     : return(CMD_QUIT);
246   case KEY_DOWN      :
247   case CTRL('N')     : return(REQ_NEXT_ITEM);
248   case KEY_UP        :
249   case CTRL('P')     : return(REQ_PREV_ITEM);
250   case CTRL('U')     : return(REQ_SCR_ULINE);
251   case CTRL('D')     : return(REQ_SCR_DLINE);
252   case CTRL('F')     : return(REQ_SCR_DPAGE);
253   case CTRL('B')     : return(REQ_SCR_UPAGE);
254   case CTRL('X')     : return(REQ_CLEAR_PATTERN);
255   case CTRL('H')     : return(REQ_BACK_PATTERN);
256   case CTRL('A')     : return(REQ_NEXT_MATCH);
257   case CTRL('Z')     : return(REQ_PREV_MATCH);
258   case CTRL('T')     : return(REQ_TOGGLE_ITEM);
259   case CTRL('J')     :
260   case CTRL('M')     : return(CMD_ACTION);
261   case KEY_HOME      : return(REQ_FIRST_ITEM);
262   case KEY_LEFT      : return(REQ_LEFT_ITEM);
263   case KEY_RIGHT     : return(REQ_RIGHT_ITEM);
264   case KEY_END       : return(REQ_LAST_ITEM);
265   case KEY_BACKSPACE : return(REQ_BACK_PATTERN);
266   case KEY_NPAGE     : return(REQ_SCR_DPAGE);
267   case KEY_PPAGE     : return(REQ_SCR_UPAGE);
268
269   default:
270     return(c);
271   }
272 }
273
274 NCursesMenuItem&
275 NCursesMenu::operator()(void) {
276   int drvCmnd;
277   int err;
278   int c;
279   bool b_action = FALSE;
280
281   post();
282   show();
283   refresh();
284   
285   while (!b_action && ((drvCmnd = virtualize((c=getch()))) != CMD_QUIT)) {
286     switch((err=driver(drvCmnd))) {
287     case E_REQUEST_DENIED:
288       On_Request_Denied(c);
289       break;
290     case E_NOT_SELECTABLE:
291       On_Not_Selectable(c);
292       break;
293     case E_UNKNOWN_COMMAND:
294       if (drvCmnd == CMD_ACTION) {
295         NCursesMenuItem& itm = current_item();
296         b_action = itm.action();
297       } else
298         On_Unknown_Command(c);
299       break;
300     case E_NO_MATCH:
301       On_No_Match(c);
302       break;
303     case E_OK:
304       break;
305     default:
306       OnError(err);
307     }
308   }
309
310   unpost();
311   hide();
312   refresh();
313   return *(my_items[::item_index (::current_item (menu))]);
314 }