Commit | Line | Data |
---|---|---|
8f9bbc40 | 1 | #include "libslang.h" |
5c35d69f | 2 | #include "ui.h" |
8f9bbc40 | 3 | #include <linux/compiler.h> |
ef8f34aa ACM |
4 | #include <linux/list.h> |
5 | #include <linux/rbtree.h> | |
6 | #include <stdlib.h> | |
7 | #include <sys/ttydefaults.h> | |
8 | #include "browser.h" | |
59e8fe32 | 9 | #include "helpline.h" |
ef8f34aa ACM |
10 | #include "../color.h" |
11 | #include "../util.h" | |
8f9bbc40 | 12 | #include <stdio.h> |
ef8f34aa | 13 | |
8f9bbc40 | 14 | static int ui_browser__percent_color(double percent, bool current) |
ef8f34aa ACM |
15 | { |
16 | if (current) | |
17 | return HE_COLORSET_SELECTED; | |
18 | if (percent >= MIN_RED) | |
19 | return HE_COLORSET_TOP; | |
20 | if (percent >= MIN_GREEN) | |
21 | return HE_COLORSET_MEDIUM; | |
22 | return HE_COLORSET_NORMAL; | |
23 | } | |
24 | ||
8f9bbc40 ACM |
25 | void ui_browser__set_color(struct ui_browser *self __used, int color) |
26 | { | |
27 | SLsmg_set_color(color); | |
28 | } | |
29 | ||
30 | void ui_browser__set_percent_color(struct ui_browser *self, | |
31 | double percent, bool current) | |
32 | { | |
33 | int color = ui_browser__percent_color(percent, current); | |
34 | ui_browser__set_color(self, color); | |
35 | } | |
36 | ||
37 | void ui_browser__gotorc(struct ui_browser *self, int y, int x) | |
38 | { | |
39 | SLsmg_gotorc(self->y + y, self->x + x); | |
40 | } | |
41 | ||
ef8f34aa ACM |
42 | void ui_browser__list_head_seek(struct ui_browser *self, off_t offset, int whence) |
43 | { | |
44 | struct list_head *head = self->entries; | |
45 | struct list_head *pos; | |
46 | ||
47 | switch (whence) { | |
48 | case SEEK_SET: | |
49 | pos = head->next; | |
50 | break; | |
51 | case SEEK_CUR: | |
d247eb6b | 52 | pos = self->top; |
ef8f34aa ACM |
53 | break; |
54 | case SEEK_END: | |
55 | pos = head->prev; | |
56 | break; | |
57 | default: | |
58 | return; | |
59 | } | |
60 | ||
61 | if (offset > 0) { | |
62 | while (offset-- != 0) | |
63 | pos = pos->next; | |
64 | } else { | |
65 | while (offset++ != 0) | |
66 | pos = pos->prev; | |
67 | } | |
68 | ||
d247eb6b | 69 | self->top = pos; |
ef8f34aa ACM |
70 | } |
71 | ||
72 | void ui_browser__rb_tree_seek(struct ui_browser *self, off_t offset, int whence) | |
73 | { | |
74 | struct rb_root *root = self->entries; | |
75 | struct rb_node *nd; | |
76 | ||
77 | switch (whence) { | |
78 | case SEEK_SET: | |
79 | nd = rb_first(root); | |
80 | break; | |
81 | case SEEK_CUR: | |
d247eb6b | 82 | nd = self->top; |
ef8f34aa ACM |
83 | break; |
84 | case SEEK_END: | |
85 | nd = rb_last(root); | |
86 | break; | |
87 | default: | |
88 | return; | |
89 | } | |
90 | ||
91 | if (offset > 0) { | |
92 | while (offset-- != 0) | |
93 | nd = rb_next(nd); | |
94 | } else { | |
95 | while (offset++ != 0) | |
96 | nd = rb_prev(nd); | |
97 | } | |
98 | ||
d247eb6b | 99 | self->top = nd; |
ef8f34aa ACM |
100 | } |
101 | ||
102 | unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self) | |
103 | { | |
104 | struct rb_node *nd; | |
105 | int row = 0; | |
106 | ||
d247eb6b ACM |
107 | if (self->top == NULL) |
108 | self->top = rb_first(self->entries); | |
ef8f34aa | 109 | |
d247eb6b | 110 | nd = self->top; |
ef8f34aa ACM |
111 | |
112 | while (nd != NULL) { | |
8f9bbc40 | 113 | ui_browser__gotorc(self, row, 0); |
ef8f34aa ACM |
114 | self->write(self, nd, row); |
115 | if (++row == self->height) | |
116 | break; | |
117 | nd = rb_next(nd); | |
118 | } | |
119 | ||
120 | return row; | |
121 | } | |
122 | ||
123 | bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row) | |
124 | { | |
d247eb6b | 125 | return self->top_idx + row == self->index; |
ef8f34aa ACM |
126 | } |
127 | ||
128 | void ui_browser__refresh_dimensions(struct ui_browser *self) | |
129 | { | |
130 | int cols, rows; | |
131 | newtGetScreenSize(&cols, &rows); | |
132 | ||
469917ce ACM |
133 | self->width = cols - 1; |
134 | self->height = rows - 2; | |
135 | self->y = 1; | |
136 | self->x = 0; | |
ef8f34aa ACM |
137 | } |
138 | ||
139 | void ui_browser__reset_index(struct ui_browser *self) | |
140 | { | |
d247eb6b | 141 | self->index = self->top_idx = 0; |
ef8f34aa ACM |
142 | self->seek(self, 0, SEEK_SET); |
143 | } | |
144 | ||
4c1c952e ACM |
145 | void ui_browser__add_exit_key(struct ui_browser *self, int key) |
146 | { | |
147 | newtFormAddHotKey(self->form, key); | |
148 | } | |
149 | ||
150 | void ui_browser__add_exit_keys(struct ui_browser *self, int keys[]) | |
151 | { | |
152 | int i = 0; | |
153 | ||
154 | while (keys[i] && i < 64) { | |
155 | ui_browser__add_exit_key(self, keys[i]); | |
156 | ++i; | |
157 | } | |
158 | } | |
159 | ||
b210b3bb ACM |
160 | void __ui_browser__show_title(struct ui_browser *browser, const char *title) |
161 | { | |
162 | SLsmg_gotorc(0, 0); | |
163 | ui_browser__set_color(browser, NEWT_COLORSET_ROOT); | |
164 | slsmg_write_nstring(title, browser->width); | |
165 | } | |
166 | ||
167 | void ui_browser__show_title(struct ui_browser *browser, const char *title) | |
168 | { | |
169 | pthread_mutex_lock(&ui__lock); | |
170 | __ui_browser__show_title(browser, title); | |
171 | pthread_mutex_unlock(&ui__lock); | |
172 | } | |
173 | ||
59e8fe32 ACM |
174 | int ui_browser__show(struct ui_browser *self, const char *title, |
175 | const char *helpline, ...) | |
ef8f34aa | 176 | { |
59e8fe32 | 177 | va_list ap; |
4c1c952e ACM |
178 | int keys[] = { NEWT_KEY_UP, NEWT_KEY_DOWN, NEWT_KEY_PGUP, |
179 | NEWT_KEY_PGDN, NEWT_KEY_HOME, NEWT_KEY_END, ' ', | |
180 | NEWT_KEY_LEFT, NEWT_KEY_ESCAPE, 'q', CTRL('c'), 0 }; | |
59e8fe32 | 181 | |
469917ce | 182 | if (self->form != NULL) |
ef8f34aa | 183 | newtFormDestroy(self->form); |
469917ce | 184 | |
ef8f34aa | 185 | ui_browser__refresh_dimensions(self); |
4c1c952e | 186 | self->form = newtForm(NULL, NULL, 0); |
ef8f34aa ACM |
187 | if (self->form == NULL) |
188 | return -1; | |
189 | ||
469917ce | 190 | self->sb = newtVerticalScrollbar(self->width, 1, self->height, |
ef8f34aa ACM |
191 | HE_COLORSET_NORMAL, |
192 | HE_COLORSET_SELECTED); | |
193 | if (self->sb == NULL) | |
194 | return -1; | |
195 | ||
5c35d69f | 196 | pthread_mutex_lock(&ui__lock); |
b210b3bb | 197 | __ui_browser__show_title(self, title); |
469917ce | 198 | |
4c1c952e | 199 | ui_browser__add_exit_keys(self, keys); |
ef8f34aa | 200 | newtFormAddComponent(self->form, self->sb); |
59e8fe32 ACM |
201 | |
202 | va_start(ap, helpline); | |
203 | ui_helpline__vpush(helpline, ap); | |
204 | va_end(ap); | |
5c35d69f | 205 | pthread_mutex_unlock(&ui__lock); |
ef8f34aa ACM |
206 | return 0; |
207 | } | |
208 | ||
59e8fe32 ACM |
209 | void ui_browser__hide(struct ui_browser *self) |
210 | { | |
5c35d69f | 211 | pthread_mutex_lock(&ui__lock); |
59e8fe32 | 212 | newtFormDestroy(self->form); |
59e8fe32 ACM |
213 | self->form = NULL; |
214 | ui_helpline__pop(); | |
5c35d69f | 215 | pthread_mutex_unlock(&ui__lock); |
59e8fe32 ACM |
216 | } |
217 | ||
ef8f34aa ACM |
218 | int ui_browser__refresh(struct ui_browser *self) |
219 | { | |
220 | int row; | |
221 | ||
5c35d69f | 222 | pthread_mutex_lock(&ui__lock); |
ef8f34aa ACM |
223 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); |
224 | row = self->refresh(self); | |
8f9bbc40 | 225 | ui_browser__set_color(self, HE_COLORSET_NORMAL); |
d247eb6b | 226 | SLsmg_fill_region(self->y + row, self->x, |
ef8f34aa | 227 | self->height - row, self->width, ' '); |
5c35d69f | 228 | pthread_mutex_unlock(&ui__lock); |
ef8f34aa ACM |
229 | |
230 | return 0; | |
231 | } | |
232 | ||
b50e003d | 233 | int ui_browser__run(struct ui_browser *self) |
ef8f34aa | 234 | { |
b50e003d ACM |
235 | struct newtExitStruct es; |
236 | ||
ef8f34aa ACM |
237 | if (ui_browser__refresh(self) < 0) |
238 | return -1; | |
239 | ||
240 | while (1) { | |
241 | off_t offset; | |
242 | ||
b50e003d | 243 | newtFormRun(self->form, &es); |
ef8f34aa | 244 | |
b50e003d | 245 | if (es.reason != NEWT_EXIT_HOTKEY) |
ef8f34aa | 246 | break; |
b50e003d | 247 | switch (es.u.key) { |
ef8f34aa ACM |
248 | case NEWT_KEY_DOWN: |
249 | if (self->index == self->nr_entries - 1) | |
250 | break; | |
251 | ++self->index; | |
d247eb6b ACM |
252 | if (self->index == self->top_idx + self->height) { |
253 | ++self->top_idx; | |
ef8f34aa ACM |
254 | self->seek(self, +1, SEEK_CUR); |
255 | } | |
256 | break; | |
257 | case NEWT_KEY_UP: | |
258 | if (self->index == 0) | |
259 | break; | |
260 | --self->index; | |
d247eb6b ACM |
261 | if (self->index < self->top_idx) { |
262 | --self->top_idx; | |
ef8f34aa ACM |
263 | self->seek(self, -1, SEEK_CUR); |
264 | } | |
265 | break; | |
266 | case NEWT_KEY_PGDN: | |
267 | case ' ': | |
d247eb6b | 268 | if (self->top_idx + self->height > self->nr_entries - 1) |
ef8f34aa ACM |
269 | break; |
270 | ||
271 | offset = self->height; | |
272 | if (self->index + offset > self->nr_entries - 1) | |
273 | offset = self->nr_entries - 1 - self->index; | |
274 | self->index += offset; | |
d247eb6b | 275 | self->top_idx += offset; |
ef8f34aa ACM |
276 | self->seek(self, +offset, SEEK_CUR); |
277 | break; | |
278 | case NEWT_KEY_PGUP: | |
d247eb6b | 279 | if (self->top_idx == 0) |
ef8f34aa ACM |
280 | break; |
281 | ||
d247eb6b ACM |
282 | if (self->top_idx < self->height) |
283 | offset = self->top_idx; | |
ef8f34aa ACM |
284 | else |
285 | offset = self->height; | |
286 | ||
287 | self->index -= offset; | |
d247eb6b | 288 | self->top_idx -= offset; |
ef8f34aa ACM |
289 | self->seek(self, -offset, SEEK_CUR); |
290 | break; | |
291 | case NEWT_KEY_HOME: | |
292 | ui_browser__reset_index(self); | |
293 | break; | |
294 | case NEWT_KEY_END: | |
295 | offset = self->height - 1; | |
296 | if (offset >= self->nr_entries) | |
297 | offset = self->nr_entries - 1; | |
298 | ||
299 | self->index = self->nr_entries - 1; | |
d247eb6b | 300 | self->top_idx = self->index - offset; |
ef8f34aa ACM |
301 | self->seek(self, -offset, SEEK_END); |
302 | break; | |
303 | default: | |
b50e003d | 304 | return es.u.key; |
ef8f34aa ACM |
305 | } |
306 | if (ui_browser__refresh(self) < 0) | |
307 | return -1; | |
308 | } | |
b50e003d | 309 | return -1; |
ef8f34aa ACM |
310 | } |
311 | ||
312 | unsigned int ui_browser__list_head_refresh(struct ui_browser *self) | |
313 | { | |
314 | struct list_head *pos; | |
315 | struct list_head *head = self->entries; | |
316 | int row = 0; | |
317 | ||
d247eb6b ACM |
318 | if (self->top == NULL || self->top == self->entries) |
319 | self->top = head->next; | |
ef8f34aa | 320 | |
d247eb6b | 321 | pos = self->top; |
ef8f34aa ACM |
322 | |
323 | list_for_each_from(pos, head) { | |
8f9bbc40 | 324 | ui_browser__gotorc(self, row, 0); |
ef8f34aa ACM |
325 | self->write(self, pos, row); |
326 | if (++row == self->height) | |
327 | break; | |
328 | } | |
329 | ||
330 | return row; | |
331 | } | |
332 | ||
333 | static struct newtPercentTreeColors { | |
334 | const char *topColorFg, *topColorBg; | |
335 | const char *mediumColorFg, *mediumColorBg; | |
336 | const char *normalColorFg, *normalColorBg; | |
337 | const char *selColorFg, *selColorBg; | |
338 | const char *codeColorFg, *codeColorBg; | |
339 | } defaultPercentTreeColors = { | |
340 | "red", "lightgray", | |
341 | "green", "lightgray", | |
342 | "black", "lightgray", | |
343 | "lightgray", "magenta", | |
344 | "blue", "lightgray", | |
345 | }; | |
346 | ||
347 | void ui_browser__init(void) | |
348 | { | |
349 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | |
350 | ||
351 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | |
352 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | |
353 | sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); | |
354 | sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); | |
355 | sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); | |
356 | } |