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