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