Manual pages: mcmcdiffmceditmcview

root/lib/widget/listbox.c

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

DEFINITIONS

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

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

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