2 Widgets for the Midnight Commander
4 Copyright (C) 1994, 1995, 1996, 1998, 1999, 2000, 2001, 2002, 2003,
5 2004, 2005, 2006, 2007, 2009, 2010, 2011
6 The Free Software Foundation, Inc.
9 Radek Doulik, 1994, 1995
10 Miguel de Icaza, 1994, 1995
12 Andrej Borsenkow, 1996
14 Andrew Borodin <aborodin@vmail.ru>, 2009, 2010
16 This file is part of the Midnight Commander.
18 The Midnight Commander is free software: you can redistribute it
19 and/or modify it under the terms of the GNU General Public License as
20 published by the Free Software Foundation, either version 3 of the License,
21 or (at your option) any later version.
23 The Midnight Commander is distributed in the hope that it will be useful,
24 but WITHOUT ANY WARRANTY; without even the implied warranty of
25 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 GNU General Public License for more details.
28 You should have received a copy of the GNU General Public License
29 along with this program. If not, see <http://www.gnu.org/licenses/>.
33 * \brief Source: WListbox widget
40 #include "lib/global.h"
42 #include "lib/tty/tty.h"
43 #include "lib/tty/mouse.h"
45 #include "lib/strutil.h"
46 #include "lib/util.h" /* Q_() */
47 #include "lib/keybind.h" /* global_keymap_t */
48 #include "lib/widget.h"
50 /*** global variables ****************************************************************************/
52 const global_keymap_t
*listbox_map
= NULL
;
54 /*** file scope macro definitions ****************************************************************/
56 /*** file scope type declarations ****************************************************************/
58 /*** file scope variables ************************************************************************/
60 /*** file scope functions ************************************************************************/
63 listbox_entry_cmp (const void *a
, const void *b
)
65 const WLEntry
*ea
= (const WLEntry
*) a
;
66 const WLEntry
*eb
= (const WLEntry
*) b
;
68 return strcmp (ea
->text
, eb
->text
);
71 /* --------------------------------------------------------------------------------------------- */
74 listbox_entry_free (void *data
)
81 /* --------------------------------------------------------------------------------------------- */
84 listbox_drawscroll (WListbox
* l
)
86 Widget
*w
= WIDGET (l
);
87 int max_line
= w
->lines
- 1;
91 /* Are we at the top? */
92 widget_move (w
, 0, w
->cols
);
94 tty_print_one_vline (TRUE
);
98 /* Are we at the bottom? */
99 widget_move (w
, max_line
, w
->cols
);
100 if ((l
->top
+ w
->lines
== l
->count
) || (w
->lines
>= l
->count
))
101 tty_print_one_vline (TRUE
);
103 tty_print_char ('v');
105 /* Now draw the nice relative pointer */
107 line
= 1 + ((l
->pos
* (w
->lines
- 2)) / l
->count
);
109 for (i
= 1; i
< max_line
; i
++)
111 widget_move (w
, i
, w
->cols
);
113 tty_print_one_vline (TRUE
);
115 tty_print_char ('*');
119 /* --------------------------------------------------------------------------------------------- */
122 listbox_draw (WListbox
* l
, gboolean focused
)
124 Widget
*w
= WIDGET (l
);
125 const Dlg_head
*h
= w
->owner
;
126 const gboolean disabled
= (w
->options
& W_DISABLED
) != 0;
127 const int normalc
= disabled
? DISABLED_COLOR
: h
->color
[DLG_COLOR_NORMAL
];
132 ? h
->color
[DLG_COLOR_HOT_FOCUS
]
133 : h
->color
[DLG_COLOR_FOCUS
];
141 le
= g_list_nth (l
->list
, l
->top
);
142 /* pos = (le == NULL) ? 0 : g_list_position (l->list, le); */
143 pos
= (le
== NULL
) ? 0 : l
->top
;
145 for (i
= 0; i
< w
->lines
; i
++)
149 /* Display the entry */
150 if (pos
== l
->pos
&& sel_line
== -1)
156 tty_setcolor (normalc
);
158 widget_move (l
, i
, 1);
160 if ((i
> 0 && pos
>= l
->count
) || (l
->list
== NULL
) || (le
== NULL
))
164 WLEntry
*e
= (WLEntry
*) le
->data
;
166 le
= g_list_next (le
);
170 tty_print_string (str_fit_to_term (text
, w
->cols
- 2, J_LEFT_FIT
));
173 l
->cursor_y
= sel_line
;
175 if (l
->scrollbar
&& (l
->count
> w
->lines
))
177 tty_setcolor (normalc
);
178 listbox_drawscroll (l
);
182 /* --------------------------------------------------------------------------------------------- */
185 listbox_check_hotkey (WListbox
* l
, int key
)
190 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
192 WLEntry
*e
= (WLEntry
*) le
->data
;
194 if (e
->hotkey
== key
)
201 /* --------------------------------------------------------------------------------------------- */
203 /* Selects from base the pos element */
205 listbox_select_pos (WListbox
* l
, int base
, int pos
)
207 int last
= l
->count
- 1;
210 base
= min (base
, last
);
215 /* --------------------------------------------------------------------------------------------- */
218 listbox_fwd (WListbox
* l
)
220 if (l
->pos
+ 1 >= l
->count
)
221 listbox_select_first (l
);
223 listbox_select_entry (l
, l
->pos
+ 1);
226 /* --------------------------------------------------------------------------------------------- */
229 listbox_back (WListbox
* l
)
232 listbox_select_last (l
);
234 listbox_select_entry (l
, l
->pos
- 1);
237 /* --------------------------------------------------------------------------------------------- */
240 listbox_execute_cmd (WListbox
* l
, unsigned long command
)
242 cb_ret_t ret
= MSG_HANDLED
;
244 Widget
*w
= WIDGET (l
);
255 listbox_select_first (l
);
258 listbox_select_last (l
);
261 for (i
= 0; (i
< w
->lines
- 1) && (l
->pos
> 0); i
++)
265 for (i
= 0; (i
< w
->lines
- 1) && (l
->pos
< l
->count
- 1); i
++)
271 gboolean is_last
= (l
->pos
+ 1 >= l
->count
);
272 gboolean is_more
= (l
->top
+ w
->lines
>= l
->count
);
274 listbox_remove_current (l
);
275 if ((l
->top
> 0) && (is_last
|| is_more
))
280 if (l
->deletable
&& mc_global
.widget
.confirm_history_cleanup
281 /* TRANSLATORS: no need to translate 'DialogTitle', it's just a context prefix */
282 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
283 _("Do you want clean this history?"),
284 D_ERROR
, 2, _("&Yes"), _("&No")) == 0))
285 listbox_remove_list (l
);
288 ret
= MSG_NOT_HANDLED
;
294 /* --------------------------------------------------------------------------------------------- */
296 /* Return MSG_HANDLED if we want a redraw */
298 listbox_key (WListbox
* l
, int key
)
300 unsigned long command
;
303 return MSG_NOT_HANDLED
;
305 /* focus on listbox item N by '0'..'9' keys */
306 if (key
>= '0' && key
<= '9')
309 listbox_select_entry (l
, key
- '0');
311 /* need scroll to item? */
312 if (abs (oldpos
- l
->pos
) > WIDGET (l
)->lines
)
318 command
= keybind_lookup_keymap_command (listbox_map
, key
);
319 if (command
== CK_IgnoreKey
)
320 return MSG_NOT_HANDLED
;
321 return listbox_execute_cmd (l
, command
);
324 /* --------------------------------------------------------------------------------------------- */
326 /* Listbox item adding function */
328 listbox_append_item (WListbox
* l
, WLEntry
* e
, listbox_append_t pos
)
332 case LISTBOX_APPEND_AT_END
:
333 l
->list
= g_list_append (l
->list
, e
);
336 case LISTBOX_APPEND_BEFORE
:
337 l
->list
= g_list_insert_before (l
->list
, g_list_nth (l
->list
, l
->pos
), e
);
342 case LISTBOX_APPEND_AFTER
:
343 l
->list
= g_list_insert (l
->list
, e
, l
->pos
+ 1);
346 case LISTBOX_APPEND_SORTED
:
347 l
->list
= g_list_insert_sorted (l
->list
, e
, (GCompareFunc
) listbox_entry_cmp
);
357 /* --------------------------------------------------------------------------------------------- */
360 listbox_destroy (WListbox
* l
)
362 listbox_remove_list (l
);
365 /* --------------------------------------------------------------------------------------------- */
368 listbox_callback (Widget
* w
, widget_msg_t msg
, int parm
)
370 WListbox
*l
= (WListbox
*) w
;
371 Dlg_head
*h
= w
->owner
;
383 pos
= listbox_check_hotkey (l
, parm
);
385 return MSG_NOT_HANDLED
;
387 listbox_select_entry (l
, pos
);
388 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
390 if (l
->callback
!= NULL
)
391 action
= l
->callback (l
);
393 action
= LISTBOX_DONE
;
395 if (action
== LISTBOX_DONE
)
397 h
->ret_value
= B_ENTER
;
405 ret_code
= listbox_key (l
, parm
);
406 if (ret_code
!= MSG_NOT_HANDLED
)
408 listbox_draw (l
, TRUE
);
409 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
414 return listbox_execute_cmd (l
, parm
);
417 widget_move (l
, l
->cursor_y
, 0);
418 h
->callback (h
, w
, DLG_ACTION
, l
->pos
, NULL
);
424 listbox_draw (l
, msg
!= WIDGET_UNFOCUS
);
435 return default_proc (msg
, parm
);
439 /* --------------------------------------------------------------------------------------------- */
442 listbox_event (Gpm_Event
* event
, void *data
)
444 WListbox
*l
= (WListbox
*) data
;
445 Widget
*w
= WIDGET (data
);
447 if (!mouse_global_in_widget (event
, w
))
448 return MOU_UNHANDLED
;
451 if ((event
->type
& GPM_DOWN
) != 0)
452 dlg_select_widget (l
);
457 if ((event
->type
& (GPM_DOWN
| GPM_DRAG
)) != 0)
459 int ret
= MOU_REPEAT
;
463 local
= mouse_get_local (event
, w
);
465 for (i
= -local
.y
; i
>= 0; i
--)
467 else if (local
.y
> w
->lines
)
468 for (i
= local
.y
- w
->lines
; i
> 0; i
--)
470 else if ((local
.buttons
& GPM_B_UP
) != 0)
475 else if ((local
.buttons
& GPM_B_DOWN
) != 0)
481 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, local
.y
- 1));
483 /* We need to refresh ourselves since the dialog manager doesn't */
484 /* know about this event */
485 listbox_draw (l
, TRUE
);
490 if ((event
->type
& (GPM_DOUBLE
| GPM_UP
)) == (GPM_UP
| GPM_DOUBLE
))
495 local
= mouse_get_local (event
, w
);
496 dlg_select_widget (l
);
497 listbox_select_entry (l
, listbox_select_pos (l
, l
->top
, local
.y
- 1));
499 if (l
->callback
!= NULL
)
500 action
= l
->callback (l
);
502 action
= LISTBOX_DONE
;
504 if (action
== LISTBOX_DONE
)
506 w
->owner
->ret_value
= B_ENTER
;
514 /* --------------------------------------------------------------------------------------------- */
515 /*** public functions ****************************************************************************/
516 /* --------------------------------------------------------------------------------------------- */
519 listbox_new (int y
, int x
, int height
, int width
, gboolean deletable
, lcback_fn callback
)
527 l
= g_new (WListbox
, 1);
529 init_widget (w
, y
, x
, height
, width
, listbox_callback
, listbox_event
);
534 l
->deletable
= deletable
;
535 l
->callback
= callback
;
536 l
->allow_duplicates
= TRUE
;
537 l
->scrollbar
= !mc_global
.tty
.slow_terminal
;
538 widget_want_hotkey (w
, TRUE
);
539 widget_want_cursor (w
, FALSE
);
544 /* --------------------------------------------------------------------------------------------- */
547 listbox_search_text (WListbox
* l
, const char *text
)
554 for (i
= 0, le
= l
->list
; le
!= NULL
; i
++, le
= g_list_next (le
))
556 WLEntry
*e
= (WLEntry
*) le
->data
;
558 if (strcmp (e
->text
, text
) == 0)
566 /* --------------------------------------------------------------------------------------------- */
568 /* Selects the first entry and scrolls the list to the top */
570 listbox_select_first (WListbox
* l
)
575 /* --------------------------------------------------------------------------------------------- */
577 /* Selects the last entry and scrolls the list to the bottom */
579 listbox_select_last (WListbox
* l
)
581 int lines
= WIDGET (l
)->lines
;
583 l
->pos
= l
->count
- 1;
584 l
->top
= l
->count
> lines
? l
->count
- lines
: 0;
587 /* --------------------------------------------------------------------------------------------- */
590 listbox_select_entry (WListbox
* l
, int dest
)
594 gboolean top_seen
= FALSE
;
600 for (pos
= 0, le
= l
->list
; le
!= NULL
; pos
++, le
= g_list_next (le
))
612 int lines
= WIDGET (l
)->lines
;
614 if (l
->pos
- l
->top
>= lines
)
615 l
->top
= l
->pos
- lines
+ 1;
621 /* If we are unable to find it, set decent values */
625 /* --------------------------------------------------------------------------------------------- */
627 /* Returns the current string text as well as the associated extra data */
629 listbox_get_current (WListbox
* l
, char **string
, void **extra
)
635 e
= (WLEntry
*) g_list_nth_data (l
->list
, l
->pos
);
640 *string
= ok
? e
->text
: NULL
;
643 *extra
= ok
? e
->data
: NULL
;
646 /* --------------------------------------------------------------------------------------------- */
649 listbox_remove_current (WListbox
* l
)
651 if ((l
!= NULL
) && (l
->count
!= 0))
655 current
= g_list_nth (l
->list
, l
->pos
);
656 l
->list
= g_list_remove_link (l
->list
, current
);
657 listbox_entry_free ((WLEntry
*) current
->data
);
658 g_list_free_1 (current
);
663 else if (l
->pos
>= l
->count
)
664 l
->pos
= l
->count
- 1;
668 /* --------------------------------------------------------------------------------------------- */
671 listbox_set_list (WListbox
* l
, GList
* list
)
673 listbox_remove_list (l
);
679 l
->count
= g_list_length (list
);
683 /* --------------------------------------------------------------------------------------------- */
686 listbox_remove_list (WListbox
* l
)
688 if ((l
!= NULL
) && (l
->count
!= 0))
690 g_list_foreach (l
->list
, (GFunc
) listbox_entry_free
, NULL
);
691 g_list_free (l
->list
);
693 l
->count
= l
->pos
= l
->top
= 0;
697 /* --------------------------------------------------------------------------------------------- */
700 listbox_add_item (WListbox
* l
, listbox_append_t pos
, int hotkey
, const char *text
, void *data
)
707 if (!l
->allow_duplicates
&& (listbox_search_text (l
, text
) >= 0))
710 entry
= g_new (WLEntry
, 1);
711 entry
->text
= g_strdup (text
);
713 entry
->hotkey
= hotkey
;
715 listbox_append_item (l
, entry
, pos
);
720 /* --------------------------------------------------------------------------------------------- */