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