| 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 = ⊄
|
|---|
| 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 | }
|
|---|