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

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