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-2022
   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 /*** 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 (const WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
  90 {
  91     const WRect *w = &CONST_WIDGET (l)->rect;
  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 (l, 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 (l, 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 *wl = WIDGET (l);
 133     const WRect *w = &CONST_WIDGET (l)->rect;
 134     const int *colors;
 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     colors = widget_get_colors (wl);
 144 
 145     disabled = widget_get_state (wl, WST_DISABLED);
 146     normalc = disabled ? DISABLED_COLOR : colors[DLG_COLOR_NORMAL];
 147     selc = disabled ? DISABLED_COLOR : colors[focused ? DLG_COLOR_HOT_FOCUS : DLG_COLOR_FOCUS];
 148 
 149     if (l->list != NULL)
 150     {
 151         length = g_queue_get_length (l->list);
 152         le = g_queue_peek_nth_link (l->list, (guint) l->top);
 153     }
 154 
 155     /*    pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
 156     pos = (le == NULL) ? 0 : l->top;
 157 
 158     for (i = 0; i < w->lines; i++)
 159     {
 160         const char *text = "";
 161 
 162         /* Display the entry */
 163         if (pos == l->pos && sel_line == -1)
 164         {
 165             sel_line = i;
 166             tty_setcolor (selc);
 167         }
 168         else
 169             tty_setcolor (normalc);
 170 
 171         widget_gotoyx (l, i, 1);
 172 
 173         if (l->list != NULL && le != NULL && (i == 0 || pos < length))
 174         {
 175             WLEntry *e = LENTRY (le->data);
 176 
 177             text = e->text;
 178             le = g_list_next (le);
 179             pos++;
 180         }
 181 
 182         tty_print_string (str_fit_to_term (text, w->cols - 2, J_LEFT_FIT));
 183     }
 184 
 185     l->cursor_y = sel_line;
 186 
 187     if (l->scrollbar && length > w->lines)
 188     {
 189         tty_setcolor (normalc);
 190         listbox_drawscroll (l);
 191     }
 192 }
 193 
 194 /* --------------------------------------------------------------------------------------------- */
 195 
 196 static int
 197 listbox_check_hotkey (WListbox * l, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 198 {
 199     if (!listbox_is_empty (l))
 200     {
 201         int i;
 202         GList *le;
 203 
 204         for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
 205         {
 206             WLEntry *e = LENTRY (le->data);
 207 
 208             if (e->hotkey == key)
 209                 return i;
 210         }
 211     }
 212 
 213     return (-1);
 214 }
 215 
 216 /* --------------------------------------------------------------------------------------------- */
 217 
 218 /* Calculates the item displayed at screen row 'y' (y==0 being the widget's 1st row). */
 219 static int
 220 listbox_y_pos (WListbox * l, int y)
     /* [previous][next][first][last][top][bottom][index][help]  */
 221 {
 222     return MIN (l->top + y, LISTBOX_LAST (l));
 223 }
 224 
 225 /* --------------------------------------------------------------------------------------------- */
 226 
 227 static void
 228 listbox_fwd (WListbox * l, gboolean wrap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 229 {
 230     if (!listbox_is_empty (l))
 231     {
 232         if ((guint) l->pos + 1 < g_queue_get_length (l->list))
 233             listbox_select_entry (l, l->pos + 1);
 234         else if (wrap)
 235             listbox_select_first (l);
 236     }
 237 }
 238 
 239 /* --------------------------------------------------------------------------------------------- */
 240 
 241 static void
 242 listbox_fwd_n (WListbox * l, int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 243 {
 244     listbox_select_entry (l, MIN (l->pos + n, LISTBOX_LAST (l)));
 245 }
 246 
 247 /* --------------------------------------------------------------------------------------------- */
 248 
 249 static void
 250 listbox_back (WListbox * l, gboolean wrap)
     /* [previous][next][first][last][top][bottom][index][help]  */
 251 {
 252     if (!listbox_is_empty (l))
 253     {
 254         if (l->pos > 0)
 255             listbox_select_entry (l, l->pos - 1);
 256         else if (wrap)
 257             listbox_select_last (l);
 258     }
 259 }
 260 
 261 /* --------------------------------------------------------------------------------------------- */
 262 
 263 static void
 264 listbox_back_n (WListbox * l, int n)
     /* [previous][next][first][last][top][bottom][index][help]  */
 265 {
 266     listbox_select_entry (l, MAX (l->pos - n, 0));
 267 }
 268 
 269 /* --------------------------------------------------------------------------------------------- */
 270 
 271 static cb_ret_t
 272 listbox_execute_cmd (WListbox * l, long command)
     /* [previous][next][first][last][top][bottom][index][help]  */
 273 {
 274     cb_ret_t ret = MSG_HANDLED;
 275     const WRect *w = &CONST_WIDGET (l)->rect;
 276 
 277     if (l->list == NULL || g_queue_is_empty (l->list))
 278         return MSG_NOT_HANDLED;
 279 
 280     switch (command)
 281     {
 282     case CK_Up:
 283         listbox_back (l, TRUE);
 284         break;
 285     case CK_Down:
 286         listbox_fwd (l, TRUE);
 287         break;
 288     case CK_Top:
 289         listbox_select_first (l);
 290         break;
 291     case CK_Bottom:
 292         listbox_select_last (l);
 293         break;
 294     case CK_PageUp:
 295         listbox_back_n (l, w->lines - 1);
 296         break;
 297     case CK_PageDown:
 298         listbox_fwd_n (l, w->lines - 1);
 299         break;
 300     case CK_Delete:
 301         if (l->deletable)
 302         {
 303             gboolean is_last, is_more;
 304             int length;
 305 
 306             length = g_queue_get_length (l->list);
 307 
 308             is_last = (l->pos + 1 >= length);
 309             is_more = (l->top + w->lines >= length);
 310 
 311             listbox_remove_current (l);
 312             if ((l->top > 0) && (is_last || is_more))
 313                 l->top--;
 314         }
 315         break;
 316     case CK_Clear:
 317         if (l->deletable && mc_global.widget.confirm_history_cleanup
 318             /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
 319             && (query_dialog (Q_ ("DialogTitle|History cleanup"),
 320                               _("Do you want clean this history?"),
 321                               D_ERROR, 2, _("&Yes"), _("&No")) == 0))
 322             listbox_remove_list (l);
 323         break;
 324     case CK_View:
 325     case CK_Edit:
 326     case CK_Enter:
 327         ret = send_message (WIDGET (l)->owner, l, MSG_NOTIFY, command, NULL);
 328         break;
 329     default:
 330         ret = MSG_NOT_HANDLED;
 331     }
 332 
 333     return ret;
 334 }
 335 
 336 /* --------------------------------------------------------------------------------------------- */
 337 
 338 /* Return MSG_HANDLED if we want a redraw */
 339 static cb_ret_t
 340 listbox_key (WListbox * l, int key)
     /* [previous][next][first][last][top][bottom][index][help]  */
 341 {
 342     long command;
 343 
 344     if (l->list == NULL)
 345         return MSG_NOT_HANDLED;
 346 
 347     /* focus on listbox item N by '0'..'9' keys */
 348     if (key >= '0' && key <= '9')
 349     {
 350         listbox_select_entry (l, key - '0');
 351         return MSG_HANDLED;
 352     }
 353 
 354     command = widget_lookup_key (WIDGET (l), key);
 355     if (command == CK_IgnoreKey)
 356         return MSG_NOT_HANDLED;
 357     return listbox_execute_cmd (l, command);
 358 }
 359 
 360 /* --------------------------------------------------------------------------------------------- */
 361 
 362 /* Listbox item adding function */
 363 static inline void
 364 listbox_append_item (WListbox * l, WLEntry * e, listbox_append_t pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 365 {
 366     if (l->list == NULL)
 367     {
 368         l->list = g_queue_new ();
 369         pos = LISTBOX_APPEND_AT_END;
 370     }
 371 
 372     switch (pos)
 373     {
 374     case LISTBOX_APPEND_AT_END:
 375         g_queue_push_tail (l->list, e);
 376         break;
 377 
 378     case LISTBOX_APPEND_BEFORE:
 379         g_queue_insert_before (l->list, g_queue_peek_nth_link (l->list, (guint) l->pos), e);
 380         break;
 381 
 382     case LISTBOX_APPEND_AFTER:
 383         g_queue_insert_after (l->list, g_queue_peek_nth_link (l->list, (guint) l->pos), e);
 384         break;
 385 
 386     case LISTBOX_APPEND_SORTED:
 387         g_queue_insert_sorted (l->list, e, (GCompareDataFunc) listbox_entry_cmp, NULL);
 388         break;
 389 
 390     default:
 391         break;
 392     }
 393 }
 394 
 395 /* --------------------------------------------------------------------------------------------- */
 396 
 397 /* Call this whenever the user changes the selected item. */
 398 static void
 399 listbox_on_change (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 400 {
 401     listbox_draw (l, TRUE);
 402     send_message (WIDGET (l)->owner, l, MSG_NOTIFY, 0, NULL);
 403 }
 404 
 405 /* --------------------------------------------------------------------------------------------- */
 406 
 407 static void
 408 listbox_do_action (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 409 {
 410     int action;
 411 
 412     if (listbox_is_empty (l))
 413         return;
 414 
 415     if (l->callback != NULL)
 416         action = l->callback (l);
 417     else
 418         action = LISTBOX_DONE;
 419 
 420     if (action == LISTBOX_DONE)
 421     {
 422         WDialog *h = DIALOG (WIDGET (l)->owner);
 423 
 424         h->ret_value = B_ENTER;
 425         dlg_stop (h);
 426     }
 427 }
 428 
 429 /* --------------------------------------------------------------------------------------------- */
 430 
 431 static void
 432 listbox_run_hotkey (WListbox * l, int pos)
     /* [previous][next][first][last][top][bottom][index][help]  */
 433 {
 434     listbox_select_entry (l, pos);
 435     listbox_on_change (l);
 436     listbox_do_action (l);
 437 }
 438 
 439 /* --------------------------------------------------------------------------------------------- */
 440 
 441 static inline void
 442 listbox_destroy (WListbox * l)
     /* [previous][next][first][last][top][bottom][index][help]  */
 443 {
 444     listbox_remove_list (l);
 445 }
 446 
 447 /* --------------------------------------------------------------------------------------------- */
 448 
 449 static cb_ret_t
 450 listbox_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
     /* [previous][next][first][last][top][bottom][index][help]  */
 451 {
 452     WListbox *l = LISTBOX (w);
 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         {
 471             cb_ret_t ret_code;
 472 
 473             ret_code = listbox_key (l, parm);
 474             if (ret_code != MSG_NOT_HANDLED)
 475                 listbox_on_change (l);
 476             return ret_code;
 477         }
 478 
 479     case MSG_ACTION:
 480         return listbox_execute_cmd (l, parm);
 481 
 482     case MSG_CURSOR:
 483         widget_gotoyx (l, l->cursor_y, 0);
 484         return MSG_HANDLED;
 485 
 486     case MSG_DRAW:
 487         listbox_draw (l, widget_get_state (w, WST_FOCUSED));
 488         return MSG_HANDLED;
 489 
 490     case MSG_DESTROY:
 491         listbox_destroy (l);
 492         return MSG_HANDLED;
 493 
 494     default:
 495         return widget_default_callback (w, sender, msg, parm, data);
 496     }
 497 }
 498 
 499 /* --------------------------------------------------------------------------------------------- */
 500 
 501 static void
 502 listbox_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event)
     /* [previous][next][first][last][top][bottom][index][help]  */
 503 {
 504     WListbox *l = LISTBOX (w);
 505     int old_pos;
 506 
 507     old_pos = l->pos;
 508 
 509     switch (msg)
 510     {
 511     case MSG_MOUSE_DOWN:
 512         widget_select (w);
 513         listbox_select_entry (l, listbox_y_pos (l, event->y));
 514         break;
 515 
 516     case MSG_MOUSE_SCROLL_UP:
 517         listbox_back (l, FALSE);
 518         break;
 519 
 520     case MSG_MOUSE_SCROLL_DOWN:
 521         listbox_fwd (l, FALSE);
 522         break;
 523 
 524     case MSG_MOUSE_DRAG:
 525         event->result.repeat = TRUE;    /* It'd be functional even without this. */
 526         listbox_select_entry (l, listbox_y_pos (l, event->y));
 527         break;
 528 
 529     case MSG_MOUSE_CLICK:
 530         /* We don't call listbox_select_entry() here: MSG_MOUSE_DOWN/DRAG did this already. */
 531         if (event->count == GPM_DOUBLE) /* Double click */
 532             listbox_do_action (l);
 533         break;
 534 
 535     default:
 536         break;
 537     }
 538 
 539     /* If the selection has changed, we redraw the widget and notify the dialog. */
 540     if (l->pos != old_pos)
 541         listbox_on_change (l);
 542 }
 543 
 544 /* --------------------------------------------------------------------------------------------- */
 545 /*** public functions ****************************************************************************/
 546 /* --------------------------------------------------------------------------------------------- */
 547 
 548 WListbox *
 549 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback)
     /* [previous][next][first][last][top][bottom][index][help]  */
 550 {
 551     WRect r = { y, x, 1, width };
 552     WListbox *l;
 553     Widget *w;
 554 
 555     l = g_new (WListbox, 1);
 556     w = WIDGET (l);
 557     r.lines = height > 0 ? height : 1;
 558     widget_init (w, &r, 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)->rect.lines;
 638     int length;
 639 
 640     length = listbox_get_length (l);
 641 
 642     l->pos = DOZ (length, 1);
 643     l->top = DOZ (length, lines);
 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)->rect.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]  */