local navigation
[oftp] / src / tui_list.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <assert.h>
4 #include "darray.h"
5 #include "tui.h"
6 #include "tuipriv.h"
7 #include "tgfx.h"
8 #include "util.h"
9
10 static void free_list(struct tui_widget *w, void *cls);
11 static void draw_list(struct tui_widget *w, void *cls);
12
13 struct tui_widget *tui_list(const char *title, int x, int y, int width, int height, tui_callback cbfunc, void *cbdata)
14 {
15         struct tui_list *w;
16
17         if(!(w = calloc(1, sizeof *w))) {
18                 return 0;
19         }
20         w->type = TUI_LIST;
21         w->title = strdup(title);
22         w->x = x;
23         w->y = y;
24         w->width = width;
25         w->height = height;
26
27         w->entries = darr_alloc(0, sizeof *w->entries);
28         w->sel = -1;
29         w->dirty = 1;
30
31         if(cbfunc) {
32                 tui_set_callback((struct tui_widget*)w, TUI_ONMODIFY, cbfunc, cbdata);
33         }
34         tui_set_callback((struct tui_widget*)w, TUI_FREE, free_list, 0);
35         tui_set_callback((struct tui_widget*)w, TUI_DRAW, draw_list, 0);
36         return (struct tui_widget*)w;
37 }
38
39 static void free_list(struct tui_widget *w, void *cls)
40 {
41         struct tui_list *wl = (struct tui_list*)w;
42
43         tui_clear_list(w);
44         darr_free(wl->entries);
45 }
46
47
48 void tui_clear_list(struct tui_widget *w)
49 {
50         int i;
51         struct tui_list *wl = (struct tui_list*)w;
52         assert(wl->type == TUI_LIST);
53
54         for(i=0; i<darr_size(wl->entries); i++) {
55                 free(wl->entries[i]);
56         }
57         darr_clear(wl->entries);
58         wl->dirty = 1;
59
60         tui_call_callback(w, TUI_ONMODIFY);
61 }
62
63 void tui_add_list_item(struct tui_widget *w, const char *text)
64 {
65         char *str;
66         struct tui_list *wl = (struct tui_list*)w;
67         assert(wl->type == TUI_LIST);
68         str = strdup_nf(text);
69         darr_push(wl->entries, &str);
70         wl->dirty = 1;
71
72         tui_call_callback(w, TUI_ONMODIFY);
73 }
74
75 int tui_num_list_items(struct tui_widget *w)
76 {
77         struct tui_list *wl = (struct tui_list*)w;
78         assert(wl->type == TUI_LIST);
79         return darr_size(wl->entries);
80 }
81
82 int tui_list_select(struct tui_widget *w, int idx)
83 {
84         int offs, nelem;
85         struct tui_list *wl = (struct tui_list*)w;
86         assert(wl->type == TUI_LIST);
87
88         if(idx == wl->sel) {
89                 return 0;       /* no change */
90         }
91
92         if(idx < 0) {
93                 wl->sel = -1;
94                 return 0;
95         }
96         if(idx >= (nelem = darr_size(wl->entries))) {
97                 return -1;
98         }
99         wl->sel = idx;
100
101         if(idx < wl->view_offs || idx >= wl->view_offs + wl->height) {
102                 offs = idx - wl->height / 2;
103                 if(offs + wl->height >= nelem) {
104                         offs = nelem - wl->height;
105                 }
106                 if(offs < 0) {
107                         offs = 0;
108                 }
109                 wl->view_offs = offs;
110         }
111
112         wl->dirty = 1;
113         tui_call_callback(w, TUI_ONMODIFY);
114         return 0;
115 }
116
117 int tui_get_list_sel(struct tui_widget *w)
118 {
119         struct tui_list *wl = (struct tui_list*)w;
120         assert(wl->type == TUI_LIST);
121         return wl->sel;
122 }
123
124 int tui_list_sel_next(struct tui_widget *w)
125 {
126         int nelem;
127         struct tui_list *wl = (struct tui_list*)w;
128         assert(wl->type == TUI_LIST);
129
130         nelem = darr_size(wl->entries);
131
132         if(wl->sel + 1 >= nelem) {
133                 return -1;
134         }
135
136         if(++wl->sel - wl->view_offs >= wl->height) {
137                 wl->view_offs = wl->sel - wl->height;
138         }
139         wl->dirty = 1;
140         tui_call_callback(w, TUI_ONMODIFY);
141         return 0;
142 }
143
144 int tui_list_sel_prev(struct tui_widget *w)
145 {
146         struct tui_list *wl = (struct tui_list*)w;
147         assert(wl->type == TUI_LIST);
148
149         if(wl->sel <= 0) {
150                 return -1;
151         }
152         if(--wl->sel < wl->view_offs) {
153                 wl->view_offs = wl->sel;
154         }
155         wl->dirty = 1;
156         tui_call_callback(w, TUI_ONMODIFY);
157         return 0;
158 }
159
160 int tui_list_sel_start(struct tui_widget *w)
161 {
162         struct tui_list *wl = (struct tui_list*)w;
163         assert(wl->type == TUI_LIST);
164
165         wl->sel = 0;
166         wl->view_offs = 0;
167         wl->dirty = 1;
168         tui_call_callback(w, TUI_ONMODIFY);
169         return 0;
170 }
171
172 int tui_list_sel_end(struct tui_widget *w)
173 {
174         int nelem;
175         struct tui_list *wl = (struct tui_list*)w;
176         assert(wl->type == TUI_LIST);
177
178         nelem = darr_size(wl->entries);
179
180         wl->sel = nelem - 1;
181         wl->view_offs = nelem - wl->height;
182         if(wl->view_offs < 0) wl->view_offs = 0;
183         wl->dirty = 1;
184         tui_call_callback(w, TUI_ONMODIFY);
185         return 0;
186 }
187
188 void tui_sort_list(struct tui_widget *w, int (*cmpfunc)(const void*, const void*))
189 {
190         int nelem;
191         struct tui_list *wl = (struct tui_list*)w;
192         assert(wl->type == TUI_LIST);
193
194         if(!cmpfunc) {
195                 cmpfunc = (int (*)(const void*, const void*))strcmp;
196         }
197
198         nelem = darr_size(wl->entries);
199         qsort(wl->entries, nelem, sizeof *wl->entries, cmpfunc);
200
201         wl->dirty = 1;
202         tui_call_callback(w, TUI_ONMODIFY);
203 }
204
205 static void draw_list(struct tui_widget *w, void *cls)
206 {
207         int i, x, y, num, idx;
208         struct tui_list *wl = (struct tui_list*)w;
209
210         tui_wtoscr(w, 0, 0, &x, &y);
211
212         tg_bgcolor(TGFX_BLUE);
213         tg_fgcolor(TGFX_CYAN);
214         tg_rect(wl->title, x, y, wl->width, wl->height, TGFX_FRAME);
215
216         num = darr_size(wl->entries);
217         if(num > wl->height - 2) {
218                 num = wl->height - 2;
219         }
220
221         x++;
222         for(i=0; i<num; i++) {
223                 idx = i + wl->view_offs;
224                 if(w->focus && idx == wl->sel) {
225                         tg_bgcolor(TGFX_CYAN);
226                         tg_fgcolor(TGFX_BLUE);
227
228                         tg_rect(0, x, ++y, wl->width-2, 1, 0);
229                         tg_text(x, y, "%s", wl->entries[idx]);
230
231                         tg_bgcolor(TGFX_BLUE);
232                         tg_fgcolor(TGFX_CYAN);
233                 } else {
234                         tg_text(x, ++y, "%s", wl->entries[idx]);
235                 }
236         }
237 }