This source file includes following definitions.
- listbox_entry_cmp
- listbox_entry_free
- listbox_drawscroll
- listbox_draw
- listbox_check_hotkey
- listbox_y_pos
- listbox_fwd
- listbox_fwd_n
- listbox_back
- listbox_back_n
- listbox_execute_cmd
- listbox_key
- listbox_add_entry
- listbox_on_change
- listbox_do_action
- listbox_run_hotkey
- listbox_destroy
- listbox_callback
- listbox_mouse_callback
- listbox_new
- listbox_search_text
- listbox_search_data
- listbox_select_first
- listbox_select_last
- listbox_set_current
- listbox_get_length
- listbox_get_current
- listbox_get_nth_entry
- listbox_get_first_link
- listbox_remove_current
- listbox_is_empty
- listbox_set_list
- listbox_remove_list
- listbox_add_item
- listbox_add_item_take
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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"
45 #include "lib/widget.h"
46
47
48
49 const global_keymap_t *listbox_map = NULL;
50
51
52
53
54 #define LISTBOX_LAST(l) (listbox_is_empty (l) ? 0 : (int) g_queue_get_length ((l)->list) - 1)
55
56
57
58
59
60
61
62
63
64
65
66 static int
67 listbox_entry_cmp (const void *a, const void *b, void *user_data)
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)
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)
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
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
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
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)
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 : colors[focused ? DLG_COLOR_HOT_FOCUS : DLG_COLOR_FOCUS];
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
160 pos = (le == NULL) ? 0 : l->top;
161
162 for (i = 0; i < w->lines; i++)
163 {
164 const char *text = "";
165
166
167 if (pos == l->current && sel_line == -1)
168 {
169 sel_line = i;
170 tty_setcolor (selc);
171 }
172 else
173 tty_setcolor (normalc);
174
175 widget_gotoyx (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)
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
223 static int
224 listbox_y_pos (WListbox *l, int y)
225 {
226 return MIN (l->top + y, LISTBOX_LAST (l));
227 }
228
229
230
231 static void
232 listbox_fwd (WListbox *l, gboolean wrap)
233 {
234 if (!listbox_is_empty (l))
235 {
236 if ((guint) l->current + 1 < g_queue_get_length (l->list))
237 listbox_set_current (l, l->current + 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)
247 {
248 listbox_set_current (l, MIN (l->current + n, LISTBOX_LAST (l)));
249 }
250
251
252
253 static void
254 listbox_back (WListbox *l, gboolean wrap)
255 {
256 if (!listbox_is_empty (l))
257 {
258 if (l->current > 0)
259 listbox_set_current (l, l->current - 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)
269 {
270 listbox_set_current (l, MAX (l->current - n, 0));
271 }
272
273
274
275 static cb_ret_t
276 listbox_execute_cmd (WListbox *l, long command)
277 {
278 cb_ret_t ret = MSG_HANDLED;
279 const WRect *w = &CONST_WIDGET (l)->rect;
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->current + 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
322 && mc_global.widget.confirm_history_cleanup
323
324 && (query_dialog (Q_ ("DialogTitle|History cleanup"),
325 _ ("Do you want clean this history?"), D_ERROR, 2, _ ("&Yes"),
326 _ ("&No"))
327 == 0))
328 listbox_remove_list (l);
329 break;
330 case CK_View:
331 case CK_Edit:
332 case CK_Enter:
333 ret = send_message (WIDGET (l)->owner, l, MSG_NOTIFY, command, NULL);
334 break;
335 default:
336 ret = MSG_NOT_HANDLED;
337 }
338
339 return ret;
340 }
341
342
343
344
345 static cb_ret_t
346 listbox_key (WListbox *l, int key)
347 {
348 long command;
349
350 if (l->list == NULL)
351 return MSG_NOT_HANDLED;
352
353
354 if (key >= '0' && key <= '9')
355 {
356 listbox_set_current (l, key - '0');
357 return MSG_HANDLED;
358 }
359
360 command = widget_lookup_key (WIDGET (l), key);
361 if (command == CK_IgnoreKey)
362 return MSG_NOT_HANDLED;
363 return listbox_execute_cmd (l, command);
364 }
365
366
367
368
369 static inline void
370 listbox_add_entry (WListbox *l, WLEntry *e, listbox_append_t pos)
371 {
372 if (l->list == NULL)
373 {
374 l->list = g_queue_new ();
375 pos = LISTBOX_APPEND_AT_END;
376 }
377
378 switch (pos)
379 {
380 case LISTBOX_APPEND_AT_END:
381 g_queue_push_tail (l->list, e);
382 break;
383
384 case LISTBOX_APPEND_BEFORE:
385 g_queue_insert_before (l->list, g_queue_peek_nth_link (l->list, (guint) l->current), e);
386 break;
387
388 case LISTBOX_APPEND_AFTER:
389 g_queue_insert_after (l->list, g_queue_peek_nth_link (l->list, (guint) l->current), e);
390 break;
391
392 case LISTBOX_APPEND_SORTED:
393 g_queue_insert_sorted (l->list, e, (GCompareDataFunc) listbox_entry_cmp, NULL);
394 break;
395
396 default:
397 break;
398 }
399 }
400
401
402
403
404 static void
405 listbox_on_change (WListbox *l)
406 {
407 listbox_draw (l, TRUE);
408 send_message (WIDGET (l)->owner, l, MSG_NOTIFY, 0, NULL);
409 }
410
411
412
413 static void
414 listbox_do_action (WListbox *l)
415 {
416 int action;
417
418 if (listbox_is_empty (l))
419 return;
420
421 if (l->callback != NULL)
422 action = l->callback (l);
423 else
424 action = LISTBOX_DONE;
425
426 if (action == LISTBOX_DONE)
427 {
428 WDialog *h = DIALOG (WIDGET (l)->owner);
429
430 h->ret_value = B_ENTER;
431 dlg_close (h);
432 }
433 }
434
435
436
437 static void
438 listbox_run_hotkey (WListbox *l, int pos)
439 {
440 listbox_set_current (l, pos);
441 listbox_on_change (l);
442 listbox_do_action (l);
443 }
444
445
446
447 static inline void
448 listbox_destroy (WListbox *l)
449 {
450 listbox_remove_list (l);
451 }
452
453
454
455 static cb_ret_t
456 listbox_callback (Widget *w, Widget *sender, widget_msg_t msg, int parm, void *data)
457 {
458 WListbox *l = LISTBOX (w);
459
460 switch (msg)
461 {
462 case MSG_HOTKEY:
463 {
464 int pos;
465
466 pos = listbox_check_hotkey (l, parm);
467 if (pos < 0)
468 return MSG_NOT_HANDLED;
469
470 listbox_run_hotkey (l, pos);
471
472 return MSG_HANDLED;
473 }
474
475 case MSG_KEY:
476 {
477 cb_ret_t ret_code;
478
479 ret_code = listbox_key (l, parm);
480 if (ret_code != MSG_NOT_HANDLED)
481 listbox_on_change (l);
482 return ret_code;
483 }
484
485 case MSG_ACTION:
486 return listbox_execute_cmd (l, parm);
487
488 case MSG_CURSOR:
489 widget_gotoyx (l, l->cursor_y, 0);
490 return MSG_HANDLED;
491
492 case MSG_DRAW:
493 listbox_draw (l, widget_get_state (w, WST_FOCUSED));
494 return MSG_HANDLED;
495
496 case MSG_DESTROY:
497 listbox_destroy (l);
498 return MSG_HANDLED;
499
500 default:
501 return widget_default_callback (w, sender, msg, parm, data);
502 }
503 }
504
505
506
507 static void
508 listbox_mouse_callback (Widget *w, mouse_msg_t msg, mouse_event_t *event)
509 {
510 WListbox *l = LISTBOX (w);
511 int old_current;
512
513 old_current = l->current;
514
515 switch (msg)
516 {
517 case MSG_MOUSE_DOWN:
518 widget_select (w);
519 listbox_set_current (l, listbox_y_pos (l, event->y));
520 break;
521
522 case MSG_MOUSE_SCROLL_UP:
523 listbox_back (l, FALSE);
524 break;
525
526 case MSG_MOUSE_SCROLL_DOWN:
527 listbox_fwd (l, FALSE);
528 break;
529
530 case MSG_MOUSE_DRAG:
531 event->result.repeat = TRUE;
532 listbox_set_current (l, listbox_y_pos (l, event->y));
533 break;
534
535 case MSG_MOUSE_CLICK:
536
537 if (event->count == GPM_DOUBLE)
538 listbox_do_action (l);
539 break;
540
541 default:
542 break;
543 }
544
545
546 if (l->current != old_current)
547 listbox_on_change (l);
548 }
549
550
551
552
553
554 WListbox *
555 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback)
556 {
557 WRect r = { y, x, 1, width };
558 WListbox *l;
559 Widget *w;
560
561 l = g_new (WListbox, 1);
562 w = WIDGET (l);
563 r.lines = height > 0 ? height : 1;
564 widget_init (w, &r, listbox_callback, listbox_mouse_callback);
565 w->options |= WOP_SELECTABLE | WOP_WANT_HOTKEY;
566 w->keymap = listbox_map;
567
568 l->list = NULL;
569 l->top = l->current = 0;
570 l->deletable = deletable;
571 l->callback = callback;
572 l->allow_duplicates = TRUE;
573 l->scrollbar = !mc_global.tty.slow_terminal;
574
575 return l;
576 }
577
578
579
580
581
582
583 int
584 listbox_search_text (WListbox *l, const char *text)
585 {
586 if (!listbox_is_empty (l))
587 {
588 int i;
589 GList *le;
590
591 for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
592 {
593 WLEntry *e = LENTRY (le->data);
594
595 if (strcmp (e->text, text) == 0)
596 return i;
597 }
598 }
599
600 return (-1);
601 }
602
603
604
605
606
607
608 int
609 listbox_search_data (WListbox *l, const void *data)
610 {
611 if (!listbox_is_empty (l))
612 {
613 int i;
614 GList *le;
615
616 for (i = 0, le = g_queue_peek_head_link (l->list); le != NULL; i++, le = g_list_next (le))
617 {
618 WLEntry *e = LENTRY (le->data);
619
620 if (e->data == data)
621 return i;
622 }
623 }
624
625 return (-1);
626 }
627
628
629
630
631 void
632 listbox_select_first (WListbox *l)
633 {
634 l->current = l->top = 0;
635 }
636
637
638
639
640 void
641 listbox_select_last (WListbox *l)
642 {
643 int lines = WIDGET (l)->rect.lines;
644 int length;
645
646 length = listbox_get_length (l);
647
648 l->current = DOZ (length, 1);
649 l->top = DOZ (length, lines);
650 }
651
652
653
654 void
655 listbox_set_current (WListbox *l, int dest)
656 {
657 GList *le;
658 int pos;
659 gboolean top_seen = FALSE;
660
661 if (listbox_is_empty (l) || dest < 0)
662 return;
663
664
665 for (pos = 0, le = g_queue_peek_head_link (l->list); le != NULL; pos++, le = g_list_next (le))
666 {
667 if (pos == l->top)
668 top_seen = TRUE;
669
670 if (pos == dest)
671 {
672 l->current = dest;
673 if (!top_seen)
674 l->top = l->current;
675 else
676 {
677 int lines = WIDGET (l)->rect.lines;
678
679 if (l->current - l->top >= lines)
680 l->top = l->current - lines + 1;
681 }
682 return;
683 }
684 }
685
686
687 l->current = l->top = 0;
688 }
689
690
691
692 int
693 listbox_get_length (const WListbox *l)
694 {
695 return listbox_is_empty (l) ? 0 : (int) g_queue_get_length (l->list);
696 }
697
698
699
700
701 void
702 listbox_get_current (WListbox *l, char **string, void **extra)
703 {
704 WLEntry *e = NULL;
705 gboolean ok;
706
707 if (l != NULL)
708 e = listbox_get_nth_entry (l, l->current);
709
710 ok = (e != NULL);
711
712 if (string != NULL)
713 *string = ok ? e->text : NULL;
714
715 if (extra != NULL)
716 *extra = ok ? e->data : NULL;
717 }
718
719
720
721 WLEntry *
722 listbox_get_nth_entry (const WListbox *l, int pos)
723 {
724 if (!listbox_is_empty (l) && pos >= 0)
725 {
726 GList *item;
727
728 item = g_queue_peek_nth_link (l->list, (guint) pos);
729 if (item != NULL)
730 return LENTRY (item->data);
731 }
732
733 return NULL;
734 }
735
736
737
738 GList *
739 listbox_get_first_link (const WListbox *l)
740 {
741 return (l == NULL || l->list == NULL) ? NULL : g_queue_peek_head_link (l->list);
742 }
743
744
745
746 void
747 listbox_remove_current (WListbox *l)
748 {
749 if (!listbox_is_empty (l))
750 {
751 GList *current;
752 int length;
753
754 current = g_queue_peek_nth_link (l->list, (guint) l->current);
755 listbox_entry_free (current->data);
756 g_queue_delete_link (l->list, current);
757
758 length = g_queue_get_length (l->list);
759
760 if (length == 0)
761 l->top = l->current = 0;
762 else if (l->current >= length)
763 l->current = length - 1;
764 }
765 }
766
767
768
769 gboolean
770 listbox_is_empty (const WListbox *l)
771 {
772 return (l == NULL || l->list == NULL || g_queue_is_empty (l->list));
773 }
774
775
776
777
778
779
780
781
782
783 void
784 listbox_set_list (WListbox *l, GQueue *list)
785 {
786 listbox_remove_list (l);
787
788 if (l != NULL)
789 l->list = list;
790 }
791
792
793
794 void
795 listbox_remove_list (WListbox *l)
796 {
797 if (l != NULL)
798 {
799 if (l->list != NULL)
800 {
801 g_queue_free_full (l->list, (GDestroyNotify) listbox_entry_free);
802 l->list = NULL;
803 }
804
805 l->current = l->top = 0;
806 }
807 }
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823 char *
824 listbox_add_item (WListbox *l, listbox_append_t pos, int hotkey, const char *text, void *data,
825 gboolean free_data)
826 {
827 return listbox_add_item_take (l, pos, hotkey, g_strdup (text), data, free_data);
828 }
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846 char *
847 listbox_add_item_take (WListbox *l, listbox_append_t pos, int hotkey, char *text, void *data,
848 gboolean free_data)
849 {
850 WLEntry *entry;
851
852 if (l == NULL)
853 return NULL;
854
855 if (!l->allow_duplicates && (listbox_search_text (l, text) >= 0))
856 return NULL;
857
858 entry = g_new (WLEntry, 1);
859 entry->text = text;
860 entry->data = data;
861 entry->free_data = free_data;
862 entry->hotkey = hotkey;
863
864 listbox_add_entry (l, entry, pos);
865
866 return entry->text;
867 }
868
869