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 14 static 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 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 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 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 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 self->width = cols - 1; 134 self->height = rows - 2; 135 self->y = 1; 136 self->x = 0; 137 } 138 139 void 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 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 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 174 int 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 209 void 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 218 int 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 233 int 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 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 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 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 } 357