ncurses 5.7 - patch 20100109
[ncurses.git] / c++ / cursesm.cc
1 // * this is for making 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 #include "internal.h"
35 #include "cursesm.h"
36 #include "cursesapp.h"
37
38 MODULE_ID("$Id: cursesm.cc,v 1.22 2005/04/02 20:39:05 tom Exp $")
39
40 NCursesMenuItem::~NCursesMenuItem()
41 {
42   if (item)
43     OnError(::free_item(item));
44 }
45
46 bool
47 NCursesMenuItem::action()
48 {
49   return FALSE;
50 }
51
52 NCursesMenuCallbackItem::~NCursesMenuCallbackItem()
53 {
54 }
55
56 bool
57 NCursesMenuCallbackItem::action()
58 {
59   if (p_fct)
60     return p_fct (*this);
61   else
62     return FALSE;
63 }
64
65 /* Internal hook functions. They will route the hook
66  * calls to virtual methods of the NCursesMenu class,
67  * so in C++ providing a hook is done simply by
68  * implementing a virtual method in a derived class
69  */
70 void
71 _nc_xx_mnu_init(MENU *m)
72 {
73   NCursesMenu::getHook(m)->On_Menu_Init();
74 }
75
76 void
77 _nc_xx_mnu_term(MENU *m)
78 {
79   NCursesMenu::getHook(m)->On_Menu_Termination();
80 }
81
82 void
83 _nc_xx_itm_init(MENU *m)
84 {
85   NCursesMenu* M = NCursesMenu::getHook(m);
86   M->On_Item_Init (*(M->current_item ()));
87 }
88
89 void
90 _nc_xx_itm_term(MENU *m)
91 {
92   NCursesMenu* M = NCursesMenu::getHook(m);
93   M->On_Item_Termination (*(M->current_item ()));
94 }
95
96 /* Construct an ITEM* array from an array of NCursesMenuItem
97  * objects.
98  */
99 ITEM**
100 NCursesMenu::mapItems(NCursesMenuItem* nitems[])
101 {
102   int itemCount = 0,lcv;
103
104   for (lcv=0; nitems[lcv]->item; ++lcv)
105     ++itemCount;
106
107   ITEM** itemArray = new ITEM*[itemCount + 1];
108
109   for (lcv=0;nitems[lcv]->item;++lcv) {
110     itemArray[lcv] = nitems[lcv]->item;
111   }
112   itemArray[lcv] = NULL;
113
114   my_items = nitems;
115
116   if (menu)
117     delete[] ::menu_items(menu);
118   return itemArray;
119 }
120
121 void
122 NCursesMenu::InitMenu(NCursesMenuItem* nitems[],
123                       bool with_frame,
124                       bool autoDelete_Items)
125 {
126   int mrows, mcols;
127
128   keypad(TRUE);
129   meta(TRUE);
130
131   b_framed = with_frame;
132   b_autoDelete = autoDelete_Items;
133
134   menu = static_cast<MENU*>(0);
135   menu = ::new_menu(mapItems(nitems));
136   if (!menu)
137     OnError (E_SYSTEM_ERROR);
138
139   UserHook* hook = new UserHook;
140   hook->m_user   = NULL;
141   hook->m_back   = this;
142   hook->m_owner  = menu;
143   ::set_menu_userptr(menu, static_cast<void*>(hook));
144
145   ::set_menu_init (menu, _nc_xx_mnu_init);
146   ::set_menu_term (menu, _nc_xx_mnu_term);
147   ::set_item_init (menu, _nc_xx_itm_init);
148   ::set_item_term (menu, _nc_xx_itm_term);
149
150   scale(mrows, mcols);
151   ::set_menu_win(menu, w);
152
153   if (with_frame) {
154     if ((mrows > height()-2) || (mcols > width()-2))
155       OnError(E_NO_ROOM);
156     sub = new NCursesWindow(*this,mrows,mcols,1,1,'r');
157     ::set_menu_sub(menu, sub->w);
158     b_sub_owner = TRUE;
159   }
160   else {
161     sub = static_cast<NCursesWindow*>(0);
162     b_sub_owner = FALSE;
163   }
164   setDefaultAttributes();
165 }
166
167 void
168 NCursesMenu::setDefaultAttributes()
169 {
170   NCursesApplication* S = NCursesApplication::getApplication();
171   if (S) {
172     ::set_menu_fore(menu, S->foregrounds());
173     ::set_menu_back(menu, S->backgrounds());
174     ::set_menu_grey(menu, S->inactives());
175   }
176 }
177
178 NCursesMenu::~NCursesMenu()
179 {
180   UserHook* hook = reinterpret_cast<UserHook*>(::menu_userptr(menu));
181   delete hook;
182   if (b_sub_owner) {
183     delete sub;
184     ::set_menu_sub(menu, static_cast<WINDOW *>(0));
185   }
186   if (menu) {
187     ITEM** itms = ::menu_items(menu);
188     int cnt = count();
189
190     OnError(::set_menu_items(menu, static_cast<ITEM**>(0)));
191
192     if (b_autoDelete) {
193       if (cnt>0) {
194         for (int i=0; i <= cnt; i++)
195           delete my_items[i];
196       }
197       delete[] my_items;
198     }
199
200     ::free_menu(menu);
201     // It's essential to do this after free_menu()
202     delete[] itms;
203   }
204 }
205
206 void
207 NCursesMenu::setSubWindow(NCursesWindow& nsub)
208 {
209   if (!isDescendant(nsub))
210     OnError(E_SYSTEM_ERROR);
211   else {
212     if (b_sub_owner)
213       delete sub;
214     sub = &nsub;
215     ::set_menu_sub(menu,sub->w);
216   }
217 }
218
219 bool
220 NCursesMenu::set_pattern (const char *pat)
221 {
222   int res = ::set_menu_pattern (menu, pat);
223   switch(res) {
224   case E_OK:
225     break;
226   case E_NO_MATCH:
227     return FALSE;
228   default:
229     OnError (res);
230   }
231   return TRUE;
232 }
233
234 // call the menu driver and do basic error checking.
235 int
236 NCursesMenu::driver (int c)
237 {
238   int res = ::menu_driver (menu, c);
239   switch (res) {
240   case E_OK:
241   case E_REQUEST_DENIED:
242   case E_NOT_SELECTABLE:
243   case E_UNKNOWN_COMMAND:
244   case E_NO_MATCH:
245     break;
246   default:
247     OnError (res);
248   }
249   return (res);
250 }
251
252 static const int CMD_QUIT   = MAX_COMMAND + 1;
253 static const int CMD_ACTION = MAX_COMMAND + 2;
254 //
255 // -------------------------------------------------------------------------
256 // Provide a default key virtualization. Translate the keyboard
257 // code c into a menu request code.
258 // The default implementation provides a hopefully straightforward
259 // mapping for the most common keystrokes and menu requests.
260 // -------------------------------------------------------------------------
261 int
262 NCursesMenu::virtualize(int c)
263 {
264   switch(c) {
265   case CTRL('X')     : return(CMD_QUIT);              // eXit
266
267   case KEY_DOWN      : return(REQ_DOWN_ITEM);
268   case CTRL('N')     : return(REQ_NEXT_ITEM);         // Next
269   case KEY_UP        : return(REQ_UP_ITEM);
270   case CTRL('P')     : return(REQ_PREV_ITEM);         // Previous
271
272   case CTRL('U')     : return(REQ_SCR_ULINE);         // Up
273   case CTRL('D')     : return(REQ_SCR_DLINE);         // Down
274   case CTRL('F')     : return(REQ_SCR_DPAGE);         // Forward
275   case CTRL('B')     : return(REQ_SCR_UPAGE);         // Backward
276
277   case CTRL('Y')     : return(REQ_CLEAR_PATTERN);
278   case CTRL('H')     : return(REQ_BACK_PATTERN);
279   case CTRL('A')     : return(REQ_NEXT_MATCH);
280   case CTRL('E')     : return(REQ_PREV_MATCH);
281   case CTRL('T')     : return(REQ_TOGGLE_ITEM);
282
283   case CTRL('J')     :
284   case CTRL('M')     : return(CMD_ACTION);
285
286   case KEY_HOME      : return(REQ_FIRST_ITEM);
287   case KEY_LEFT      : return(REQ_LEFT_ITEM);
288   case KEY_RIGHT     : return(REQ_RIGHT_ITEM);
289   case KEY_END       : return(REQ_LAST_ITEM);
290   case KEY_BACKSPACE : return(REQ_BACK_PATTERN);
291   case KEY_NPAGE     : return(REQ_SCR_DPAGE);
292   case KEY_PPAGE     : return(REQ_SCR_UPAGE);
293
294   default:
295     return(c);
296   }
297 }
298
299 NCursesMenuItem*
300 NCursesMenu::operator()(void)
301 {
302   int drvCmnd;
303   int err;
304   int c;
305   bool b_action = FALSE;
306
307   post();
308   show();
309   refresh();
310
311   while (!b_action && ((drvCmnd = virtualize((c=getKey()))) != CMD_QUIT)) {
312
313     switch((err=driver(drvCmnd))) {
314     case E_REQUEST_DENIED:
315       On_Request_Denied(c);
316       break;
317     case E_NOT_SELECTABLE:
318       On_Not_Selectable(c);
319       break;
320     case E_UNKNOWN_COMMAND:
321       if (drvCmnd == CMD_ACTION) {
322         if (options() & O_ONEVALUE) {
323           NCursesMenuItem* itm = current_item();
324           assert(itm != 0);
325           if (itm->options() & O_SELECTABLE)
326             {
327               b_action = itm->action();
328               refresh();
329             }
330           else
331             On_Not_Selectable(c);
332         }
333         else {
334           int n = count();
335           for(int i=0; i<n; i++) {
336             NCursesMenuItem* itm = my_items[i];
337             if (itm->value()) {
338               b_action |= itm->action();
339               refresh();
340             }
341           }
342         }
343       } else
344         On_Unknown_Command(c);
345       break;
346     case E_NO_MATCH:
347       On_No_Match(c);
348       break;
349     case E_OK:
350       break;
351     default:
352       OnError(err);
353     }
354   }
355
356   unpost();
357   hide();
358   refresh();
359   if (options() & O_ONEVALUE)
360     return my_items[::item_index (::current_item (menu))];
361   else
362     return NULL;
363 }
364
365 void
366 NCursesMenu::On_Menu_Init()
367 {
368 }
369
370 void
371 NCursesMenu::On_Menu_Termination()
372 {
373 }
374
375 void
376 NCursesMenu::On_Item_Init(NCursesMenuItem& item)
377 {
378 }
379
380 void
381 NCursesMenu::On_Item_Termination(NCursesMenuItem& item)
382 {
383 }
384
385 void
386 NCursesMenu::On_Request_Denied(int c) const
387 {
388   ::beep();
389 }
390
391 void
392 NCursesMenu::On_Not_Selectable(int c) const
393 {
394   ::beep();
395 }
396
397 void
398 NCursesMenu::On_No_Match(int c) const
399 {
400   ::beep();
401 }
402
403 void
404 NCursesMenu::On_Unknown_Command(int c) const
405 {
406   ::beep();
407 }