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

   1 /*
   2    Widgets for the Midnight Commander
   3 
   4    Copyright (C) 1994-2024
   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 <http://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 (!g_queue_is_empty (l->list))
 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;
 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 ? DISABLED_COLOR : colors[DLG_COLOR_NORMAL];
 151     selc = disabled ? DISABLED_COLOR : colors[focused ? DLG_COLOR_HOT_FOCUS : DLG_COLOR_FOCUS];
 152 
 153     if (l->list != NULL)
 154     {
 155         length = g_queue_get_length (l->list);
 156         le = g_queue_peek_nth_link (l->list, (guint) l->top);
 157     }
 158 
 159     /*    pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
 160     pos = (le == NULL) ? 0 : l->top;
 161 
 162     for (i = 0; i < w->lines; i++)
 163     {
 164         const char *text = "";
 165 
 166         /* Display the entry */
 167         if (pos == l->current && sel_line == -1)
 168         {
 169             sel_line = i;
 170             tty_setcolor (selc);
 171         }
 172         else
 173             tty_setcolor (normalc);
 174 
 175         widget_gotoyx (l, i, 1);
 176 
 177         if (l->list != NULL && le != NULL && (i == 0 || pos < length))
 178         {
 179             WLEntry *e = LENTRY (le->data);
 180 
 181             text = e->text;
 182             le = g_list_next (le);
 183             pos++;
 184         }
 185 
 186         tty_print_string (str_fit_to_term (text, w->cols - 2, J_LEFT_FIT));
 187     }
 188 
 189     l->cursor_y = sel_line;
 190 
 191     if (l->scrollbar && length > w->lines)
 192     {
 193         tty_setcolor (normalc);
 194         listbox_drawscroll (l);
 195     }
 196 }
 197 
 198 /* --------------------------------------------------------------------------------------------- */
 199 
 200 static int
 201 listbox_check_hotkey (WListbox * l, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 202 {
 203     if (!listbox_is_empty (l))
 204     {
 205         int i;
 206         GList *le;
 207 
 208         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 209         {
 210             WLEntry *e = LENTRY (le->data);
 211 
 212             if (e->hotkey == key)
 213                 return i;
 214         }
 215     }
 216 
 217     return (-1);
 218 }
 219 
 220 /* --------------------------------------------------------------------------------------------- */
 221 
 222 /* Calculates the item displayed at screen row 'y' (y==0 being the widget's 1st row). */
 223 static int
 224 listbox_y_pos (WListbox * l, int y)
     /* [previous][next][first][last][top][bottom][index][help]  */
 225 {
 226     return MIN (l->top + y, LISTBOX_LAST (l));
 227 }
 228 
 229 /* --------------------------------------------------------------------------------------------- */
 230 
 231 static void
 232 listbox_fwd (WListbox * l, gboolean wrap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 233 {
 234     if (!listbox_is_empty (l))
 235     {
 236         if ((guint) l->current + 1 < g_queue_get_length (l->list))
 237             listbox_set_current (l, l->current + 1);
 238         else if (wrap)
 239             listbox_select_first (l);
 240     }
 241 }
 242 
 243 /* --------------------------------------------------------------------------------------------- */
 244 
 245 static void
 246 listbox_fwd_n (WListbox * l, int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 247 {
 248     listbox_set_current (l, MIN (l->current + n, LISTBOX_LAST (l)));
 249 }
 250 
 251 /* --------------------------------------------------------------------------------------------- */
 252 
 253 static void
 254 listbox_back (WListbox * l, gboolean wrap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 255 {
 256     if (!listbox_is_empty (l))
 257     {
 258         if (l->current > 0)
 259             listbox_set_current (l, l->current - 1);
 260         else if (wrap)
 261             listbox_select_last (l);
 262     }
 263 }
 264 
 265 /* --------------------------------------------------------------------------------------------- */
 266 
 267 static void
 268 listbox_back_n (WListbox * l, int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 269 {
 270     listbox_set_current (l, MAX (l->current - n, 0));
 271 }
 272 
 273 /* --------------------------------------------------------------------------------------------- */
 274 
 275 static cb_ret_t
 276 listbox_execute_cmd (WListbox * l, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 277 {
 278     cb_ret_t ret = MSG_HANDLED;
 279     const WRect *w = &CONST_WIDGET (l)->rect;
 280 
 281     if (l->list == NULL || g_queue_is_empty (l->list))
 282         return MSG_NOT_HANDLED;
 283 
 284     switch (command)
 285     {
 286     case CK_Up:
 287         listbox_back (l, TRUE);
 288         break;
 289     case CK_Down:
 290         listbox_fwd (l, TRUE);
 291         break;
 292     case CK_Top:
 293         listbox_select_first (l);
 294         break;
 295     case CK_Bottom:
 296         listbox_select_last (l);
 297         break;
 298     case CK_PageUp:
 299         listbox_back_n (l, w->lines - 1);
 300         break;
 301     case CK_PageDown:
 302         listbox_fwd_n (l, w->lines - 1);
 303         break;
 304     case CK_Delete:
 305         if (l->deletable)
 306         {
 307             gboolean is_last, is_more;
 308             int length;
 309 
 310             length = g_queue_get_length (l->list);
 311 
 312             is_last = (l->current + 1 >= length);
 313             is_more = (l->top + w->lines >= length);
 314 
 315             listbox_remove_current (l);
 316             if ((l->top > 0) && (is_last || is_more))
 317                 l->top--;
 318         }
 319         break;
 320     case CK_Clear:
 321         if (l->deletable && mc_global.widget.confirm_history_cleanup
 322             /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
 323             && (query_dialog (Q_ ("DialogTitle|History cleanup"),
 324                               _("Do you want clean this history?"),
 325                               D_ERROR, 2, _("&Yes"), _("&No")) == 0))
 326             listbox_remove_list (l);
 327         break;
 328     case CK_View:
 329     case CK_Edit:
 330     case CK_Enter:
 331         ret = send_message (WIDGET (l)->owner, l, MSG_NOTIFY, command, NULL);
 332         break;
 333     default:
 334         ret = MSG_NOT_HANDLED;
 335     }
 336 
 337     return ret;
 338 }
 339 
 340 /* --------------------------------------------------------------------------------------------- */
 341 
 342 /* Return MSG_HANDLED if we want a redraw */
 343 static cb_ret_t
 344 listbox_key (WListbox * l, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 345 {
 346     long command;
 347 
 348     if (l->list == NULL)
 349         return MSG_NOT_HANDLED;
 350 
 351     /* focus on listbox item N by '0'..'9' keys */
 352     if (key >= '0' && key <= '9')
 353     {
 354         listbox_set_current (l, key - '0');
 355         return MSG_HANDLED;
 356     }
 357 
 358     command = widget_lookup_key (WIDGET (l), key);
 359     if (command == CK_IgnoreKey)
 360         return MSG_NOT_HANDLED;
 361     return listbox_execute_cmd (l, command);
 362 }
 363 
 364 /* --------------------------------------------------------------------------------------------- */
 365 
 366 /* Listbox item adding function */
 367 static inline void
 368 listbox_add_entry (WListbox * l, WLEntry * e, listbox_append_t pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 369 {
 370     if (l->list == NULL)
 371     {
 372         l->list = g_queue_new ();
 373         pos = LISTBOX_APPEND_AT_END;
 374     }
 375 
 376     switch (pos)
 377     {
 378     case LISTBOX_APPEND_AT_END:
 379         g_queue_push_tail (l->list, e);
 380         break;
 381 
 382     case LISTBOX_APPEND_BEFORE:
 383         g_queue_insert_before (l->list, g_queue_peek_nth_link (l->list, (guint) l->current), e);
 384         break;
 385 
 386     case LISTBOX_APPEND_AFTER:
 387         g_queue_insert_after (l->list, g_queue_peek_nth_link (l->list, (guint) l->current), e);
 388         break;
 389 
 390     case LISTBOX_APPEND_SORTED:
 391         g_queue_insert_sorted (l->list, e, (GCompareDataFunc) listbox_entry_cmp, NULL);
 392         break;
 393 
 394     default:
 395         break;
 396     }
 397 }
 398 
 399 /* --------------------------------------------------------------------------------------------- */
 400 
 401 /* Call this whenever the user changes the selected item. */
 402 static void
 403 listbox_on_change (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 404 {
 405     listbox_draw (l, TRUE);
 406     send_message (WIDGET (l)->owner, l, MSG_NOTIFY, 0, NULL);
 407 }
 408 
 409 /* --------------------------------------------------------------------------------------------- */
 410 
 411 static void
 412 listbox_do_action (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 413 {
 414     int action;
 415 
 416     if (listbox_is_empty (l))
 417         return;
 418 
 419     if (l->callback != NULL)
 420         action = l->callback (l);
 421     else
 422         action = LISTBOX_DONE;
 423 
 424     if (action == LISTBOX_DONE)
 425     {
 426         WDialog *h = DIALOG (WIDGET (l)->owner);
 427 
 428         h->ret_value = B_ENTER;
 429         dlg_close (h);
 430     }
 431 }
 432 
 433 /* --------------------------------------------------------------------------------------------- */
 434 
 435 static void
 436 listbox_run_hotkey (WListbox * l, int pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 437 {
 438     listbox_set_current (l, pos);
 439     listbox_on_change (l);
 440     listbox_do_action (l);
 441 }
 442 
 443 /* --------------------------------------------------------------------------------------------- */
 444 
 445 static inline void
 446 listbox_destroy (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 447 {
 448     listbox_remove_list (l);
 449 }
 450 
 451 /* --------------------------------------------------------------------------------------------- */
 452 
 453 static cb_ret_t
 454 listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 455 {
 456     WListbox *l = LISTBOX (w);
 457 
 458     switch (msg)
 459     {
 460     case MSG_HOTKEY:
 461         {
 462             int pos;
 463 
 464             pos = listbox_check_hotkey (l, parm);
 465             if (pos < 0)
 466                 return MSG_NOT_HANDLED;
 467 
 468             listbox_run_hotkey (l, pos);
 469 
 470             return MSG_HANDLED;
 471         }
 472 
 473     case MSG_KEY:
 474         {
 475             cb_ret_t ret_code;
 476 
 477             ret_code = listbox_key (l, parm);
 478             if (ret_code != MSG_NOT_HANDLED)
 479                 listbox_on_change (l);
 480             return ret_code;
 481         }
 482 
 483     case MSG_ACTION:
 484         return listbox_execute_cmd (l, parm);
 485 
 486     case MSG_CURSOR:
 487         widget_gotoyx (l, l->cursor_y, 0);
 488         return MSG_HANDLED;
 489 
 490     case MSG_DRAW:
 491         listbox_draw (l, widget_get_state (w, WST_FOCUSED));
 492         return MSG_HANDLED;
 493 
 494     case MSG_DESTROY:
 495         listbox_destroy (l);
 496         return MSG_HANDLED;
 497 
 498     default:
 499         return widget_default_callback (w, sender, msg, parm, data);
 500     }
 501 }
 502 
 503 /* --------------------------------------------------------------------------------------------- */
 504 
 505 static void
 506 listbox_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 507 {
 508     WListbox *l = LISTBOX (w);
 509     int old_current;
 510 
 511     old_current = l->current;
 512 
 513     switch (msg)
 514     {
 515     case MSG_MOUSE_DOWN:
 516         widget_select (w);
 517         listbox_set_current (l, listbox_y_pos (l, event->y));
 518         break;
 519 
 520     case MSG_MOUSE_SCROLL_UP:
 521         listbox_back (l, FALSE);
 522         break;
 523 
 524     case MSG_MOUSE_SCROLL_DOWN:
 525         listbox_fwd (l, FALSE);
 526         break;
 527 
 528     case MSG_MOUSE_DRAG:
 529         event->result.repeat = TRUE;    /* It'd be functional even without this. */
 530         listbox_set_current (l, listbox_y_pos (l, event->y));
 531         break;
 532 
 533     case MSG_MOUSE_CLICK:
 534         /* We don't call listbox_set_current() here: MSG_MOUSE_DOWN/DRAG did this already. */
 535         if (event->count == GPM_DOUBLE) /* Double click */
 536             listbox_do_action (l);
 537         break;
 538 
 539     default:
 540         break;
 541     }
 542 
 543     /* If the selection has changed, we redraw the widget and notify the dialog. */
 544     if (l->current != old_current)
 545         listbox_on_change (l);
 546 }
 547 
 548 /* --------------------------------------------------------------------------------------------- */
 549 /*** public functions ****************************************************************************/
 550 /* --------------------------------------------------------------------------------------------- */
 551 
 552 WListbox *
 553 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback)
     /* [previous][next][first][last][top][bottom][index][help]  */
 554 {
 555     WRect r = { y, x, 1, width };
 556     WListbox *l;
 557     Widget *w;
 558 
 559     l = g_new (WListbox, 1);
 560     w = WIDGET (l);
 561     r.lines = height > 0 ? height : 1;
 562     widget_init (w, &r, listbox_callback, listbox_mouse_callback);
 563     w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY;
 564     w->keymap = listbox_map;
 565 
 566     l->list = NULL;
 567     l->top = l->current = 0;
 568     l->deletable = deletable;
 569     l->callback = callback;
 570     l->allow_duplicates = TRUE;
 571     l->scrollbar = !mc_global.tty.slow_terminal;
 572 
 573     return l;
 574 }
 575 
 576 /* --------------------------------------------------------------------------------------------- */
 577 
 578 /**
 579  * Finds item by its label.
 580  */
 581 int
 582 listbox_search_text (WListbox * l, const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 583 {
 584     if (!listbox_is_empty (l))
 585     {
 586         int i;
 587         GList *le;
 588 
 589         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 590         {
 591             WLEntry *e = LENTRY (le->data);
 592 
 593             if (strcmp (e->text, text) == 0)
 594                 return i;
 595         }
 596     }
 597 
 598     return (-1);
 599 }
 600 
 601 /* --------------------------------------------------------------------------------------------- */
 602 
 603 /**
 604  * Finds item by its 'data' slot.
 605  */
 606 int
 607 listbox_search_data (WListbox * l, const void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 608 {
 609     if (!listbox_is_empty (l))
 610     {
 611         int i;
 612         GList *le;
 613 
 614         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 615         {
 616             WLEntry *e = LENTRY (le->data);
 617 
 618             if (e->data == data)
 619                 return i;
 620         }
 621     }
 622 
 623     return (-1);
 624 }
 625 
 626 /* --------------------------------------------------------------------------------------------- */
 627 
 628 /* Select the first entry and scrolls the list to the top */
 629 void
 630 listbox_select_first (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 631 {
 632     l->current = l->top = 0;
 633 }
 634 
 635 /* --------------------------------------------------------------------------------------------- */
 636 
 637 /* Selects the last entry and scrolls the list to the bottom */
 638 void
 639 listbox_select_last (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 640 {
 641     int lines = WIDGET (l)->rect.lines;
 642     int length;
 643 
 644     length = listbox_get_length (l);
 645 
 646     l->current = DOZ (length, 1);
 647     l->top = DOZ (length, lines);
 648 }
 649 
 650 /* --------------------------------------------------------------------------------------------- */
 651 
 652 void
 653 listbox_set_current (WListbox * l, int dest)
     /* [previous][next][first][last][top][bottom][index][help]  */
 654 {
 655     GList *le;
 656     int pos;
 657     gboolean top_seen = FALSE;
 658 
 659     if (listbox_is_empty (l) || dest < 0)
 660         return;
 661 
 662     /* Special case */
 663     for (pos = 0, le = g_queue_peek_head_link (l->list); le != NULL; pos++, le = g_list_next (le))
 664     {
 665         if (pos == l->top)
 666             top_seen = TRUE;
 667 
 668         if (pos == dest)
 669         {
 670             l->current = dest;
 671             if (!top_seen)
 672                 l->top = l->current;
 673             else
 674             {
 675                 int lines = WIDGET (l)->rect.lines;
 676 
 677                 if (l->current - l->top >= lines)
 678                     l->top = l->current - lines + 1;
 679             }
 680             return;
 681         }
 682     }
 683 
 684     /* If we are unable to find it, set decent values */
 685     l->current = l->top = 0;
 686 }
 687 
 688 /* --------------------------------------------------------------------------------------------- */
 689 
 690 int
 691 listbox_get_length (const WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 692 {
 693     return listbox_is_empty (l) ? 0 : (int) g_queue_get_length (l->list);
 694 }
 695 
 696 /* --------------------------------------------------------------------------------------------- */
 697 
 698 /* Returns the current string text as well as the associated extra data */
 699 void
 700 listbox_get_current (WListbox * l, char **string, void **extra)
     /* [previous][next][first][last][top][bottom][index][help]  */
 701 {
 702     WLEntry *e = NULL;
 703     gboolean ok;
 704 
 705     if (l != NULL)
 706         e = listbox_get_nth_entry (l, l->current);
 707 
 708     ok = (e != NULL);
 709 
 710     if (string != NULL)
 711         *string = ok ? e->text : NULL;
 712 
 713     if (extra != NULL)
 714         *extra = ok ? e->data : NULL;
 715 }
 716 
 717 /* --------------------------------------------------------------------------------------------- */
 718 
 719 WLEntry *
 720 listbox_get_nth_entry (const WListbox * l, int pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 721 {
 722     if (!listbox_is_empty (l) && pos >= 0)
 723     {
 724         GList *item;
 725 
 726         item = g_queue_peek_nth_link (l->list, (guint) pos);
 727         if (item != NULL)
 728             return LENTRY (item->data);
 729     }
 730 
 731     return NULL;
 732 }
 733 
 734 /* --------------------------------------------------------------------------------------------- */
 735 
 736 GList *
 737 listbox_get_first_link (const WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 738 {
 739     return (l == NULL || l->list == NULL) ? NULL : g_queue_peek_head_link (l->list);
 740 }
 741 
 742 /* --------------------------------------------------------------------------------------------- */
 743 
 744 void
 745 listbox_remove_current (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 746 {
 747     if (!listbox_is_empty (l))
 748     {
 749         GList *current;
 750         int length;
 751 
 752         current = g_queue_peek_nth_link (l->list, (guint) l->current);
 753         listbox_entry_free (current->data);
 754         g_queue_delete_link (l->list, current);
 755 
 756         length = g_queue_get_length (l->list);
 757 
 758         if (length == 0)
 759             l->top = l->current = 0;
 760         else if (l->current >= length)
 761             l->current = length - 1;
 762     }
 763 }
 764 
 765 /* --------------------------------------------------------------------------------------------- */
 766 
 767 gboolean
 768 listbox_is_empty (const WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 769 {
 770     return (l == NULL || l->list == NULL || g_queue_is_empty (l->list));
 771 }
 772 
 773 /* --------------------------------------------------------------------------------------------- */
 774 
 775 /**
 776  * Set new listbox items list.
 777  *
 778  * @param l WListbox object
 779  * @param list list of WLEntry objects
 780  */
 781 void
 782 listbox_set_list (WListbox * l, GQueue * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 783 {
 784     listbox_remove_list (l);
 785 
 786     if (l != NULL)
 787         l->list = list;
 788 }
 789 
 790 /* --------------------------------------------------------------------------------------------- */
 791 
 792 void
 793 listbox_remove_list (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 794 {
 795     if (l != NULL)
 796     {
 797         if (l->list != NULL)
 798         {
 799             g_queue_free_full (l->list, (GDestroyNotify) listbox_entry_free);
 800             l->list = NULL;
 801         }
 802 
 803         l->current = l->top = 0;
 804     }
 805 }
 806 
 807 /* --------------------------------------------------------------------------------------------- */
 808 
 809 char *
 810 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data,
     /* [previous][next][first][last][top][bottom][index][help]  */
 811                   gboolean free_data)
 812 {
 813     WLEntry *entry;
 814 
 815     if (l == NULL)
 816         return NULL;
 817 
 818     if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
 819         return NULL;
 820 
 821     entry = g_new (WLEntry, 1);
 822     entry->text = g_strdup (text);
 823     entry->data = data;
 824     entry->free_data = free_data;
 825     entry->hotkey = hotkey;
 826 
 827     listbox_add_entry (l, entry, pos);
 828 
 829     return entry->text;
 830 }
 831 
 832 /* --------------------------------------------------------------------------------------------- */

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