]>
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" | |
19 | #include "../color.h" | |
20 | #include "../util.h" | |
21 | ||
22 | #if SLANG_VERSION < 20104 | |
23 | #define sltt_set_color(obj, name, fg, bg) \ | |
24 | SLtt_set_color(obj,(char *)name, (char *)fg, (char *)bg) | |
25 | #else | |
26 | #define sltt_set_color SLtt_set_color | |
27 | #endif | |
28 | ||
29 | newtComponent newt_form__new(void); | |
30 | ||
31 | int ui_browser__percent_color(double percent, bool current) | |
32 | { | |
33 | if (current) | |
34 | return HE_COLORSET_SELECTED; | |
35 | if (percent >= MIN_RED) | |
36 | return HE_COLORSET_TOP; | |
37 | if (percent >= MIN_GREEN) | |
38 | return HE_COLORSET_MEDIUM; | |
39 | return HE_COLORSET_NORMAL; | |
40 | } | |
41 | ||
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) { | |
d247eb6b | 113 | SLsmg_gotorc(self->y + row, self->x); |
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 | ||
133 | if (self->width > cols - 4) | |
134 | self->width = cols - 4; | |
135 | self->height = rows - 5; | |
136 | if (self->height > self->nr_entries) | |
137 | self->height = self->nr_entries; | |
d247eb6b ACM |
138 | self->y = (rows - self->height) / 2; |
139 | self->x = (cols - self->width) / 2; | |
ef8f34aa ACM |
140 | } |
141 | ||
142 | void ui_browser__reset_index(struct ui_browser *self) | |
143 | { | |
d247eb6b | 144 | self->index = self->top_idx = 0; |
ef8f34aa ACM |
145 | self->seek(self, 0, SEEK_SET); |
146 | } | |
147 | ||
148 | int ui_browser__show(struct ui_browser *self, const char *title) | |
149 | { | |
150 | if (self->form != NULL) { | |
151 | newtFormDestroy(self->form); | |
152 | newtPopWindow(); | |
153 | } | |
154 | ui_browser__refresh_dimensions(self); | |
155 | newtCenteredWindow(self->width, self->height, title); | |
156 | self->form = newt_form__new(); | |
157 | if (self->form == NULL) | |
158 | return -1; | |
159 | ||
160 | self->sb = newtVerticalScrollbar(self->width, 0, self->height, | |
161 | HE_COLORSET_NORMAL, | |
162 | HE_COLORSET_SELECTED); | |
163 | if (self->sb == NULL) | |
164 | return -1; | |
165 | ||
166 | newtFormAddHotKey(self->form, NEWT_KEY_UP); | |
167 | newtFormAddHotKey(self->form, NEWT_KEY_DOWN); | |
168 | newtFormAddHotKey(self->form, NEWT_KEY_PGUP); | |
169 | newtFormAddHotKey(self->form, NEWT_KEY_PGDN); | |
170 | newtFormAddHotKey(self->form, NEWT_KEY_HOME); | |
171 | newtFormAddHotKey(self->form, NEWT_KEY_END); | |
9e22d637 | 172 | newtFormAddHotKey(self->form, ' '); |
ef8f34aa ACM |
173 | newtFormAddComponent(self->form, self->sb); |
174 | return 0; | |
175 | } | |
176 | ||
177 | int ui_browser__refresh(struct ui_browser *self) | |
178 | { | |
179 | int row; | |
180 | ||
181 | newtScrollbarSet(self->sb, self->index, self->nr_entries - 1); | |
182 | row = self->refresh(self); | |
183 | SLsmg_set_color(HE_COLORSET_NORMAL); | |
d247eb6b | 184 | SLsmg_fill_region(self->y + row, self->x, |
ef8f34aa ACM |
185 | self->height - row, self->width, ' '); |
186 | ||
187 | return 0; | |
188 | } | |
189 | ||
190 | int ui_browser__run(struct ui_browser *self, struct newtExitStruct *es) | |
191 | { | |
192 | if (ui_browser__refresh(self) < 0) | |
193 | return -1; | |
194 | ||
195 | while (1) { | |
196 | off_t offset; | |
197 | ||
198 | newtFormRun(self->form, es); | |
199 | ||
200 | if (es->reason != NEWT_EXIT_HOTKEY) | |
201 | break; | |
202 | if (is_exit_key(es->u.key)) | |
203 | return es->u.key; | |
204 | switch (es->u.key) { | |
205 | case NEWT_KEY_DOWN: | |
206 | if (self->index == self->nr_entries - 1) | |
207 | break; | |
208 | ++self->index; | |
d247eb6b ACM |
209 | if (self->index == self->top_idx + self->height) { |
210 | ++self->top_idx; | |
ef8f34aa ACM |
211 | self->seek(self, +1, SEEK_CUR); |
212 | } | |
213 | break; | |
214 | case NEWT_KEY_UP: | |
215 | if (self->index == 0) | |
216 | break; | |
217 | --self->index; | |
d247eb6b ACM |
218 | if (self->index < self->top_idx) { |
219 | --self->top_idx; | |
ef8f34aa ACM |
220 | self->seek(self, -1, SEEK_CUR); |
221 | } | |
222 | break; | |
223 | case NEWT_KEY_PGDN: | |
224 | case ' ': | |
d247eb6b | 225 | if (self->top_idx + self->height > self->nr_entries - 1) |
ef8f34aa ACM |
226 | break; |
227 | ||
228 | offset = self->height; | |
229 | if (self->index + offset > self->nr_entries - 1) | |
230 | offset = self->nr_entries - 1 - self->index; | |
231 | self->index += offset; | |
d247eb6b | 232 | self->top_idx += offset; |
ef8f34aa ACM |
233 | self->seek(self, +offset, SEEK_CUR); |
234 | break; | |
235 | case NEWT_KEY_PGUP: | |
d247eb6b | 236 | if (self->top_idx == 0) |
ef8f34aa ACM |
237 | break; |
238 | ||
d247eb6b ACM |
239 | if (self->top_idx < self->height) |
240 | offset = self->top_idx; | |
ef8f34aa ACM |
241 | else |
242 | offset = self->height; | |
243 | ||
244 | self->index -= offset; | |
d247eb6b | 245 | self->top_idx -= offset; |
ef8f34aa ACM |
246 | self->seek(self, -offset, SEEK_CUR); |
247 | break; | |
248 | case NEWT_KEY_HOME: | |
249 | ui_browser__reset_index(self); | |
250 | break; | |
251 | case NEWT_KEY_END: | |
252 | offset = self->height - 1; | |
253 | if (offset >= self->nr_entries) | |
254 | offset = self->nr_entries - 1; | |
255 | ||
256 | self->index = self->nr_entries - 1; | |
d247eb6b | 257 | self->top_idx = self->index - offset; |
ef8f34aa ACM |
258 | self->seek(self, -offset, SEEK_END); |
259 | break; | |
260 | default: | |
261 | return es->u.key; | |
262 | } | |
263 | if (ui_browser__refresh(self) < 0) | |
264 | return -1; | |
265 | } | |
266 | return 0; | |
267 | } | |
268 | ||
269 | unsigned int ui_browser__list_head_refresh(struct ui_browser *self) | |
270 | { | |
271 | struct list_head *pos; | |
272 | struct list_head *head = self->entries; | |
273 | int row = 0; | |
274 | ||
d247eb6b ACM |
275 | if (self->top == NULL || self->top == self->entries) |
276 | self->top = head->next; | |
ef8f34aa | 277 | |
d247eb6b | 278 | pos = self->top; |
ef8f34aa ACM |
279 | |
280 | list_for_each_from(pos, head) { | |
d247eb6b | 281 | SLsmg_gotorc(self->y + row, self->x); |
ef8f34aa ACM |
282 | self->write(self, pos, row); |
283 | if (++row == self->height) | |
284 | break; | |
285 | } | |
286 | ||
287 | return row; | |
288 | } | |
289 | ||
290 | static struct newtPercentTreeColors { | |
291 | const char *topColorFg, *topColorBg; | |
292 | const char *mediumColorFg, *mediumColorBg; | |
293 | const char *normalColorFg, *normalColorBg; | |
294 | const char *selColorFg, *selColorBg; | |
295 | const char *codeColorFg, *codeColorBg; | |
296 | } defaultPercentTreeColors = { | |
297 | "red", "lightgray", | |
298 | "green", "lightgray", | |
299 | "black", "lightgray", | |
300 | "lightgray", "magenta", | |
301 | "blue", "lightgray", | |
302 | }; | |
303 | ||
304 | void ui_browser__init(void) | |
305 | { | |
306 | struct newtPercentTreeColors *c = &defaultPercentTreeColors; | |
307 | ||
308 | sltt_set_color(HE_COLORSET_TOP, NULL, c->topColorFg, c->topColorBg); | |
309 | sltt_set_color(HE_COLORSET_MEDIUM, NULL, c->mediumColorFg, c->mediumColorBg); | |
310 | sltt_set_color(HE_COLORSET_NORMAL, NULL, c->normalColorFg, c->normalColorBg); | |
311 | sltt_set_color(HE_COLORSET_SELECTED, NULL, c->selColorFg, c->selColorBg); | |
312 | sltt_set_color(HE_COLORSET_CODE, NULL, c->codeColorFg, c->codeColorBg); | |
313 | } |