1#include "libslang.h"
2#include "ui.h"
3#include <linux/compiler.h>
4#include <linux/list.h>
5#include <linux/rbtree.h>
6#include <stdlib.h>
7#include <sys/ttydefaults.h>
8#include "browser.h"
9#include "helpline.h"
10#include "../color.h"
11#include "../util.h"
12#include <stdio.h>
13
14static int ui_browser__percent_color(double percent, bool current)
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
25void ui_browser__set_color(struct ui_browser *self __used, int color)
26{
27	SLsmg_set_color(color);
28}
29
30void 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
37void ui_browser__gotorc(struct ui_browser *self, int y, int x)
38{
39	SLsmg_gotorc(self->y + y, self->x + x);
40}
41
42void 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:
52		pos = self->top;
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
69	self->top = pos;
70}
71
72void 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:
82		nd = self->top;
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
99	self->top = nd;
100}
101
102unsigned int ui_browser__rb_tree_refresh(struct ui_browser *self)
103{
104	struct rb_node *nd;
105	int row = 0;
106
107	if (self->top == NULL)
108                self->top = rb_first(self->entries);
109
110	nd = self->top;
111
112	while (nd != NULL) {
113		ui_browser__gotorc(self, row, 0);
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
123bool ui_browser__is_current_entry(struct ui_browser *self, unsigned row)
124{
125	return self->top_idx + row == self->index;
126}
127
128void ui_browser__refresh_dimensions(struct ui_browser *self)
129{
130	int cols, rows;
131	newtGetScreenSize(&cols, &rows);
132
133	self->width = cols - 1;
134	self->height = rows - 2;
135	self->y = 1;
136	self->x = 0;
137}
138
139void ui_browser__reset_index(struct ui_browser *self)
140{
141	self->index = self->top_idx = 0;
142	self->seek(self, 0, SEEK_SET);
143}
144
145void ui_browser__add_exit_key(struct ui_browser *self, int key)
146{
147	newtFormAddHotKey(self->form, key);
148}
149
150void 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
160void __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
167void 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
174int ui_browser__show(struct ui_browser *self, const char *title,
175		     const char *helpline, ...)
176{
177	va_list ap;
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 };
181
182	if (self->form != NULL)
183		newtFormDestroy(self->form);
184
185	ui_browser__refresh_dimensions(self);
186	self->form = newtForm(NULL, NULL, 0);
187	if (self->form == NULL)
188		return -1;
189
190	self->sb = newtVerticalScrollbar(self->width, 1, self->height,
191					 HE_COLORSET_NORMAL,
192					 HE_COLORSET_SELECTED);
193	if (self->sb == NULL)
194		return -1;
195
196	pthread_mutex_lock(&ui__lock);
197	__ui_browser__show_title(self, title);
198
199	ui_browser__add_exit_keys(self, keys);
200	newtFormAddComponent(self->form, self->sb);
201
202	va_start(ap, helpline);
203	ui_helpline__vpush(helpline, ap);
204	va_end(ap);
205	pthread_mutex_unlock(&ui__lock);
206	return 0;
207}
208
209void ui_browser__hide(struct ui_browser *self)
210{
211	pthread_mutex_lock(&ui__lock);
212	newtFormDestroy(self->form);
213	self->form = NULL;
214	ui_helpline__pop();
215	pthread_mutex_unlock(&ui__lock);
216}
217
218int ui_browser__refresh(struct ui_browser *self)
219{
220	int row;
221
222	pthread_mutex_lock(&ui__lock);
223	newtScrollbarSet(self->sb, self->index, self->nr_entries - 1);
224	row = self->refresh(self);
225	ui_browser__set_color(self, HE_COLORSET_NORMAL);
226	SLsmg_fill_region(self->y + row, self->x,
227			  self->height - row, self->width, ' ');
228	pthread_mutex_unlock(&ui__lock);
229
230	return 0;
231}
232
233int ui_browser__run(struct ui_browser *self)
234{
235	struct newtExitStruct es;
236
237	if (ui_browser__refresh(self) < 0)
238		return -1;
239
240	while (1) {
241		off_t offset;
242
243		newtFormRun(self->form, &es);
244
245		if (es.reason != NEWT_EXIT_HOTKEY)
246			break;
247		switch (es.u.key) {
248		case NEWT_KEY_DOWN:
249			if (self->index == self->nr_entries - 1)
250				break;
251			++self->index;
252			if (self->index == self->top_idx + self->height) {
253				++self->top_idx;
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;
261			if (self->index < self->top_idx) {
262				--self->top_idx;
263				self->seek(self, -1, SEEK_CUR);
264			}
265			break;
266		case NEWT_KEY_PGDN:
267		case ' ':
268			if (self->top_idx + self->height > self->nr_entries - 1)
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;
275			self->top_idx += offset;
276			self->seek(self, +offset, SEEK_CUR);
277			break;
278		case NEWT_KEY_PGUP:
279			if (self->top_idx == 0)
280				break;
281
282			if (self->top_idx < self->height)
283				offset = self->top_idx;
284			else
285				offset = self->height;
286
287			self->index -= offset;
288			self->top_idx -= offset;
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;
300			self->top_idx = self->index - offset;
301			self->seek(self, -offset, SEEK_END);
302			break;
303		default:
304			return es.u.key;
305		}
306		if (ui_browser__refresh(self) < 0)
307			return -1;
308	}
309	return -1;
310}
311
312unsigned 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
318	if (self->top == NULL || self->top == self->entries)
319                self->top = head->next;
320
321	pos = self->top;
322
323	list_for_each_from(pos, head) {
324		ui_browser__gotorc(self, row, 0);
325		self->write(self, pos, row);
326		if (++row == self->height)
327			break;
328	}
329
330	return row;
331}
332
333static 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
347void 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}
357