perf ui: Make SPACE work as PGDN in all browsers
[cascardo/linux.git] / tools / perf / util / ui / browser.c
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:
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
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:
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
102 unsigned 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                 SLsmg_gotorc(self->y + row, self->x);
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 {
125         return self->top_idx + row == self->index;
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;
138         self->y  = (rows - self->height) / 2;
139         self->x = (cols - self->width) / 2;
140 }
141
142 void ui_browser__reset_index(struct ui_browser *self)
143 {
144         self->index = self->top_idx = 0;
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);
172         newtFormAddHotKey(self->form, ' ');
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);
184         SLsmg_fill_region(self->y + row, self->x,
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;
209                         if (self->index == self->top_idx + self->height) {
210                                 ++self->top_idx;
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;
218                         if (self->index < self->top_idx) {
219                                 --self->top_idx;
220                                 self->seek(self, -1, SEEK_CUR);
221                         }
222                         break;
223                 case NEWT_KEY_PGDN:
224                 case ' ':
225                         if (self->top_idx + self->height > self->nr_entries - 1)
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;
232                         self->top_idx += offset;
233                         self->seek(self, +offset, SEEK_CUR);
234                         break;
235                 case NEWT_KEY_PGUP:
236                         if (self->top_idx == 0)
237                                 break;
238
239                         if (self->top_idx < self->height)
240                                 offset = self->top_idx;
241                         else
242                                 offset = self->height;
243
244                         self->index -= offset;
245                         self->top_idx -= offset;
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;
257                         self->top_idx = self->index - offset;
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
275         if (self->top == NULL || self->top == self->entries)
276                 self->top = head->next;
277
278         pos = self->top;
279
280         list_for_each_from(pos, head) {
281                 SLsmg_gotorc(self->y + row, self->x);
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 }