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 38MODULE_ID("$Id: cursesm.cc,v 1.22 2005/04/02 20:39:05 tom Exp $") 39 40NCursesMenuItem::~NCursesMenuItem() 41{ 42 if (item) 43 OnError(::free_item(item)); 44} 45 46bool 47NCursesMenuItem::action() 48{ 49 return FALSE; 50} 51 52NCursesMenuCallbackItem::~NCursesMenuCallbackItem() 53{ 54} 55 56bool 57NCursesMenuCallbackItem::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 */ 70void 71_nc_xx_mnu_init(MENU *m) 72{ 73 NCursesMenu::getHook(m)->On_Menu_Init(); 74} 75 76void 77_nc_xx_mnu_term(MENU *m) 78{ 79 NCursesMenu::getHook(m)->On_Menu_Termination(); 80} 81 82void 83_nc_xx_itm_init(MENU *m) 84{ 85 NCursesMenu* M = NCursesMenu::getHook(m); 86 M->On_Item_Init (*(M->current_item ())); 87} 88 89void 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 */ 99ITEM** 100NCursesMenu::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 121void 122NCursesMenu::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 167void 168NCursesMenu::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 178NCursesMenu::~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 206void 207NCursesMenu::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 = ⊄ 215 ::set_menu_sub(menu,sub->w); 216 } 217} 218 219bool 220NCursesMenu::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. 235int 236NCursesMenu::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 252static const int CMD_QUIT = MAX_COMMAND + 1; 253static 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// ------------------------------------------------------------------------- 261int 262NCursesMenu::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 299NCursesMenuItem* 300NCursesMenu::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 365void 366NCursesMenu::On_Menu_Init() 367{ 368} 369 370void 371NCursesMenu::On_Menu_Termination() 372{ 373} 374 375void 376NCursesMenu::On_Item_Init(NCursesMenuItem& item) 377{ 378} 379 380void 381NCursesMenu::On_Item_Termination(NCursesMenuItem& item) 382{ 383} 384 385void 386NCursesMenu::On_Request_Denied(int c) const 387{ 388 ::beep(); 389} 390 391void 392NCursesMenu::On_Not_Selectable(int c) const 393{ 394 ::beep(); 395} 396 397void 398NCursesMenu::On_No_Match(int c) const 399{ 400 ::beep(); 401} 402 403void 404NCursesMenu::On_Unknown_Command(int c) const 405{ 406 ::beep(); 407} 408