| @@ -2,7 +2,8 @@ · 1 unmodified line |
| 2 | #include "oso.h" | 2 | #include "oso.h" |
| 3 | #include <ctype.h> | 3 | #include <ctype.h> |
| 4 | #include <form.h> | 4 | #include <form.h> |
| 5 | #include <menu.h> | 5 | |
| | 6 | ORCA_NOINLINE static void qmenu_reprint(Qmenu *qm); |
| 6 | | 7 | |
| 7 | void term_util_init_colors() { | 8 | void term_util_init_colors() { |
| 8 | if (has_colors()) { | 9 | if (has_colors()) { |
| @@ -38,20 +39,18 @@ struct Qmsg { · 29 unmodified lines |
| 38 | Qmsg_dismiss_mode dismiss_mode; | 39 | Qmsg_dismiss_mode dismiss_mode; |
| 39 | }; | 40 | }; |
| 40 | | 41 | |
| 41 | struct Qmenu_item_extra { | 42 | typedef struct Qmenu_item { |
| 42 | int user_id; | 43 | char const *text; |
| | 44 | int id; |
| 43 | U8 owns_string : 1, is_spacer : 1; | 45 | U8 owns_string : 1, is_spacer : 1; |
| 44 | }; | 46 | } Qmenu_item; |
| 45 | | 47 | |
| 46 | struct Qmenu { | 48 | struct Qmenu { |
| 47 | Qblock qblock; | 49 | Qblock qblock; |
| 48 | MENU *ncurses_menu; | 50 | Qmenu_item *items; |
| 49 | ITEM **ncurses_items; | | |
| 50 | Usz items_count, items_cap; | 51 | Usz items_count, items_cap; |
| 51 | ITEM *initial_item; | 52 | int current_item, id; |
| 52 | int id; | 53 | U8 needs_reprint : 1, is_frontmost : 1; |
| 53 | // Flag for right-padding hack. Temp until we do our own menus | | |
| 54 | U8 has_submenu_item : 1; | | |
| 55 | }; | 54 | }; |
| 56 | | 55 | |
| 57 | struct Qform { | 56 | struct Qform { |
| @@ -175,18 +174,26 @@ bool qnav_draw(void) { · 117 unmodified lines |
| 175 | getmaxyx(stdscr, term_h, term_w); | 174 | getmaxyx(stdscr, term_h, term_w); |
| 176 | for (Usz i = 0; i < qnav_stack.count; ++i) { | 175 | for (Usz i = 0; i < qnav_stack.count; ++i) { |
| 177 | Qblock *qb = qnav_stack.blocks[i]; | 176 | Qblock *qb = qnav_stack.blocks[i]; |
| 178 | if (qnav_stack.occlusion_dirty) { | 177 | bool is_frontmost = i == qnav_stack.count - 1; |
| 179 | bool is_frontmost = i == qnav_stack.count - 1; | 178 | if (qnav_stack.occlusion_dirty) |
| 180 | qblock_print_frame(qb, is_frontmost); | 179 | qblock_print_frame(qb, is_frontmost); |
| 181 | switch (qb->tag) { | 180 | switch (qb->tag) { |
| 182 | case Qblock_type_qmsg: | 181 | case Qblock_type_qmsg: |
| 183 | break; | 182 | break; |
| 184 | case Qblock_type_qmenu: | 183 | case Qblock_type_qmenu: { |
| 185 | qmenu_set_displayed_active(qmenu_of(qb), is_frontmost); | 184 | Qmenu *qm = qmenu_of(qb); |
| 186 | break; | 185 | if (qm->is_frontmost != is_frontmost) { |
| 187 | case Qblock_type_qform: | 186 | qm->is_frontmost = is_frontmost; |
| 188 | break; | 187 | qm->needs_reprint = 1; |
| | 188 | } |
| | 189 | if (qm->needs_reprint) { |
| | 190 | qmenu_reprint(qm); |
| | 191 | qm->needs_reprint = 0; |
| 189 | } | 192 | } |
| | 193 | break; |
| | 194 | } |
| | 195 | case Qblock_type_qform: |
| | 196 | break; |
| 190 | } | 197 | } |
| 191 | touchwin(qb->outer_window); // here? or after continue? | 198 | touchwin(qb->outer_window); // here? or after continue? |
| 192 | if (term_h < 1 || term_w < 1) | 199 | if (term_h < 1 || term_w < 1) |
| @@ -222,7 +229,7 @@ void qblock_print_title(Qblock *qb, char const *title, int attr) { · 29 unmodified lines |
| 222 | wattr_get(qb->outer_window, &attrs, &pair, NULL); | 229 | wattr_get(qb->outer_window, &attrs, &pair, NULL); |
| 223 | wattrset(qb->outer_window, attr); | 230 | wattrset(qb->outer_window, attr); |
| 224 | waddch(qb->outer_window, ' '); | 231 | waddch(qb->outer_window, ' '); |
| 225 | wprintw(qb->outer_window, title); | 232 | waddstr(qb->outer_window, title); |
| 226 | waddch(qb->outer_window, ' '); | 233 | waddch(qb->outer_window, ' '); |
| 227 | wattr_set(qb->outer_window, attrs, pair, NULL); | 234 | wattr_set(qb->outer_window, attrs, pair, NULL); |
| 228 | } | 235 | } |
| @@ -343,91 +350,65 @@ Qmsg *qmsg_of(Qblock *qb) { return ORCA_CONTAINER_OF(qb, Qmsg, qblock); } · 114 unmodified lines |
| 343 | Qmenu *qmenu_create(int id) { | 350 | Qmenu *qmenu_create(int id) { |
| 344 | Qmenu *qm = (Qmenu *)malloc(sizeof(Qmenu)); | 351 | Qmenu *qm = (Qmenu *)malloc(sizeof(Qmenu)); |
| 345 | qblock_init(&qm->qblock, Qblock_type_qmenu); | 352 | qblock_init(&qm->qblock, Qblock_type_qmenu); |
| 346 | qm->ncurses_menu = NULL; | 353 | qm->items = NULL; |
| 347 | qm->ncurses_items = NULL; | | |
| 348 | qm->items_count = 0; | 354 | qm->items_count = 0; |
| 349 | qm->items_cap = 0; | 355 | qm->items_cap = 0; |
| 350 | qm->initial_item = NULL; | 356 | qm->current_item = 0; |
| 351 | qm->id = id; | 357 | qm->id = id; |
| 352 | qm->has_submenu_item = 0; | 358 | qm->needs_reprint = 1; |
| | 359 | qm->is_frontmost = 0; |
| 353 | return qm; | 360 | return qm; |
| 354 | } | 361 | } |
| 355 | void qmenu_destroy(Qmenu *qm) { qmenu_free(qm); } | 362 | void qmenu_destroy(Qmenu *qm) { qmenu_free(qm); } |
| 356 | int qmenu_id(Qmenu const *qm) { return qm->id; } | 363 | int qmenu_id(Qmenu const *qm) { return qm->id; } |
| 357 | static ORCA_NOINLINE void | 364 | static ORCA_NOINLINE Qmenu_item *qmenu_allocitems(Qmenu *qm, Usz count) { |
| 358 | qmenu_allocitems(Qmenu *qm, Usz count, Usz *out_idx, ITEM ***out_items, | | |
| 359 | struct Qmenu_item_extra **out_extras) { | | |
| 360 | Usz old_count = qm->items_count; | 365 | Usz old_count = qm->items_count; |
| 361 | // Add 1 for the extra null terminator guy | 366 | if (old_count > SIZE_MAX - count) // overflow |
| 362 | Usz new_count = old_count + count + 1; | 367 | exit(1); |
| | 368 | Usz new_count = old_count + count; |
| 363 | Usz items_cap = qm->items_cap; | 369 | Usz items_cap = qm->items_cap; |
| 364 | ITEM **items = qm->ncurses_items; | 370 | Qmenu_item *items = qm->items; |
| 365 | if (new_count > items_cap) { | 371 | if (new_count > items_cap) { |
| 366 | // todo overflow check, realloc fail check | 372 | // todo overflow check, realloc fail check |
| 367 | Usz old_cap = items_cap; | | |
| 368 | Usz new_cap = new_count < 32 ? 32 : orca_round_up_power2(new_count); | 373 | Usz new_cap = new_count < 32 ? 32 : orca_round_up_power2(new_count); |
| 369 | Usz new_size = new_cap * (sizeof(ITEM *) + sizeof(struct Qmenu_item_extra)); | 374 | Usz new_size = new_cap * sizeof(Qmenu_item); |
| 370 | ITEM **new_items = (ITEM **)realloc(items, new_size); | 375 | Qmenu_item *new_items = (Qmenu_item *)realloc(items, new_size); |
| 371 | if (!new_items) | 376 | if (!new_items) |
| 372 | exit(1); | 377 | exit(1); |
| 373 | items = new_items; | 378 | items = new_items; |
| 374 | items_cap = new_cap; | 379 | items_cap = new_cap; |
| 375 | // Move old extras data to new position | 380 | qm->items = new_items; |
| 376 | Usz old_extras_offset = sizeof(ITEM *) * old_cap; | | |
| 377 | Usz new_extras_offset = sizeof(ITEM *) * new_cap; | | |
| 378 | Usz old_extras_size = sizeof(struct Qmenu_item_extra) * old_count; | | |
| 379 | memmove((char *)items + new_extras_offset, | | |
| 380 | (char *)items + old_extras_offset, old_extras_size); | | |
| 381 | qm->ncurses_items = new_items; | | |
| 382 | qm->items_cap = new_cap; | 381 | qm->items_cap = new_cap; |
| 383 | } | 382 | } |
| 384 | // Not using new_count here in order to leave an extra 1 for the null | 383 | qm->items_count = new_count; |
| 385 | // terminator as required by ncurses. | 384 | return items + old_count; |
| 386 | qm->items_count = old_count + count; | 385 | } |
| 387 | Usz extras_offset = sizeof(ITEM *) * items_cap; | 386 | ORCA_NOINLINE static void qmenu_reprint(Qmenu *qm) { |
| 388 | *out_idx = old_count; | 387 | WINDOW *win = qm->qblock.content_window; |
| 389 | *out_items = items + old_count; | 388 | Qmenu_item *items = qm->items; |
| 390 | *out_extras = | 389 | bool isfront = qm->is_frontmost; |
| 391 | (struct Qmenu_item_extra *)((char *)items + extras_offset) + old_count; | 390 | werase(win); |
| 392 | } | 391 | for (Usz i = 0, n = qm->items_count; i < n; ++i) { |
| 393 | ORCA_FORCEINLINE static struct Qmenu_item_extra * | 392 | bool iscur = items[i].id == qm->current_item; |
| 394 | qmenu_item_extras_ptr(Qmenu *qm) { | 393 | wattrset(win, isfront ? iscur ? A_BOLD : A_NORMAL : A_DIM); |
| 395 | Usz offset = sizeof(ITEM *) * qm->items_cap; | 394 | wmove(win, (int)i, iscur ? 1 : 3); |
| 396 | return (struct Qmenu_item_extra *)((char *)qm->ncurses_items + offset); | 395 | if (iscur) |
| 397 | } | 396 | waddstr(win, "> "); |
| 398 | // Get the curses menu item user pointer out, turn it to an int, and use it as | 397 | waddstr(win, items[i].text); |
| 399 | // an index into the 'extras' arrays. | 398 | } |
| 400 | ORCA_FORCEINLINE static struct Qmenu_item_extra * | | |
| 401 | qmenu_itemextra(struct Qmenu_item_extra *extras, ITEM *item) { | | |
| 402 | return extras + (int)(intptr_t)(item_userptr(item)); | | |
| 403 | } | 399 | } |
| 404 | void qmenu_set_title(Qmenu *qm, char const *title) { | 400 | void qmenu_set_title(Qmenu *qm, char const *title) { |
| 405 | qblock_set_title(&qm->qblock, title); | 401 | qblock_set_title(&qm->qblock, title); |
| 406 | } | 402 | } |
| 407 | void qmenu_add_choice(Qmenu *qm, int id, char const *text) { | 403 | void qmenu_add_choice(Qmenu *qm, int id, char const *text) { |
| 408 | assert(id != 0); | 404 | assert(id != 0); |
| 409 | Usz idx; | 405 | Qmenu_item *item = qmenu_allocitems(qm, 1); |
| 410 | ITEM **items; | 406 | item->text = text; |
| 411 | struct Qmenu_item_extra *extras; | 407 | item->id = id; |
| 412 | qmenu_allocitems(qm, 1, &idx, &items, &extras); | 408 | item->owns_string = false; |
| 413 | items[0] = new_item(text, NULL); | 409 | item->is_spacer = false; |
| 414 | set_item_userptr(items[0], (void *)(uintptr_t)idx); | 410 | if (!qm->current_item) |
| 415 | extras[0].user_id = id; | 411 | qm->current_item = id; |
| 416 | extras[0].owns_string = false; | | |
| 417 | extras[0].is_spacer = false; | | |
| 418 | } | | |
| 419 | void qmenu_add_submenu(Qmenu *qm, int id, char const *text) { | | |
| 420 | assert(id != 0); | | |
| 421 | qm->has_submenu_item = true; // don't add +1 right padding to subwindow | | |
| 422 | Usz idx; | | |
| 423 | ITEM **items; | | |
| 424 | struct Qmenu_item_extra *extras; | | |
| 425 | qmenu_allocitems(qm, 1, &idx, &items, &extras); | | |
| 426 | items[0] = new_item(text, ">"); | | |
| 427 | set_item_userptr(items[0], (void *)(uintptr_t)idx); | | |
| 428 | extras[0].user_id = id; | | |
| 429 | extras[0].owns_string = false; | | |
| 430 | extras[0].is_spacer = false; | | |
| 431 | } | 412 | } |
| 432 | void qmenu_add_printf(Qmenu *qm, int id, char const *fmt, ...) { | 413 | void qmenu_add_printf(Qmenu *qm, int id, char const *fmt, ...) { |
| 433 | va_list ap; | 414 | va_list ap; |
| @@ -442,85 +423,42 @@ void qmenu_add_printf(Qmenu *qm, int id, char const *fmt, ...) { · 8 unmodified lines |
| 442 | va_end(ap); | 423 | va_end(ap); |
| 443 | if (printedsize != textsize) | 424 | if (printedsize != textsize) |
| 444 | exit(1); // todo better handling? | 425 | exit(1); // todo better handling? |
| 445 | Usz idx; | 426 | Qmenu_item *item = qmenu_allocitems(qm, 1); |
| 446 | ITEM **items; | 427 | item->text = buffer; |
| 447 | struct Qmenu_item_extra *extras; | 428 | item->id = id; |
| 448 | qmenu_allocitems(qm, 1, &idx, &items, &extras); | 429 | item->owns_string = true; |
| 449 | items[0] = new_item(buffer, NULL); | 430 | item->is_spacer = false; |
| 450 | set_item_userptr(items[0], (void *)(uintptr_t)idx); | 431 | if (!qm->current_item) |
| 451 | extras[0].user_id = id; | 432 | qm->current_item = id; |
| 452 | extras[0].owns_string = true; | | |
| 453 | extras[0].is_spacer = false; | | |
| 454 | } | 433 | } |
| 455 | void qmenu_add_spacer(Qmenu *qm) { | 434 | void qmenu_add_spacer(Qmenu *qm) { |
| 456 | Usz idx; | 435 | Qmenu_item *item = qmenu_allocitems(qm, 1); |
| 457 | ITEM **items; | 436 | item->text = " "; |
| 458 | struct Qmenu_item_extra *extras; | 437 | item->id = 0; |
| 459 | qmenu_allocitems(qm, 1, &idx, &items, &extras); | 438 | item->owns_string = false; |
| 460 | items[0] = new_item(" ", NULL); | 439 | item->is_spacer = true; |
| 461 | item_opts_off(items[0], O_SELECTABLE); | | |
| 462 | set_item_userptr(items[0], (void *)(uintptr_t)idx); | | |
| 463 | extras[0].user_id = 0; | | |
| 464 | extras[0].owns_string = false; | | |
| 465 | extras[0].is_spacer = true; | | |
| 466 | } | 440 | } |
| 467 | void qmenu_set_current_item(Qmenu *qm, int id) { | 441 | void qmenu_set_current_item(Qmenu *qm, int id) { |
| 468 | ITEM **items = qm->ncurses_items; | 442 | if (qm->current_item == id) |
| 469 | struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); | | |
| 470 | ITEM *found = NULL; | | |
| 471 | for (Usz i = 0, n = qm->items_count; i < n; i++) { | | |
| 472 | ITEM *item = items[i]; | | |
| 473 | if (qmenu_itemextra(extras, item)->user_id == id) { | | |
| 474 | found = item; | | |
| 475 | break; | | |
| 476 | } | | |
| 477 | } | | |
| 478 | if (!found) | | |
| 479 | return; | 443 | return; |
| 480 | if (qm->ncurses_menu) { | 444 | qm->current_item = id; |
| 481 | set_current_item(qm->ncurses_menu, found); | 445 | qm->needs_reprint = 1; |
| 482 | } else { | | |
| 483 | qm->initial_item = found; | | |
| 484 | } | | |
| 485 | } | | |
| 486 | int qmenu_current_item(Qmenu *qm) { | | |
| 487 | ITEM *item = NULL; | | |
| 488 | if (qm->ncurses_menu) | | |
| 489 | item = current_item(qm->ncurses_menu); | | |
| 490 | if (!item) | | |
| 491 | item = qm->initial_item; | | |
| 492 | if (!item) | | |
| 493 | return 0; | | |
| 494 | struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); | | |
| 495 | return qmenu_itemextra(extras, item)->user_id; | | |
| 496 | } | | |
| 497 | void qmenu_set_displayed_active(Qmenu *qm, bool active) { | | |
| 498 | // Could add a flag in the Qmenu to avoid redundantly changing this stuff. | | |
| 499 | set_menu_fore(qm->ncurses_menu, active ? A_BOLD : A_DIM); | | |
| 500 | set_menu_back(qm->ncurses_menu, active ? A_NORMAL : A_DIM); | | |
| 501 | set_menu_grey(qm->ncurses_menu, active ? A_DIM : A_DIM); | | |
| 502 | } | 446 | } |
| | 447 | int qmenu_current_item(Qmenu *qm) { return qm->current_item; } |
| 503 | void qmenu_push_to_nav(Qmenu *qm) { | 448 | void qmenu_push_to_nav(Qmenu *qm) { |
| 504 | // new_menu() will get angry if there are no items in the menu. We'll get a | 449 | // Probably a programming error if there are no items. Make the menu visible |
| 505 | // null pointer back, and our code will get angry. Instead, just add an empty | 450 | // so the programmer knows something went wrong. |
| 506 | // spacer item. This will probably only ever occur as a programming error, | | |
| 507 | // but we should try to avoid having to deal with qmenu_push_to_nav() | | |
| 508 | // returning a non-ignorable error for now. | | |
| 509 | if (qm->items_count == 0) | 451 | if (qm->items_count == 0) |
| 510 | qmenu_add_spacer(qm); | 452 | qmenu_add_spacer(qm); |
| 511 | // Allocating items always leaves an extra available item at the end. This is | 453 | Usz n = qm->items_count; |
| 512 | // so we can assign a NULL to it here, since ncurses requires the array to be | 454 | Qmenu_item *items = qm->items; |
| 513 | // null terminated instead of using a count. | 455 | int menu_min_h = (int)n, menu_min_w = 0; |
| 514 | qm->ncurses_items[qm->items_count] = NULL; | 456 | for (Usz i = 0; i < n; ++i) { |
| 515 | qm->ncurses_menu = new_menu(qm->ncurses_items); | 457 | int item_w = (int)strlen(items[i].text); |
| 516 | set_menu_mark(qm->ncurses_menu, " > "); | 458 | if (item_w > menu_min_w) |
| 517 | set_menu_fore(qm->ncurses_menu, A_BOLD); | 459 | menu_min_w = item_w; |
| 518 | set_menu_grey(qm->ncurses_menu, A_DIM); | 460 | } |
| 519 | set_menu_format(qm->ncurses_menu, 30, 1); // temp to allow large Y | 461 | menu_min_w += 3 + 1; // left " > " plus 1 empty space on right |
| 520 | int menu_min_h, menu_min_w; | | |
| 521 | scale_menu(qm->ncurses_menu, &menu_min_h, &menu_min_w); | | |
| 522 | if (!qm->has_submenu_item) | | |
| 523 | menu_min_w += 1; // temp hack | | |
| 524 | if (qm->qblock.title) { | 462 | if (qm->qblock.title) { |
| 525 | // Stupid lack of wcswidth() means we can't know how wide this string is | 463 | // Stupid lack of wcswidth() means we can't know how wide this string is |
| 526 | // actually displayed. Just fake it for now, until we have Unicode strings | 464 | // actually displayed. Just fake it for now, until we have Unicode strings |
| @@ -529,58 +467,50 @@ void qmenu_push_to_nav(Qmenu *qm) { · 2 unmodified lines |
| 529 | if (title_w > menu_min_w) | 467 | if (title_w > menu_min_w) |
| 530 | menu_min_w = title_w; | 468 | menu_min_w = title_w; |
| 531 | } | 469 | } |
| 532 | if (qm->initial_item) | | |
| 533 | set_current_item(qm->ncurses_menu, qm->initial_item); | | |
| 534 | qnav_stack_push(&qm->qblock, menu_min_h, menu_min_w); | 470 | qnav_stack_push(&qm->qblock, menu_min_h, menu_min_w); |
| 535 | set_menu_win(qm->ncurses_menu, qm->qblock.outer_window); | | |
| 536 | set_menu_sub(qm->ncurses_menu, qm->qblock.content_window); | | |
| 537 | // TODO use this to set how "big" the menu is, visually, for scrolling. | | |
| 538 | // (ncurses can't figure that out on its own, aparently...) | | |
| 539 | // We'll need to split apart some work chunks so that we calculate the size | | |
| 540 | // beforehand. | | |
| 541 | // set_menu_format(qm->ncurses_menu, 5, 1); | | |
| 542 | post_menu(qm->ncurses_menu); | | |
| 543 | } | 471 | } |
| 544 | | 472 | |
| 545 | void qmenu_free(Qmenu *qm) { | 473 | void qmenu_free(Qmenu *qm) { |
| 546 | unpost_menu(qm->ncurses_menu); | 474 | Qmenu_item *items = qm->items; |
| 547 | free_menu(qm->ncurses_menu); | 475 | for (Usz i = 0, n = qm->items_count; i < n; ++i) { |
| 548 | struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); | 476 | if (items[i].owns_string) |
| 549 | for (Usz i = 0; i < qm->items_count; ++i) { | 477 | free((void *)items[i].text); |
| 550 | ITEM *item = qm->ncurses_items[i]; | | |
| 551 | struct Qmenu_item_extra *extra = qmenu_itemextra(extras, item); | | |
| 552 | char const *freed_str = NULL; | | |
| 553 | if (extra->owns_string) | | |
| 554 | freed_str = item_name(item); | | |
| 555 | free_item(qm->ncurses_items[i]); | | |
| 556 | if (freed_str) | | |
| 557 | free((void *)freed_str); | | |
| 558 | } | 478 | } |
| 559 | free(qm->ncurses_items); | 479 | free(qm->items); |
| 560 | free(qm); | 480 | free(qm); |
| 561 | } | 481 | } |
| 562 | | 482 | |
| 563 | ORCA_NOINLINE | 483 | ORCA_NOINLINE static void qmenu_drive_upordown(Qmenu *qm, bool downwards) { |
| 564 | static void qmenu_drive_upordown(Qmenu *qm, int req_up_or_down) { | 484 | Qmenu_item *items = qm->items; |
| 565 | struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); | 485 | Usz n = qm->items_count; |
| 566 | ITEM *starting = current_item(qm->ncurses_menu); | 486 | if (n <= 1) |
| 567 | menu_driver(qm->ncurses_menu, req_up_or_down); | 487 | return; |
| 568 | ITEM *cur = current_item(qm->ncurses_menu); | 488 | int cur_id = qm->current_item; |
| | 489 | Usz starting = 0; |
| | 490 | for (; starting < n; ++starting) { |
| | 491 | if (items[starting].id == cur_id) |
| | 492 | goto found; |
| | 493 | } |
| | 494 | return; |
| | 495 | found:; |
| | 496 | Usz current = starting; |
| 569 | for (;;) { | 497 | for (;;) { |
| 570 | if (!cur || cur == starting) | 498 | if (downwards && current < n - 1) |
| 571 | break; | 499 | current++; |
| 572 | if (!qmenu_itemextra(extras, cur)->is_spacer) | 500 | else if (!downwards && current > 0) |
| | 501 | current--; |
| | 502 | if (current == starting) |
| 573 | break; | 503 | break; |
| 574 | ITEM *prev = cur; | 504 | if (!items[current].is_spacer) |
| 575 | menu_driver(qm->ncurses_menu, req_up_or_down); | | |
| 576 | cur = current_item(qm->ncurses_menu); | | |
| 577 | if (cur == prev) | | |
| 578 | break; | 505 | break; |
| 579 | } | 506 | } |
| | 507 | if (current != starting) { |
| | 508 | qm->current_item = items[current].id; |
| | 509 | qm->needs_reprint = 1; |
| | 510 | } |
| 580 | } | 511 | } |
| 581 | | 512 | |
| 582 | bool qmenu_drive(Qmenu *qm, int key, Qmenu_action *out_action) { | 513 | bool qmenu_drive(Qmenu *qm, int key, Qmenu_action *out_action) { |
| 583 | struct Qmenu_item_extra *extras = qmenu_item_extras_ptr(qm); | | |
| 584 | switch (key) { | 514 | switch (key) { |
| 585 | case 27: { | 515 | case 27: { |
| 586 | out_action->any.type = Qmenu_action_type_canceled; | 516 | out_action->any.type = Qmenu_action_type_canceled; |
| @@ -588,17 +518,15 @@ bool qmenu_drive(Qmenu *qm, int key, Qmenu_action *out_action) { · 1 unmodified line |
| 588 | } | 518 | } |
| 589 | case ' ': | 519 | case ' ': |
| 590 | case '\r': | 520 | case '\r': |
| 591 | case KEY_ENTER: { | 521 | case KEY_ENTER: |
| 592 | ITEM *cur = current_item(qm->ncurses_menu); | | |
| 593 | out_action->picked.type = Qmenu_action_type_picked; | 522 | out_action->picked.type = Qmenu_action_type_picked; |
| 594 | out_action->picked.id = cur ? qmenu_itemextra(extras, cur)->user_id : 0; | 523 | out_action->picked.id = qm->current_item; |
| 595 | return true; | 524 | return true; |
| 596 | } | | |
| 597 | case KEY_UP: | 525 | case KEY_UP: |
| 598 | qmenu_drive_upordown(qm, REQ_UP_ITEM); | 526 | qmenu_drive_upordown(qm, false); |
| 599 | return false; | 527 | return false; |
| 600 | case KEY_DOWN: | 528 | case KEY_DOWN: |
| 601 | qmenu_drive_upordown(qm, REQ_DOWN_ITEM); | 529 | qmenu_drive_upordown(qm, true); |
| 602 | return false; | 530 | return false; |
| 603 | } | 531 | } |
| 604 | return false; | 532 | return false; |