Manual pages: mcmcdiffmceditmcview

root/lib/widget/listbox.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. listbox_entry_cmp
  2. listbox_entry_free
  3. listbox_drawscroll
  4. listbox_draw
  5. listbox_check_hotkey
  6. listbox_y_pos
  7. listbox_fwd
  8. listbox_fwd_n
  9. listbox_back
  10. listbox_back_n
  11. listbox_execute_cmd
  12. listbox_key
  13. listbox_add_entry
  14. listbox_on_change
  15. listbox_do_action
  16. listbox_run_hotkey
  17. listbox_destroy
  18. listbox_callback
  19. listbox_mouse_callback
  20. listbox_new
  21. listbox_search_text
  22. listbox_search_data
  23. listbox_select_first
  24. listbox_select_last
  25. listbox_set_current
  26. listbox_get_length
  27. listbox_get_current
  28. listbox_get_nth_entry
  29. listbox_get_first_link
  30. listbox_remove_current
  31. listbox_is_empty
  32. listbox_set_list
  33. listbox_remove_list
  34. listbox_add_item
  35. listbox_add_item_take

   1 /*
   2    Widgets for the Midnight Commander
   3 
   4    Copyright (C) 1994-2025
   5    Free Software Foundation, Inc.
   6 
   7    Authors:
   8    Radek Doulik, 1994, 1995
   9    Miguel de Icaza, 1994, 1995
  10    Jakub Jelinek, 1995
  11    Andrej Borsenkow, 1996
  12    Norbert Warmuth, 1997
  13    Andrew Borodin <aborodin@vmail.ru>, 2009-2022
  14 
  15    This file is part of the Midnight Commander.
  16 
  17    The Midnight Commander is free software: you can redistribute it
  18    and/or modify it under the terms of the GNU General Public License as
  19    published by the Free Software Foundation, either version 3 of the License,
  20    or (at your option) any later version.
  21 
  22    The Midnight Commander is distributed in the hope that it will be useful,
  23    but WITHOUT ANY WARRANTY; without even the implied warranty of
  24    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25    GNU General Public License for more details.
  26 
  27    You should have received a copy of the GNU General Public License
  28    along with this program.  If not, see <https://www.gnu.org/licenses/>.
  29  */
  30 
  31 /** \file listbox.c
  32  *  \brief Source: WListbox widget
  33  */
  34 
  35 #include <config.h>
  36 
  37 #include <stdlib.h>
  38 
  39 #include "lib/global.h"
  40 
  41 #include "lib/tty/tty.h"
  42 #include "lib/skin.h"
  43 #include "lib/strutil.h"
  44 #include "lib/util.h"  // Q_()
  45 #include "lib/widget.h"
  46 
  47 /*** global variables ****************************************************************************/
  48 
  49 const global_keymap_t *listbox_map = NULL;
  50 
  51 /*** file scope macro definitions ****************************************************************/
  52 
  53 /* Gives the position of the last item. */
  54 #define LISTBOX_LAST(l) (listbox_is_empty (l) ? 0 : (int) g_queue_get_length ((l)->list) - 1)
  55 
  56 /*** file scope type declarations ****************************************************************/
  57 
  58 /*** forward declarations (file scope functions) *************************************************/
  59 
  60 /*** file scope variables ************************************************************************/
  61 
  62 /* --------------------------------------------------------------------------------------------- */
  63 /*** file scope functions ************************************************************************/
  64 /* --------------------------------------------------------------------------------------------- */
  65 
  66 static int
  67 listbox_entry_cmp (const void *a, const void *b, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  68 {
  69     const WLEntry *ea = (const WLEntry *) a;
  70     const WLEntry *eb = (const WLEntry *) b;
  71 
  72     (void) user_data;
  73 
  74     return strcmp (ea->text, eb->text);
  75 }
  76 
  77 /* --------------------------------------------------------------------------------------------- */
  78 
  79 static void
  80 listbox_entry_free (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  81 {
  82     WLEntry *e = data;
  83 
  84     g_free (e->text);
  85     if (e->free_data)
  86         g_free (e->data);
  87     g_free (e);
  88 }
  89 
  90 /* --------------------------------------------------------------------------------------------- */
  91 
  92 static void
  93 listbox_drawscroll (const WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
  94 {
  95     const WRect *w = &CONST_WIDGET (l)->rect;
  96     int max_line = w->lines - 1;
  97     int line = 0;
  98     int i;
  99     int length;
 100 
 101     // Are we at the top?
 102     widget_gotoyx (l, 0, w->cols);
 103     if (l->top == 0)
 104         tty_print_one_vline (TRUE);
 105     else
 106         tty_print_char ('^');
 107 
 108     length = g_queue_get_length (l->list);
 109 
 110     // Are we at the bottom?
 111     widget_gotoyx (w, max_line, w->cols);
 112     if (l->top + w->lines == length || w->lines >= length)
 113         tty_print_one_vline (TRUE);
 114     else
 115         tty_print_char ('v');
 116 
 117     // Now draw the nice relative pointer
 118     if (length != 0)
 119         line = 1 + ((l->current * (w->lines - 2)) / length);
 120 
 121     for (i = 1; i < max_line; i++)
 122     {
 123         widget_gotoyx (l, i, w->cols);
 124         if (i != line)
 125             tty_print_one_vline (TRUE);
 126         else
 127             tty_print_char ('*');
 128     }
 129 }
 130 
 131 /* --------------------------------------------------------------------------------------------- */
 132 
 133 static void
 134 listbox_draw (WListbox *l, gboolean focused)
     /* [previous][next][first][last][top][bottom][index][help]  */
 135 {
 136     Widget *wl = WIDGET (l);
 137     const WRect *w = &CONST_WIDGET (l)->rect;
 138     const int *colors;
 139     gboolean disabled;
 140     int normalc, selc, scrollbarc;
 141     int length = 0;
 142     GList *le = NULL;
 143     int pos;
 144     int i;
 145     int sel_line = -1;
 146 
 147     colors = widget_get_colors (wl);
 148 
 149     disabled = widget_get_state (wl, WST_DISABLED);
 150     normalc = disabled ? CORE_DISABLED_COLOR : colors[DLG_COLOR_NORMAL];
 151     selc = disabled ? CORE_DISABLED_COLOR
 152                     : colors[focused ? DLG_COLOR_SELECTED_FOCUS : DLG_COLOR_SELECTED_NORMAL];
 153     scrollbarc = disabled ? CORE_DISABLED_COLOR : colors[DLG_COLOR_FRAME];
 154 
 155     if (l->list != NULL)
 156     {
 157         length = g_queue_get_length (l->list);
 158         le = g_queue_peek_nth_link (l->list, (guint) l->top);
 159     }
 160 
 161     //    pos = (le == NULL) ? 0 : g_list_position (l->list, le);
 162     pos = (le == NULL) ? 0 : l->top;
 163 
 164     for (i = 0; i < w->lines; i++)
 165     {
 166         const char *text = "";
 167 
 168         // Display the entry
 169         if (pos == l->current && sel_line == -1)
 170         {
 171             sel_line = i;
 172             tty_setcolor (selc);
 173         }
 174         else
 175             tty_setcolor (normalc);
 176 
 177         widget_gotoyx (l, i, 1);
 178 
 179         if (l->list != NULL && le != NULL && (i == 0 || pos < length))
 180         {
 181             WLEntry *e = LENTRY (le->data);
 182 
 183             text = e->text;
 184             le = g_list_next (le);
 185             pos++;
 186         }
 187 
 188         tty_print_string (str_fit_to_term (text, w->cols - 2, J_LEFT_FIT));
 189     }
 190 
 191     l->cursor_y = sel_line;
 192 
 193     if (l->scrollbar && length > w->lines)
 194     {
 195         tty_setcolor (scrollbarc);
 196         listbox_drawscroll (l);
 197     }
 198 }
 199 
 200 /* --------------------------------------------------------------------------------------------- */
 201 
 202 static int
 203 listbox_check_hotkey (WListbox *l, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 204 {
 205     if (!listbox_is_empty (l))
 206     {
 207         int i;
 208         GList *le;
 209 
 210         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 211         {
 212             WLEntry *e = LENTRY (le->data);
 213 
 214             if (e->hotkey == key)
 215                 return i;
 216         }
 217     }
 218 
 219     return (-1);
 220 }
 221 
 222 /* --------------------------------------------------------------------------------------------- */
 223 
 224 /* Calculates the item displayed at screen row 'y' (y==0 being the widget's 1st row). */
 225 static int
 226 listbox_y_pos (WListbox *l, int y)
     /* [previous][next][first][last][top][bottom][index][help]  */
 227 {
 228     return MIN (l->top + y, LISTBOX_LAST (l));
 229 }
 230 
 231 /* --------------------------------------------------------------------------------------------- */
 232 
 233 static void
 234 listbox_fwd (WListbox *l, gboolean wrap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 235 {
 236     if (!listbox_is_empty (l))
 237     {
 238         if ((guint) l->current + 1 < g_queue_get_length (l->list))
 239             listbox_set_current (l, l->current + 1);
 240         else if (wrap)
 241             listbox_select_first (l);
 242     }
 243 }
 244 
 245 /* --------------------------------------------------------------------------------------------- */
 246 
 247 static void
 248 listbox_fwd_n (WListbox *l, int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 249 {
 250     listbox_set_current (l, MIN (l->current + n, LISTBOX_LAST (l)));
 251 }
 252 
 253 /* --------------------------------------------------------------------------------------------- */
 254 
 255 static void
 256 listbox_back (WListbox *l, gboolean wrap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 257 {
 258     if (!listbox_is_empty (l))
 259     {
 260         if (l->current > 0)
 261             listbox_set_current (l, l->current - 1);
 262         else if (wrap)
 263             listbox_select_last (l);
 264     }
 265 }
 266 
 267 /* --------------------------------------------------------------------------------------------- */
 268 
 269 static void
 270 listbox_back_n (WListbox *l, int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 271 {
 272     listbox_set_current (l, MAX (l->current - n, 0));
 273 }
 274 
 275 /* --------------------------------------------------------------------------------------------- */
 276 
 277 static cb_ret_t
 278 listbox_execute_cmd (WListbox *l, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 279 {
 280     cb_ret_t ret = MSG_HANDLED;
 281     const WRect *w = &CONST_WIDGET (l)->rect;
 282 
 283     if (l->list == NULL || g_queue_is_empty (l->list))
 284         return MSG_NOT_HANDLED;
 285 
 286     switch (command)
 287     {
 288     case CK_Up:
 289         listbox_back (l, TRUE);
 290         break;
 291     case CK_Down:
 292         listbox_fwd (l, TRUE);
 293         break;
 294     case CK_Top:
 295         listbox_select_first (l);
 296         break;
 297     case CK_Bottom:
 298         listbox_select_last (l);
 299         break;
 300     case CK_PageUp:
 301         listbox_back_n (l, w->lines - 1);
 302         break;
 303     case CK_PageDown:
 304         listbox_fwd_n (l, w->lines - 1);
 305         break;
 306     case CK_Delete:
 307         if (l->deletable)
 308         {
 309             gboolean is_last, is_more;
 310             int length;
 311 
 312             length = g_queue_get_length (l->list);
 313 
 314             is_last = (l->current + 1 >= length);
 315             is_more = (l->top + w->lines >= length);
 316 
 317             listbox_remove_current (l);
 318             if ((l->top > 0) && (is_last || is_more))
 319                 l->top--;
 320         }
 321         break;
 322     case CK_Clear:
 323         if (l->deletable
 324             && mc_global.widget.confirm_history_cleanup
 325             // TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix
 326             && (query_dialog (Q_ ("DialogTitle|History cleanup"),
 327                               _ ("Do you want clean this history?"), D_ERROR, 2, _ ("&Yes"),
 328                               _ ("&No"))
 329                 == 0))
 330             listbox_remove_list (l);
 331         break;
 332     case CK_View:
 333     case CK_Edit:
 334     case CK_Enter:
 335         ret = send_message (WIDGET (l)->owner, l, MSG_NOTIFY, command, NULL);
 336         break;
 337     default:
 338         ret = MSG_NOT_HANDLED;
 339     }
 340 
 341     return ret;
 342 }
 343 
 344 /* --------------------------------------------------------------------------------------------- */
 345 
 346 /* Return MSG_HANDLED if we want a redraw */
 347 static cb_ret_t
 348 listbox_key (WListbox *l, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 349 {
 350     long command;
 351 
 352     if (l->list == NULL)
 353         return MSG_NOT_HANDLED;
 354 
 355     // focus on listbox item N by '0'..'9' keys
 356     if (key >= '0' && key <= '9')
 357     {
 358         listbox_set_current (l, key - '0');
 359         return MSG_HANDLED;
 360     }
 361 
 362     command = widget_lookup_key (WIDGET (l), key);
 363     if (command == CK_IgnoreKey)
 364         return MSG_NOT_HANDLED;
 365     return listbox_execute_cmd (l, command);
 366 }
 367 
 368 /* --------------------------------------------------------------------------------------------- */
 369 
 370 /* Listbox item adding function */
 371 static inline void
 372 listbox_add_entry (WListbox *l, WLEntry *e, listbox_append_t pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 373 {
 374     if (l->list == NULL)
 375     {
 376         l->list = g_queue_new ();
 377         pos = LISTBOX_APPEND_AT_END;
 378     }
 379 
 380     switch (pos)
 381     {
 382     case LISTBOX_APPEND_AT_END:
 383         g_queue_push_tail (l->list, e);
 384         break;
 385 
 386     case LISTBOX_APPEND_BEFORE:
 387         g_queue_insert_before (l->list, g_queue_peek_nth_link (l->list, (guint) l->current), e);
 388         break;
 389 
 390     case LISTBOX_APPEND_AFTER:
 391         g_queue_insert_after (l->list, g_queue_peek_nth_link (l->list, (guint) l->current), e);
 392         break;
 393 
 394     case LISTBOX_APPEND_SORTED:
 395         g_queue_insert_sorted (l->list, e, (GCompareDataFunc) listbox_entry_cmp, NULL);
 396         break;
 397 
 398     default:
 399         break;
 400     }
 401 }
 402 
 403 /* --------------------------------------------------------------------------------------------- */
 404 
 405 /* Call this whenever the user changes the selected item. */
 406 static void
 407 listbox_on_change (WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 408 {
 409     listbox_draw (l, TRUE);
 410     send_message (WIDGET (l)->owner, l, MSG_NOTIFY, 0, NULL);
 411 }
 412 
 413 /* --------------------------------------------------------------------------------------------- */
 414 
 415 static void
 416 listbox_do_action (WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 417 {
 418     int action;
 419 
 420     if (listbox_is_empty (l))
 421         return;
 422 
 423     if (l->callback != NULL)
 424         action = l->callback (l);
 425     else
 426         action = LISTBOX_DONE;
 427 
 428     if (action == LISTBOX_DONE)
 429     {
 430         WDialog *h = DIALOG (WIDGET (l)->owner);
 431 
 432         h->ret_value = B_ENTER;
 433         dlg_close (h);
 434     }
 435 }
 436 
 437 /* --------------------------------------------------------------------------------------------- */
 438 
 439 static void
 440 listbox_run_hotkey (WListbox *l, int pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 441 {
 442     listbox_set_current (l, pos);
 443     listbox_on_change (l);
 444     listbox_do_action (l);
 445 }
 446 
 447 /* --------------------------------------------------------------------------------------------- */
 448 
 449 static inline void
 450 listbox_destroy (WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 451 {
 452     listbox_remove_list (l);
 453 }
 454 
 455 /* --------------------------------------------------------------------------------------------- */
 456 
 457 static cb_ret_t
 458 listbox_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 459 {
 460     WListbox *l = LISTBOX (w);
 461 
 462     switch (msg)
 463     {
 464     case MSG_HOTKEY:
 465     {
 466         int pos;
 467 
 468         pos = listbox_check_hotkey (l, parm);
 469         if (pos < 0)
 470             return MSG_NOT_HANDLED;
 471 
 472         listbox_run_hotkey (l, pos);
 473 
 474         return MSG_HANDLED;
 475     }
 476 
 477     case MSG_KEY:
 478     {
 479         cb_ret_t ret_code;
 480 
 481         ret_code = listbox_key (l, parm);
 482         if (ret_code != MSG_NOT_HANDLED)
 483             listbox_on_change (l);
 484         return ret_code;
 485     }
 486 
 487     case MSG_ACTION:
 488         return listbox_execute_cmd (l, parm);
 489 
 490     case MSG_CURSOR:
 491         widget_gotoyx (l, l->cursor_y, 0);
 492         return MSG_HANDLED;
 493 
 494     case MSG_DRAW:
 495         listbox_draw (l, widget_get_state (w, WST_FOCUSED));
 496         return MSG_HANDLED;
 497 
 498     case MSG_DESTROY:
 499         listbox_destroy (l);
 500         return MSG_HANDLED;
 501 
 502     default:
 503         return widget_default_callback (w, sender, msg, parm, data);
 504     }
 505 }
 506 
 507 /* --------------------------------------------------------------------------------------------- */
 508 
 509 static void
 510 listbox_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 511 {
 512     WListbox *l = LISTBOX (w);
 513     int old_current;
 514 
 515     old_current = l->current;
 516 
 517     switch (msg)
 518     {
 519     case MSG_MOUSE_DOWN:
 520         widget_select (w);
 521         listbox_set_current (l, listbox_y_pos (l, event->y));
 522         break;
 523 
 524     case MSG_MOUSE_SCROLL_UP:
 525         listbox_back (l, FALSE);
 526         break;
 527 
 528     case MSG_MOUSE_SCROLL_DOWN:
 529         listbox_fwd (l, FALSE);
 530         break;
 531 
 532     case MSG_MOUSE_DRAG:
 533         event->result.repeat = TRUE;  // It'd be functional even without this
 534         listbox_set_current (l, listbox_y_pos (l, event->y));
 535         break;
 536 
 537     case MSG_MOUSE_CLICK:
 538         // We don't call listbox_set_current() here: MSG_MOUSE_DOWN/DRAG did this already
 539         if (event->count == GPM_DOUBLE)  // Double click
 540             listbox_do_action (l);
 541         break;
 542 
 543     default:
 544         break;
 545     }
 546 
 547     // If the selection has changed, we redraw the widget and notify the dialog.
 548     if (l->current != old_current)
 549         listbox_on_change (l);
 550 }
 551 
 552 /* --------------------------------------------------------------------------------------------- */
 553 /*** public functions ****************************************************************************/
 554 /* --------------------------------------------------------------------------------------------- */
 555 
 556 WListbox *
 557 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback)
     /* [previous][next][first][last][top][bottom][index][help]  */
 558 {
 559     WRect r = { y, x, 1, width };
 560     WListbox *l;
 561     Widget *w;
 562 
 563     l = g_new (WListbox, 1);
 564     w = WIDGET (l);
 565     r.lines = height > 0 ? height : 1;
 566     widget_init (w, &r, listbox_callback, listbox_mouse_callback);
 567     w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY;
 568     w->keymap = listbox_map;
 569 
 570     l->list = NULL;
 571     l->top = l->current = 0;
 572     l->deletable = deletable;
 573     l->callback = callback;
 574     l->allow_duplicates = TRUE;
 575     l->scrollbar = !mc_global.tty.slow_terminal;
 576 
 577     return l;
 578 }
 579 
 580 /* --------------------------------------------------------------------------------------------- */
 581 
 582 /**
 583  * Finds item by its label.
 584  */
 585 int
 586 listbox_search_text (WListbox *l, const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 587 {
 588     if (!listbox_is_empty (l))
 589     {
 590         int i;
 591         GList *le;
 592 
 593         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 594         {
 595             WLEntry *e = LENTRY (le->data);
 596 
 597             if (strcmp (e->text, text) == 0)
 598                 return i;
 599         }
 600     }
 601 
 602     return (-1);
 603 }
 604 
 605 /* --------------------------------------------------------------------------------------------- */
 606 
 607 /**
 608  * Finds item by its 'data' slot.
 609  */
 610 int
 611 listbox_search_data (WListbox *l, const void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 612 {
 613     if (!listbox_is_empty (l))
 614     {
 615         int i;
 616         GList *le;
 617 
 618         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 619         {
 620             WLEntry *e = LENTRY (le->data);
 621 
 622             if (e->data == data)
 623                 return i;
 624         }
 625     }
 626 
 627     return (-1);
 628 }
 629 
 630 /* --------------------------------------------------------------------------------------------- */
 631 
 632 /* Select the first entry and scrolls the list to the top */
 633 void
 634 listbox_select_first (WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 635 {
 636     l->current = l->top = 0;
 637 }
 638 
 639 /* --------------------------------------------------------------------------------------------- */
 640 
 641 /* Selects the last entry and scrolls the list to the bottom */
 642 void
 643 listbox_select_last (WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 644 {
 645     int lines = WIDGET (l)->rect.lines;
 646     int length;
 647 
 648     length = listbox_get_length (l);
 649 
 650     l->current = DOZ (length, 1);
 651     l->top = DOZ (length, lines);
 652 }
 653 
 654 /* --------------------------------------------------------------------------------------------- */
 655 
 656 void
 657 listbox_set_current (WListbox *l, int dest)
     /* [previous][next][first][last][top][bottom][index][help]  */
 658 {
 659     GList *le;
 660     int pos;
 661     gboolean top_seen = FALSE;
 662 
 663     if (listbox_is_empty (l) || dest < 0)
 664         return;
 665 
 666     // Special case
 667     for (pos = 0, le = g_queue_peek_head_link (l->list); le != NULL; pos++, le = g_list_next (le))
 668     {
 669         if (pos == l->top)
 670             top_seen = TRUE;
 671 
 672         if (pos == dest)
 673         {
 674             l->current = dest;
 675             if (!top_seen)
 676                 l->top = l->current;
 677             else
 678             {
 679                 int lines = WIDGET (l)->rect.lines;
 680 
 681                 if (l->current - l->top >= lines)
 682                     l->top = l->current - lines + 1;
 683             }
 684             return;
 685         }
 686     }
 687 
 688     // If we are unable to find it, set decent values
 689     l->current = l->top = 0;
 690 }
 691 
 692 /* --------------------------------------------------------------------------------------------- */
 693 
 694 int
 695 listbox_get_length (const WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 696 {
 697     return listbox_is_empty (l) ? 0 : (int) g_queue_get_length (l->list);
 698 }
 699 
 700 /* --------------------------------------------------------------------------------------------- */
 701 
 702 /* Returns the current string text as well as the associated extra data */
 703 void
 704 listbox_get_current (WListbox *l, char **string, void **extra)
     /* [previous][next][first][last][top][bottom][index][help]  */
 705 {
 706     WLEntry *e = NULL;
 707     gboolean ok;
 708 
 709     if (l != NULL)
 710         e = listbox_get_nth_entry (l, l->current);
 711 
 712     ok = (e != NULL);
 713 
 714     if (string != NULL)
 715         *string = ok ? e->text : NULL;
 716 
 717     if (extra != NULL)
 718         *extra = ok ? e->data : NULL;
 719 }
 720 
 721 /* --------------------------------------------------------------------------------------------- */
 722 
 723 WLEntry *
 724 listbox_get_nth_entry (const WListbox *l, int pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 725 {
 726     if (!listbox_is_empty (l) && pos >= 0)
 727     {
 728         GList *item;
 729 
 730         item = g_queue_peek_nth_link (l->list, (guint) pos);
 731         if (item != NULL)
 732             return LENTRY (item->data);
 733     }
 734 
 735     return NULL;
 736 }
 737 
 738 /* --------------------------------------------------------------------------------------------- */
 739 
 740 GList *
 741 listbox_get_first_link (const WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 742 {
 743     return (l == NULL || l->list == NULL) ? NULL : g_queue_peek_head_link (l->list);
 744 }
 745 
 746 /* --------------------------------------------------------------------------------------------- */
 747 
 748 void
 749 listbox_remove_current (WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 750 {
 751     if (!listbox_is_empty (l))
 752     {
 753         GList *current;
 754         int length;
 755 
 756         current = g_queue_peek_nth_link (l->list, (guint) l->current);
 757         listbox_entry_free (current->data);
 758         g_queue_delete_link (l->list, current);
 759 
 760         length = g_queue_get_length (l->list);
 761 
 762         if (length == 0)
 763             l->top = l->current = 0;
 764         else if (l->current >= length)
 765             l->current = length - 1;
 766     }
 767 }
 768 
 769 /* --------------------------------------------------------------------------------------------- */
 770 
 771 gboolean
 772 listbox_is_empty (const WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 773 {
 774     return (l == NULL || l->list == NULL || g_queue_is_empty (l->list));
 775 }
 776 
 777 /* --------------------------------------------------------------------------------------------- */
 778 
 779 /**
 780  * Set new listbox items list.
 781  *
 782  * @param l WListbox object
 783  * @param list list of WLEntry objects
 784  */
 785 void
 786 listbox_set_list (WListbox *l, GQueue *list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 787 {
 788     listbox_remove_list (l);
 789 
 790     if (l != NULL)
 791         l->list = list;
 792 }
 793 
 794 /* --------------------------------------------------------------------------------------------- */
 795 
 796 void
 797 listbox_remove_list (WListbox *l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 798 {
 799     if (l != NULL)
 800     {
 801         if (l->list != NULL)
 802         {
 803             g_queue_free_full (l->list, (GDestroyNotify) listbox_entry_free);
 804             l->list = NULL;
 805         }
 806 
 807         l->current = l->top = 0;
 808     }
 809 }
 810 
 811 /* --------------------------------------------------------------------------------------------- */
 812 
 813 /**
 814  * Add new intem to the listbox.
 815  *
 816  * @param l WListbox object
 817  * @param pos position of the item
 818  * @param hotkey position of the item
 819  * @param text item text. @l takes the copy of @text.
 820  * @param data item data
 821  * @param free_data if TRUE free the @data when @l is destroyed,
 822  *
 823  * @return pointer to copy of @text.
 824  */
 825 char *
 826 listbox_add_item (WListbox *l, listbox_append_t pos, int hotkey, const char *text, void *data,
     /* [previous][next][first][last][top][bottom][index][help]  */
 827                   gboolean free_data)
 828 {
 829     return listbox_add_item_take (l, pos, hotkey, g_strdup (text), data, free_data);
 830 }
 831 
 832 /* --------------------------------------------------------------------------------------------- */
 833 
 834 /**
 835  * Add new intem to the listbox.
 836  *
 837  * @param l WListbox object
 838  * @param pos position of the item
 839  * @param hotkey position of the item
 840  * @param text item text. Ownership of the text is transferred to the @l.
 841  * @param data item data
 842  * @param free_data if TRUE free the @data when @l is destroyed,
 843  *
 844  * After this call, @text belongs to the @l and may no longer be modified by the caller.
 845  *
 846  * @return pointer to @text.
 847  */
 848 char *
 849 listbox_add_item_take (WListbox *l, listbox_append_t pos, int hotkey, char *text, void *data,
     /* [previous][next][first][last][top][bottom][index][help]  */
 850                        gboolean free_data)
 851 {
 852     WLEntry *entry;
 853 
 854     if (l == NULL)
 855         return NULL;
 856 
 857     if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
 858         return NULL;
 859 
 860     entry = g_new (WLEntry, 1);
 861     entry->text = text;
 862     entry->data = data;
 863     entry->free_data = free_data;
 864     entry->hotkey = hotkey;
 865 
 866     listbox_add_entry (l, entry, pos);
 867 
 868     return entry->text;
 869 }
 870 
 871 /* --------------------------------------------------------------------------------------------- */

/* [previous][next][first][last][top][bottom][index][help]  */