download, progress, improved screen updates...
[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 #define VISLINES(wl)    ((wl)->height - 2)
83
84 int tui_list_select(struct tui_widget *w, int idx)
85 {
86         int offs, nelem, numvis;
87         struct tui_list *wl = (struct tui_list*)w;
88         assert(wl->type == TUI_LIST);
89
90         if(idx == wl->sel) {
91                 return 0;       /* no change */
92         }
93
94         numvis = VISLINES(wl);
95
96         if(idx < 0) {
97                 wl->sel = -1;
98                 return 0;
99         }
100         if(idx >= (nelem = darr_size(wl->entries))) {
101                 return -1;
102         }
103         wl->sel = idx;
104
105         if(idx < wl->view_offs || idx >= wl->view_offs + numvis) {
106                 offs = idx - numvis / 2;
107                 if(offs + numvis >= nelem) {
108                         offs = nelem - numvis;
109                 }
110                 if(offs < 0) {
111                         offs = 0;
112                 }
113                 wl->view_offs = offs;
114         }
115
116         wl->dirty = 1;
117         tui_call_callback(w, TUI_ONMODIFY);
118         return 0;
119 }
120
121 int tui_get_list_sel(struct tui_widget *w)
122 {
123         struct tui_list *wl = (struct tui_list*)w;
124         assert(wl->type == TUI_LIST);
125         return wl->sel;
126 }
127
128 int tui_list_sel_next(struct tui_widget *w)
129 {
130         int nelem, numvis;
131         struct tui_list *wl = (struct tui_list*)w;
132         assert(wl->type == TUI_LIST);
133
134         nelem = darr_size(wl->entries);
135
136         numvis = VISLINES(wl);
137
138         if(wl->sel + 1 >= nelem) {
139                 return -1;
140         }
141
142         if(++wl->sel - wl->view_offs >= numvis) {
143                 wl->view_offs = wl->sel - numvis + 1;
144         }
145         wl->dirty = 1;
146         tui_call_callback(w, TUI_ONMODIFY);
147         return 0;
148 }
149
150 int tui_list_sel_prev(struct tui_widget *w)
151 {
152         struct tui_list *wl = (struct tui_list*)w;
153         assert(wl->type == TUI_LIST);
154
155         if(wl->sel <= 0) {
156                 return -1;
157         }
158         if(--wl->sel < wl->view_offs) {
159                 wl->view_offs = wl->sel;
160         }
161         wl->dirty = 1;
162         tui_call_callback(w, TUI_ONMODIFY);
163         return 0;
164 }
165
166 int tui_list_sel_start(struct tui_widget *w)
167 {
168         struct tui_list *wl = (struct tui_list*)w;
169         assert(wl->type == TUI_LIST);
170
171         wl->sel = 0;
172         wl->view_offs = 0;
173         wl->dirty = 1;
174         tui_call_callback(w, TUI_ONMODIFY);
175         return 0;
176 }
177
178 int tui_list_sel_end(struct tui_widget *w)
179 {
180         int nelem, numvis;
181         struct tui_list *wl = (struct tui_list*)w;
182         assert(wl->type == TUI_LIST);
183
184         nelem = darr_size(wl->entries);
185         numvis = VISLINES(wl);
186
187         wl->sel = nelem - 1;
188         wl->view_offs = nelem - numvis;
189         if(wl->view_offs < 0) wl->view_offs = 0;
190         wl->dirty = 1;
191         tui_call_callback(w, TUI_ONMODIFY);
192         return 0;
193 }
194
195 void tui_sort_list(struct tui_widget *w, int (*cmpfunc)(const void*, const void*))
196 {
197         int nelem;
198         struct tui_list *wl = (struct tui_list*)w;
199         assert(wl->type == TUI_LIST);
200
201         if(!cmpfunc) {
202                 cmpfunc = (int (*)(const void*, const void*))strcmp;
203         }
204
205         nelem = darr_size(wl->entries);
206         qsort(wl->entries, nelem, sizeof *wl->entries, cmpfunc);
207
208         wl->dirty = 1;
209         tui_call_callback(w, TUI_ONMODIFY);
210 }
211
212 static void draw_list(struct tui_widget *w, void *cls)
213 {
214         int i, x, y, num, idx;
215         struct tui_list *wl = (struct tui_list*)w;
216
217         tui_wtoscr(w, 0, 0, &x, &y);
218
219         tg_bgcolor(TGFX_BLUE);
220         tg_fgcolor(TGFX_CYAN);
221         tg_rect(wl->title, x, y, wl->width, wl->height, TGFX_FRAME);
222
223         num = darr_size(wl->entries);
224         if(num > wl->height - 2) {
225                 num = wl->height - 2;
226         }
227
228         x++;
229         for(i=0; i<num; i++) {
230                 idx = i + wl->view_offs;
231                 if(w->focus && idx == wl->sel) {
232                         tg_bgcolor(TGFX_CYAN);
233                         tg_fgcolor(TGFX_BLUE);
234
235                         tg_rect(0, x, ++y, wl->width-2, 1, 0);
236                         tg_text(x, y, "%s", wl->entries[idx]);
237
238                         tg_bgcolor(TGFX_BLUE);
239                         tg_fgcolor(TGFX_CYAN);
240                 } else {
241                         tg_text(x, ++y, "%s", wl->entries[idx]);
242                 }
243         }
244 }