1 /* Widgets for the Midnight Commander
3 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
4 2004, 2005, 2006, 2007, 2009, 2010 Free Software Foundation, Inc.
6 Authors: 1994, 1995 Radek Doulik
7 1994, 1995 Miguel de Icaza
11 2009, 2010 Andrew Borodin
13 This program is free software; you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation; either version 2 of the License, or
16 (at your option) any later version.
18 This program is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
23 You should have received a copy of the GNU General Public License
24 along with this program; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
30 * \brief Source: WListbox widget
37 #include "lib/global.h"
39 #include "lib/tty/tty.h"
40 #include "lib/tty/mouse.h"
42 #include "lib/strutil.h"
43 #include "lib/util.h" /* Q_() */
44 #include "lib/keybind.h" /* global_keymap_t */
45 #include "lib/widget.h"
47 /*** global variables ****************************************************************************/
49 const global_keymap_t
*listbox_map
= NULL
;
51 /*** file scope macro definitions ****************************************************************/
53 /*** file scope type declarations ****************************************************************/
55 /*** file scope variables ************************************************************************/
57 /*** file scope functions ************************************************************************/
60 listbox_entry_cmp (const void *a
, const void *b
)
62 const WLEntry
*ea
= (const WLEntry
*) a
;
63 const WLEntry
*eb
= (const WLEntry
*) b
;
65 return strcmp (ea
->text
, eb
->text
);
68 /* --------------------------------------------------------------------------------------------- */
71 listbox_entry_free (void *data
)
78 /* --------------------------------------------------------------------------------------------- */
81 listbox_drawscroll (WListbox
* l
)
83 const int max_line
= l
->widget
.lines
- 1;
87 /* Are we at the top? */
88 widget_move (&l
->widget
, 0, l
->widget
.cols
);
90 tty_print_one_vline (TRUE
);
94 /* Are we at the bottom? */
95 widget_move (&l
->widget
, max_line
, l
->widget
.cols
);
96 if ((l
->top
+ l
->widget
.lines
== l
->count
) || (l
->widget
.lines
>= l
->count
))
97 tty_print_one_vline (TRUE
);
101 /* Now draw the nice relative pointer */
103 line
= 1 + ((l
->pos
* (l
->widget
.lines
- 2)) / l
->count
);
105 for (i
= 1; i
< max_line
; i
++)
107 widget_move (&l
->widget
, i
, l
->widget
.cols
);
109 tty_print_one_vline (TRUE
);
111 tty_print_char ('*');
115 /* --------------------------------------------------------------------------------------------- */
118 listbox_draw (WListbox
* l
, gboolean focused
)
120 const Dlg_head
*h
= l
->widget
.owner
;
121 const gboolean disabled
= (((Widget
*) l
)->options
& W_DISABLED
) != 0;
122 const int normalc
= disabled
? DISABLED_COLOR
: h
->color
[DLG_COLOR_NORMAL
];
124 disabled
? DISABLED_COLOR
: focused
? h
->color
[DLG_COLOR_HOT_FOCUS
] : h
->
125 color
[DLG_COLOR_FOCUS
];
132 le
= g_list_nth (l
->list
, l
->top
);
133 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
134 pos
= (le
== NULL
) ? 0 : l
->top
;
136 for (i
= 0; i
< l
->widget
.lines
; i
++)
140 /* Display the entry */
141 if (pos
== l
->pos
&& sel_line
== -1)
147 tty_setcolor (normalc
);
149 widget_move (&l
->widget
, i
, 1);
151 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
155 WLEntry
*e
= (WLEntry
*) le
->data
;
157 le
= g_list_next (le
);
161 tty_print_string (str_fit_to_term (text
, l
->widget
.cols
- 2, J_LEFT_FIT
));
164 l
->cursor_y
= sel_line
;
166 if (l
->scrollbar
&& (l
->count
> l
->widget
.lines
))
168 tty_setcolor (normalc
);
169 listbox_drawscroll (l
);
173 /* --------------------------------------------------------------------------------------------- */
176 listbox_check_hotkey (WListbox
* l
, int key
)
181 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
183 WLEntry
*e
= (WLEntry
*) le
->data
;
185 if (e
->hotkey
== key
)
192 /* --------------------------------------------------------------------------------------------- */
194 /* Selects from base the pos element */
196 listbox_select_pos (WListbox
* l
, int base
, int pos
)
198 int last
= l
->count
- 1;
201 base
= min (base
, last
);
206 /* --------------------------------------------------------------------------------------------- */
209 listbox_fwd (WListbox
* l
)
211 if (l
->pos
+ 1 >= l
->count
)
212 listbox_select_first (l
);
214 listbox_select_entry (l
, l
->pos
+ 1);
217 /* --------------------------------------------------------------------------------------------- */
220 listbox_back (WListbox
* l
)
223 listbox_select_last (l
);
225 listbox_select_entry (l
, l
->pos
- 1);
228 /* --------------------------------------------------------------------------------------------- */
231 listbox_execute_cmd (WListbox
* l
, unsigned long command
)
233 cb_ret_t ret
= MSG_HANDLED
;
245 listbox_select_first (l
);
248 listbox_select_last (l
);
251 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
> 0); i
++)
255 for (i
= 0; (i
< l
->widget
.lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
261 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
262 gboolean is_more
= (l
->top
+ l
->widget
.lines
>= l
->count
);
264 listbox_remove_current (l
);
265 if ((l
->top
> 0) && (is_last
|| is_more
))
270 if (l
->deletable
&& mc_global
.widget
.confirm_history_cleanup
271 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
272 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
273 _("Do you want clean this history?"),
274 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
275 listbox_remove_list (l
);
278 ret
= MSG_NOT_HANDLED
;
284 /* --------------------------------------------------------------------------------------------- */
286 /* Return MSG_HANDLED if we want a redraw */
288 listbox_key (WListbox
* l
, int key
)
290 unsigned long command
;
293 return MSG_NOT_HANDLED
;
295 /* focus on listbox item N by '0'..'9' keys */
296 if (key
>= '0' && key
<= '9')
299 listbox_select_entry (l
, key
- '0');
301 /* need scroll to item? */
302 if (abs (oldpos
- l
->pos
) > l
->widget
.lines
)
308 command
= keybind_lookup_keymap_command (listbox_map
, key
);
309 if (command
== CK_IgnoreKey
)
310 return MSG_NOT_HANDLED
;
311 return listbox_execute_cmd (l
, command
);
314 /* --------------------------------------------------------------------------------------------- */
316 /* Listbox item adding function */
318 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
322 case LISTBOX_APPEND_AT_END
:
323 l
->list
= g_list_append (l
->list
, e
);
326 case LISTBOX_APPEND_BEFORE
:
327 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
332 case LISTBOX_APPEND_AFTER
:
333 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
336 case LISTBOX_APPEND_SORTED
:
337 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
347 /* --------------------------------------------------------------------------------------------- */
350 listbox_destroy (WListbox
* l
)
352 listbox_remove_list (l
);
355 /* --------------------------------------------------------------------------------------------- */
358 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
360 WListbox
*l
= (WListbox
*) w
;
361 Dlg_head
*h
= l
->widget
.owner
;
373 pos
= listbox_check_hotkey (l
, parm
);
375 return MSG_NOT_HANDLED
;
377 listbox_select_entry (l
, pos
);
378 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
380 if (l
->callback
!= NULL
)
381 action
= l
->callback (l
);
383 action
= LISTBOX_DONE
;
385 if (action
== LISTBOX_DONE
)
387 h
->ret_value
= B_ENTER
;
395 ret_code
= listbox_key (l
, parm
);
396 if (ret_code
!= MSG_NOT_HANDLED
)
398 listbox_draw (l
, TRUE
);
399 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
404 return listbox_execute_cmd (l
, parm
);
407 widget_move (&l
->widget
, l
->cursor_y
, 0);
408 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
414 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
425 return default_proc (msg
, parm
);
429 /* --------------------------------------------------------------------------------------------- */
432 listbox_event (Gpm_Event
* event
, void *data
)
437 Dlg_head
*h
= l
->widget
.owner
;
440 if (event
->type
& GPM_DOWN
)
441 dlg_select_widget (l
);
446 if (event
->type
& (GPM_DOWN
| GPM_DRAG
))
448 int ret
= MOU_REPEAT
;
450 if (event
->x
< 0 || event
->x
> l
->widget
.cols
)
454 for (i
= -event
->y
; i
>= 0; i
--)
456 else if (event
->y
> l
->widget
.lines
)
457 for (i
= event
->y
- l
->widget
.lines
; i
> 0; i
--)
459 else if (event
->buttons
& GPM_B_UP
)
464 else if (event
->buttons
& GPM_B_DOWN
)
470 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
472 /* We need to refresh ourselves since the dialog manager doesn't */
473 /* know about this event */
474 listbox_draw (l
, TRUE
);
479 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
483 if (event
->x
< 0 || event
->x
>= l
->widget
.cols
484 || event
->y
< 1 || event
->y
> l
->widget
.lines
)
487 dlg_select_widget (l
);
488 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, event
->y
- 1));
490 if (l
->callback
!= NULL
)
491 action
= l
->callback (l
);
493 action
= LISTBOX_DONE
;
495 if (action
== LISTBOX_DONE
)
497 h
->ret_value
= B_ENTER
;
505 /* --------------------------------------------------------------------------------------------- */
506 /*** public functions ****************************************************************************/
507 /* --------------------------------------------------------------------------------------------- */
510 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback_fn callback
)
517 l
= g_new (WListbox
, 1);
518 init_widget (&l
->widget
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
523 l
->deletable
= deletable
;
524 l
->callback
= callback
;
525 l
->allow_duplicates
= TRUE
;
526 l
->scrollbar
= !mc_global
.tty
.slow_terminal
;
527 widget_want_hotkey (l
->widget
, TRUE
);
528 widget_want_cursor (l
->widget
, FALSE
);
533 /* --------------------------------------------------------------------------------------------- */
536 listbox_search_text (WListbox
* l
, const char *text
)
543 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
545 WLEntry
*e
= (WLEntry
*) le
->data
;
547 if (strcmp (e
->text
, text
) == 0)
555 /* --------------------------------------------------------------------------------------------- */
557 /* Selects the first entry and scrolls the list to the top */
559 listbox_select_first (WListbox
* l
)
564 /* --------------------------------------------------------------------------------------------- */
566 /* Selects the last entry and scrolls the list to the bottom */
568 listbox_select_last (WListbox
* l
)
570 l
->pos
= l
->count
- 1;
571 l
->top
= l
->count
> l
->widget
.lines
? l
->count
- l
->widget
.lines
: 0;
574 /* --------------------------------------------------------------------------------------------- */
577 listbox_select_entry (WListbox
* l
, int dest
)
581 gboolean top_seen
= FALSE
;
587 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
597 else if (l
->pos
- l
->top
>= l
->widget
.lines
)
598 l
->top
= l
->pos
- l
->widget
.lines
+ 1;
603 /* If we are unable to find it, set decent values */
607 /* --------------------------------------------------------------------------------------------- */
609 /* Returns the current string text as well as the associated extra data */
611 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
617 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
622 *string
= ok
? e
->text
: NULL
;
625 *extra
= ok
? e
->data
: NULL
;
628 /* --------------------------------------------------------------------------------------------- */
631 listbox_remove_current (WListbox
* l
)
633 if ((l
!= NULL
) && (l
->count
!= 0))
637 current
= g_list_nth (l
->list
, l
->pos
);
638 l
->list
= g_list_remove_link (l
->list
, current
);
639 listbox_entry_free ((WLEntry
*) current
->data
);
640 g_list_free_1 (current
);
645 else if (l
->pos
>= l
->count
)
646 l
->pos
= l
->count
- 1;
650 /* --------------------------------------------------------------------------------------------- */
653 listbox_set_list (WListbox
* l
, GList
* list
)
655 listbox_remove_list (l
);
661 l
->count
= g_list_length (list
);
665 /* --------------------------------------------------------------------------------------------- */
668 listbox_remove_list (WListbox
* l
)
670 if ((l
!= NULL
) && (l
->count
!= 0))
672 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
673 g_list_free (l
->list
);
675 l
->count
= l
->pos
= l
->top
= 0;
679 /* --------------------------------------------------------------------------------------------- */
682 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
689 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
692 entry
= g_new (WLEntry
, 1);
693 entry
->text
= g_strdup (text
);
695 entry
->hotkey
= hotkey
;
697 listbox_append_item (l
, entry
, pos
);
702 /* --------------------------------------------------------------------------------------------- */