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_current
  27. listbox_get_nth_item
  28. listbox_get_first_link
  29. listbox_remove_current
  30. listbox_is_empty
  31. listbox_set_list
  32. listbox_remove_list
  33. listbox_add_item

   1 /*
   2    Widgets for the Midnight Commander
   3 
   4    Copyright (C) 1994-2019
   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/keybind.h"        /* global_keymap_t */
  46 #include "lib/widget.h"
  47 
  48 /*** global variables ****************************************************************************/
  49 
  50 const global_keymap_t *listbox_map = NULL;
  51 
  52 /*** file scope macro definitions ****************************************************************/
  53 
  54 /* Gives the position of the last item. */
  55 #define LISTBOX_LAST(l) (listbox_is_empty (l) ? 0 : (int) g_queue_get_length ((l)->list) - 1)
  56 
  57 /*** file scope type declarations ****************************************************************/
  58 
  59 /*** file scope variables ************************************************************************/
  60 
  61 /*** file scope functions ************************************************************************/
  62 
  63 static int
  64 listbox_entry_cmp (const void *a, const void *b, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  65 {
  66     const WLEntry *ea = (const WLEntry *) a;
  67     const WLEntry *eb = (const WLEntry *) b;
  68 
  69     (void) user_data;
  70 
  71     return strcmp (ea->text, eb->text);
  72 }
  73 
  74 /* --------------------------------------------------------------------------------------------- */
  75 
  76 static void
  77 listbox_entry_free (void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
  78 {
  79     WLEntry *e = data;
  80 
  81     g_free (e->text);
  82     if (e->free_data)
  83         g_free (e->data);
  84     g_free (e);
  85 }
  86 
  87 /* --------------------------------------------------------------------------------------------- */
  88 
  89 static void
  90 listbox_drawscroll (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
  91 {
  92     Widget *w = WIDGET (l);
  93     int max_line = w->lines - 1;
  94     int line = 0;
  95     int i;
  96     int length;
  97 
  98     /* Are we at the top? */
  99     widget_move (w, 0, w->cols);
 100     if (l->top == 0)
 101         tty_print_one_vline (TRUE);
 102     else
 103         tty_print_char ('^');
 104 
 105     length = g_queue_get_length (l->list);
 106 
 107     /* Are we at the bottom? */
 108     widget_move (w, max_line, w->cols);
 109     if (l->top + w->lines == length || w->lines >= length)
 110         tty_print_one_vline (TRUE);
 111     else
 112         tty_print_char ('v');
 113 
 114     /* Now draw the nice relative pointer */
 115     if (!g_queue_is_empty (l->list))
 116         line = 1 + ((l->pos * (w->lines - 2)) / length);
 117 
 118     for (i = 1; i < max_line; i++)
 119     {
 120         widget_move (w, i, w->cols);
 121         if (i != line)
 122             tty_print_one_vline (TRUE);
 123         else
 124             tty_print_char ('*');
 125     }
 126 }
 127 
 128 /* --------------------------------------------------------------------------------------------- */
 129 
 130 static void
 131 listbox_draw (WListbox * l, gboolean focused)
     /* [previous][next][first][last][top][bottom][index][help]  */
 132 {
 133     Widget *w = WIDGET (l);
 134     const WDialog *h = w->owner;
 135     gboolean disabled;
 136     int normalc, selc;
 137     int length = 0;
 138     GList *le = NULL;
 139     int pos;
 140     int i;
 141     int sel_line = -1;
 142 
 143     disabled = widget_get_state (w, WST_DISABLED);
 144     normalc = disabled ? DISABLED_COLOR : h->color[DLG_COLOR_NORMAL];
 145     /* *INDENT-OFF* */
 146     selc = disabled
 147         ? DISABLED_COLOR
 148         : focused
 149             ? h->color[DLG_COLOR_HOT_FOCUS] 
 150             : h->color[DLG_COLOR_FOCUS];
 151     /* *INDENT-ON* */
 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->pos && sel_line == -1)
 168         {
 169             sel_line = i;
 170             tty_setcolor (selc);
 171         }
 172         else
 173             tty_setcolor (normalc);
 174 
 175         widget_move (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->pos + 1 < g_queue_get_length (l->list))
 237             listbox_select_entry (l, l->pos + 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_select_entry (l, MIN (l->pos + 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->pos > 0)
 259             listbox_select_entry (l, l->pos - 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_select_entry (l, MAX (l->pos - 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     Widget *w = WIDGET (l);
 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->pos + 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     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 = keybind_lookup_keymap_command (listbox_map, 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, l->pos, 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 = 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     cb_ret_t ret_code;
 453 
 454     switch (msg)
 455     {
 456     case MSG_HOTKEY:
 457         {
 458             int pos;
 459 
 460             pos = listbox_check_hotkey (l, parm);
 461             if (pos < 0)
 462                 return MSG_NOT_HANDLED;
 463 
 464             listbox_run_hotkey (l, pos);
 465 
 466             return MSG_HANDLED;
 467         }
 468 
 469     case MSG_KEY:
 470         ret_code = listbox_key (l, parm);
 471         if (ret_code != MSG_NOT_HANDLED)
 472             listbox_on_change (l);
 473         return ret_code;
 474 
 475     case MSG_ACTION:
 476         return listbox_execute_cmd (l, parm);
 477 
 478     case MSG_CURSOR:
 479         widget_move (l, l->cursor_y, 0);
 480         return MSG_HANDLED;
 481 
 482     case MSG_DRAW:
 483         listbox_draw (l, widget_get_state (w, WST_FOCUSED));
 484         return MSG_HANDLED;
 485 
 486     case MSG_DESTROY:
 487         listbox_destroy (l);
 488         return MSG_HANDLED;
 489 
 490     case MSG_RESIZE:
 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 
 561     l->list = NULL;
 562     l->top = l->pos = 0;
 563     l->deletable = deletable;
 564     l->callback = callback;
 565     l->allow_duplicates = TRUE;
 566     l->scrollbar = !mc_global.tty.slow_terminal;
 567 
 568     return l;
 569 }
 570 
 571 /* --------------------------------------------------------------------------------------------- */
 572 
 573 /**
 574  * Finds item by its label.
 575  */
 576 int
 577 listbox_search_text (WListbox * l, const char *text)
     /* [previous][next][first][last][top][bottom][index][help]  */
 578 {
 579     if (!listbox_is_empty (l))
 580     {
 581         int i;
 582         GList *le;
 583 
 584         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 585         {
 586             WLEntry *e = LENTRY (le->data);
 587 
 588             if (strcmp (e->text, text) == 0)
 589                 return i;
 590         }
 591     }
 592 
 593     return (-1);
 594 }
 595 
 596 /* --------------------------------------------------------------------------------------------- */
 597 
 598 /**
 599  * Finds item by its 'data' slot.
 600  */
 601 int
 602 listbox_search_data (WListbox * l, const void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 603 {
 604     if (!listbox_is_empty (l))
 605     {
 606         int i;
 607         GList *le;
 608 
 609         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 610         {
 611             WLEntry *e = LENTRY (le->data);
 612 
 613             if (e->data == data)
 614                 return i;
 615         }
 616     }
 617 
 618     return (-1);
 619 }
 620 
 621 /* --------------------------------------------------------------------------------------------- */
 622 
 623 /* Selects the first entry and scrolls the list to the top */
 624 void
 625 listbox_select_first (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 626 {
 627     l->pos = l->top = 0;
 628 }
 629 
 630 /* --------------------------------------------------------------------------------------------- */
 631 
 632 /* Selects the last entry and scrolls the list to the bottom */
 633 void
 634 listbox_select_last (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 635 {
 636     int lines = WIDGET (l)->lines;
 637     int length = 0;
 638 
 639     if (!listbox_is_empty (l))
 640         length = g_queue_get_length (l->list);
 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 /* Returns the current string text as well as the associated extra data */
 687 void
 688 listbox_get_current (WListbox * l, char **string, void **extra)
     /* [previous][next][first][last][top][bottom][index][help]  */
 689 {
 690     WLEntry *e = NULL;
 691     gboolean ok;
 692 
 693     if (l != NULL)
 694         e = listbox_get_nth_item (l, l->pos);
 695 
 696     ok = (e != NULL);
 697 
 698     if (string != NULL)
 699         *string = ok ? e->text : NULL;
 700 
 701     if (extra != NULL)
 702         *extra = ok ? e->data : NULL;
 703 }
 704 
 705 /* --------------------------------------------------------------------------------------------- */
 706 
 707 WLEntry *
 708 listbox_get_nth_item (const WListbox * l, int pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 709 {
 710     if (!listbox_is_empty (l) && pos >= 0)
 711     {
 712         GList *item;
 713 
 714         item = g_queue_peek_nth_link (l->list, (guint) pos);
 715         if (item != NULL)
 716             return LENTRY (item->data);
 717     }
 718 
 719     return NULL;
 720 }
 721 
 722 /* --------------------------------------------------------------------------------------------- */
 723 
 724 GList *
 725 listbox_get_first_link (const WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 726 {
 727     return (l == NULL || l->list == NULL) ? NULL : g_queue_peek_head_link (l->list);
 728 }
 729 
 730 /* --------------------------------------------------------------------------------------------- */
 731 
 732 void
 733 listbox_remove_current (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 734 {
 735     if (!listbox_is_empty (l))
 736     {
 737         GList *current;
 738         int length;
 739 
 740         current = g_queue_peek_nth_link (l->list, (guint) l->pos);
 741         listbox_entry_free (LENTRY (current->data));
 742         g_queue_delete_link (l->list, current);
 743 
 744         length = g_queue_get_length (l->list);
 745 
 746         if (length == 0)
 747             l->top = l->pos = 0;
 748         else if (l->pos >= length)
 749             l->pos = length - 1;
 750     }
 751 }
 752 
 753 /* --------------------------------------------------------------------------------------------- */
 754 
 755 gboolean
 756 listbox_is_empty (const WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 757 {
 758     return (l == NULL || l->list == NULL || g_queue_is_empty (l->list));
 759 }
 760 
 761 /* --------------------------------------------------------------------------------------------- */
 762 
 763 void
 764 listbox_set_list (WListbox * l, GList * list)
     /* [previous][next][first][last][top][bottom][index][help]  */
 765 {
 766     listbox_remove_list (l);
 767 
 768     if (l != NULL)
 769     {
 770         GList *ll;
 771 
 772         l->list = g_queue_new ();
 773 
 774         for (ll = list; ll != NULL; ll = g_list_next (ll))
 775             g_queue_push_tail (l->list, ll->data);
 776 
 777         g_list_free (list);
 778     }
 779 }
 780 
 781 /* --------------------------------------------------------------------------------------------- */
 782 
 783 void
 784 listbox_remove_list (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 785 {
 786     if (l != NULL)
 787     {
 788         if (l->list != NULL)
 789         {
 790             g_queue_free_full (l->list, (GDestroyNotify) listbox_entry_free);
 791             l->list = NULL;
 792         }
 793 
 794         l->pos = l->top = 0;
 795     }
 796 }
 797 
 798 /* --------------------------------------------------------------------------------------------- */
 799 
 800 char *
 801 listbox_add_item (WListbox * l, listbox_append_t pos, int hotkey, const char *text, void *data,
     /* [previous][next][first][last][top][bottom][index][help]  */
 802                   gboolean free_data)
 803 {
 804     WLEntry *entry;
 805 
 806     if (l == NULL)
 807         return NULL;
 808 
 809     if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
 810         return NULL;
 811 
 812     entry = g_new (WLEntry, 1);
 813     entry->text = g_strdup (text);
 814     entry->data = data;
 815     entry->free_data = free_data;
 816     entry->hotkey = hotkey;
 817 
 818     listbox_append_item (l, entry, pos);
 819 
 820     return entry->text;
 821 }
 822 
 823 /* --------------------------------------------------------------------------------------------- */

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