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