quick & dirty file list sorting
[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
61 void tui_add_list_item(struct tui_widget *w, const char *text)
62 {
63         char *str;
64         struct tui_list *wl = (struct tui_list*)w;
65         assert(wl->type == TUI_LIST);
66         str = strdup_nf(text);
67         darr_push(wl->entries, &str);
68         wl->dirty = 1;
69 }
70
71 int tui_num_list_items(struct tui_widget *w)
72 {
73         struct tui_list *wl = (struct tui_list*)w;
74         assert(wl->type == TUI_LIST);
75         return darr_size(wl->entries);
76 }
77
78 int tui_list_select(struct tui_widget *w, int idx)
79 {
80         int offs, nelem;
81         struct tui_list *wl = (struct tui_list*)w;
82         assert(wl->type == TUI_LIST);
83
84         if(idx == wl->sel) {
85                 return 0;       /* no change */
86         }
87
88         if(idx < 0) {
89                 wl->sel = -1;
90                 return 0;
91         }
92         if(idx >= (nelem = darr_size(wl->entries))) {
93                 return -1;
94         }
95         wl->sel = idx;
96
97         if(idx < wl->view_offs || idx >= wl->view_offs + wl->height) {
98                 offs = idx - wl->height / 2;
99                 if(offs + wl->height >= nelem) {
100                         offs = nelem - wl->height;
101                 }
102                 if(offs < 0) {
103                         offs = 0;
104                 }
105                 wl->view_offs = offs;
106         }
107
108         wl->dirty = 1;
109         return 0;
110 }
111
112 int tui_get_list_sel(struct tui_widget *w)
113 {
114         struct tui_list *wl = (struct tui_list*)w;
115         assert(wl->type == TUI_LIST);
116         return wl->sel;
117 }
118
119 int tui_list_sel_next(struct tui_widget *w)
120 {
121         int nelem;
122         struct tui_list *wl = (struct tui_list*)w;
123         assert(wl->type == TUI_LIST);
124
125         nelem = darr_size(wl->entries);
126
127         if(wl->sel + 1 >= nelem) {
128                 return -1;
129         }
130
131         if(++wl->sel - wl->view_offs >= wl->height) {
132                 wl->view_offs = wl->sel - wl->height;
133         }
134         wl->dirty = 1;
135         return 0;
136 }
137
138 int tui_list_sel_prev(struct tui_widget *w)
139 {
140         struct tui_list *wl = (struct tui_list*)w;
141         assert(wl->type == TUI_LIST);
142
143         if(wl->sel <= 0) {
144                 return -1;
145         }
146         if(--wl->sel < wl->view_offs) {
147                 wl->view_offs = wl->sel;
148         }
149         wl->dirty = 1;
150         return 0;
151 }
152
153 int tui_list_sel_start(struct tui_widget *w)
154 {
155         struct tui_list *wl = (struct tui_list*)w;
156         assert(wl->type == TUI_LIST);
157
158         wl->sel = 0;
159         wl->view_offs = 0;
160         wl->dirty = 1;
161         return 0;
162 }
163
164 int tui_list_sel_end(struct tui_widget *w)
165 {
166         int nelem;
167         struct tui_list *wl = (struct tui_list*)w;
168         assert(wl->type == TUI_LIST);
169
170         nelem = darr_size(wl->entries);
171
172         wl->sel = nelem - 1;
173         wl->view_offs = nelem - wl->height;
174         if(wl->view_offs < 0) wl->view_offs = 0;
175         wl->dirty = 1;
176         return 0;
177 }
178
179 void tui_sort_list(struct tui_widget *w, int (*cmpfunc)(const void*, const void*))
180 {
181         int nelem;
182         struct tui_list *wl = (struct tui_list*)w;
183         assert(wl->type == TUI_LIST);
184
185         if(!cmpfunc) {
186                 cmpfunc = (int (*)(const void*, const void*))strcmp;
187         }
188
189         nelem = darr_size(wl->entries);
190         qsort(wl->entries, nelem, sizeof *wl->entries, cmpfunc);
191 }
192
193 static void draw_list(struct tui_widget *w, void *cls)
194 {
195         int i, x, y, num, idx;
196         struct tui_list *wl = (struct tui_list*)w;
197
198         tui_wtoscr(w, 0, 0, &x, &y);
199
200         tg_bgcolor(TGFX_BLUE);
201         tg_fgcolor(TGFX_CYAN);
202         tg_rect(wl->title, x, y, wl->width, wl->height, TGFX_FRAME);
203
204         num = darr_size(wl->entries);
205         if(num > wl->height - 2) {
206                 num = wl->height - 2;
207         }
208
209         x++;
210         for(i=0; i<num; i++) {
211                 idx = i + wl->view_offs;
212                 if(idx == wl->sel) {
213                         tg_bgcolor(TGFX_CYAN);
214                         tg_fgcolor(TGFX_BLUE);
215
216                         tg_rect(0, x, ++y, wl->width-2, 1, 0);
217                         tg_text(x, y, "%s", wl->entries[idx]);
218
219                         tg_bgcolor(TGFX_BLUE);
220                         tg_fgcolor(TGFX_CYAN);
221                 } else {
222                         tg_text(x, ++y, "%s", wl->entries[idx]);
223                 }
224         }
225 }