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
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
161 pos = (le == NULL) ? 0 : l->top;
162
163 for (i = 0; i < w->lines; i++)
164 {
165 const char *text = "";
166
167
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)
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
224 static int
225 listbox_y_pos (WListbox *l, int y)
226 {
227 return MIN (l->top + y, LISTBOX_LAST (l));
228 }
229
230
231
232 static void
233 listbox_fwd (WListbox *l, gboolean wrap)
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)
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)
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)
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)
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
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
346 static cb_ret_t
347 listbox_key (WListbox *l, int key)
348 {
349 long command;
350
351 if (l->list == NULL)
352 return MSG_NOT_HANDLED;
353
354
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
370 static inline void
371 listbox_add_entry (WListbox *l, WLEntry *e, listbox_append_t pos)
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
405 static void
406 listbox_on_change (WListbox *l)
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)
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)
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)
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)
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)
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;
533 listbox_set_current (l, listbox_y_pos (l, event->y));
534 break;
535
536 case MSG_MOUSE_CLICK:
537
538 if (event->count == GPM_DOUBLE)
539 listbox_do_action (l);
540 break;
541
542 default:
543 break;
544 }
545
546
547 if (l->current != old_current)
548 listbox_on_change (l);
549 }
550
551
552
553
554
555 WListbox *
556 listbox_new (int y, int x, int height, int width, gboolean deletable, lcback_fn callback)
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
583
584 int
585 listbox_search_text (WListbox *l, const char *text)
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
608
609 int
610 listbox_search_data (WListbox *l, const void *data)
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
632 void
633 listbox_select_first (WListbox *l)
634 {
635 l->current = l->top = 0;
636 }
637
638
639
640
641 void
642 listbox_select_last (WListbox *l)
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)
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
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
688 l->current = l->top = 0;
689 }
690
691
692
693 int
694 listbox_get_length (const WListbox *l)
695 {
696 return listbox_is_empty (l) ? 0 : (int) g_queue_get_length (l->list);
697 }
698
699
700
701
702 void
703 listbox_get_current (WListbox *l, char **string, void **extra)
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)
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)
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)
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)
772 {
773 return (l == NULL || l->list == NULL || g_queue_is_empty (l->list));
774 }
775
776
777
778
779
780
781
782
783
784 void
785 listbox_set_list (WListbox *l, GQueue *list)
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)
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
814
815
816
817
818
819
820
821
822
823
824 char *
825 listbox_add_item (WListbox *l, listbox_append_t pos, int hotkey, const char *text, void *data,
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
835
836
837
838
839
840
841
842
843
844
845
846
847 char *
848 listbox_add_item_take (WListbox *l, listbox_append_t pos, int hotkey, char *text, void *data,
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