source: mainline/uspace/app/nav/panel.c@ 692c7f40

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 692c7f40 was 692c7f40, checked in by jxsvoboda <5887334+jxsvoboda@…>, 4 years ago

Panel activation

Active panel can be switched using the Tab key. Mouse activation is
not implemented.

  • Property mode set to 100644
File size: 16.4 KB
Line 
1/*
2 * Copyright (c) 2021 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup nav
30 * @{
31 */
32/** @file Navigator panel.
33 *
34 * Displays a file listing.
35 */
36
37#include <dirent.h>
38#include <errno.h>
39#include <gfx/render.h>
40#include <gfx/text.h>
41#include <stdlib.h>
42#include <str.h>
43#include <ui/control.h>
44#include <ui/paint.h>
45#include <ui/resource.h>
46#include "panel.h"
47#include "nav.h"
48
49static void panel_ctl_destroy(void *);
50static errno_t panel_ctl_paint(void *);
51static ui_evclaim_t panel_ctl_kbd_event(void *, kbd_event_t *);
52static ui_evclaim_t panel_ctl_pos_event(void *, pos_event_t *);
53
54/** Panel control ops */
55ui_control_ops_t panel_ctl_ops = {
56 .destroy = panel_ctl_destroy,
57 .paint = panel_ctl_paint,
58 .kbd_event = panel_ctl_kbd_event,
59 .pos_event = panel_ctl_pos_event
60};
61
62/** Create panel.
63 *
64 * @param window Containing window
65 * @param active @c true iff panel should be active
66 * @param rpanel Place to store pointer to new panel
67 * @return EOK on success or an error code
68 */
69errno_t panel_create(ui_window_t *window, bool active, panel_t **rpanel)
70{
71 panel_t *panel;
72 errno_t rc;
73
74 panel = calloc(1, sizeof(panel_t));
75 if (panel == NULL)
76 return ENOMEM;
77
78 rc = ui_control_new(&panel_ctl_ops, (void *)panel,
79 &panel->control);
80 if (rc != EOK) {
81 free(panel);
82 return rc;
83 }
84
85 rc = gfx_color_new_ega(0x07, &panel->color);
86 if (rc != EOK)
87 goto error;
88
89 rc = gfx_color_new_ega(0x30, &panel->curs_color);
90 if (rc != EOK)
91 goto error;
92
93 rc = gfx_color_new_ega(0x0f, &panel->act_border_color);
94 if (rc != EOK)
95 goto error;
96
97 panel->window = window;
98 list_initialize(&panel->entries);
99 panel->entries_cnt = 0;
100 panel->active = active;
101 *rpanel = panel;
102 return EOK;
103error:
104 if (panel->color != NULL)
105 gfx_color_delete(panel->color);
106 if (panel->curs_color != NULL)
107 gfx_color_delete(panel->curs_color);
108 ui_control_delete(panel->control);
109 free(panel);
110 return rc;
111}
112
113/** Destroy panel.
114 *
115 * @param panel Panel
116 */
117void panel_destroy(panel_t *panel)
118{
119 gfx_color_delete(panel->color);
120 gfx_color_delete(panel->curs_color);
121 gfx_color_delete(panel->act_border_color);
122 panel_clear_entries(panel);
123 ui_control_delete(panel->control);
124 free(panel);
125}
126
127/** Paint panel entry.
128 *
129 * @param entry Panel entry
130 * @param entry_idx Entry index (within list of entries)
131 * @return EOK on success or an error code
132 */
133errno_t panel_entry_paint(panel_entry_t *entry, size_t entry_idx)
134{
135 panel_t *panel = entry->panel;
136 gfx_context_t *gc = ui_window_get_gc(panel->window);
137 ui_resource_t *res = ui_window_get_res(panel->window);
138 gfx_font_t *font = ui_resource_get_font(res);
139 gfx_text_fmt_t fmt;
140 gfx_coord2_t pos;
141 gfx_rect_t rect;
142 size_t rows;
143 errno_t rc;
144
145 gfx_text_fmt_init(&fmt);
146 rows = panel_page_size(panel);
147
148 /* Do not display entry outside of current page */
149 if (entry_idx < panel->page_idx ||
150 entry_idx >= panel->page_idx + rows)
151 return EOK;
152
153 pos.x = panel->rect.p0.x + 1;
154 pos.y = panel->rect.p0.y + 1 + entry_idx - panel->page_idx;
155
156 if (entry == panel->cursor && panel->active)
157 fmt.color = panel->curs_color;
158 else
159 fmt.color = panel->color;
160
161 /* Draw entry background */
162 rect.p0 = pos;
163 rect.p1.x = panel->rect.p1.x - 1;
164 rect.p1.y = rect.p0.y + 1;
165
166 rc = gfx_set_color(gc, fmt.color);
167 if (rc != EOK)
168 return rc;
169
170 rc = gfx_fill_rect(gc, &rect);
171 if (rc != EOK)
172 return rc;
173
174 rc = gfx_puttext(font, &pos, &fmt, entry->name);
175 if (rc != EOK)
176 return rc;
177
178 return EOK;
179}
180
181/** Paint panel.
182 *
183 * @param panel Panel
184 */
185errno_t panel_paint(panel_t *panel)
186{
187 gfx_context_t *gc = ui_window_get_gc(panel->window);
188 ui_resource_t *res = ui_window_get_res(panel->window);
189 gfx_text_fmt_t fmt;
190 panel_entry_t *entry;
191 ui_box_style_t bstyle;
192 gfx_color_t *bcolor;
193 int i, lines;
194 errno_t rc;
195
196 gfx_text_fmt_init(&fmt);
197
198 rc = gfx_set_color(gc, panel->color);
199 if (rc != EOK)
200 return rc;
201
202 rc = gfx_fill_rect(gc, &panel->rect);
203 if (rc != EOK)
204 return rc;
205
206 if (panel->active) {
207 bstyle = ui_box_double;
208 bcolor = panel->act_border_color;
209 } else {
210 bstyle = ui_box_single;
211 bcolor = panel->color;
212 }
213
214 rc = ui_paint_text_box(res, &panel->rect, bstyle, bcolor);
215 if (rc != EOK)
216 return rc;
217
218 lines = panel_page_size(panel);
219 i = 0;
220
221 entry = panel->page;
222 while (entry != NULL && i < lines) {
223 rc = panel_entry_paint(entry, panel->page_idx + i);
224 if (rc != EOK)
225 return rc;
226
227 ++i;
228 entry = panel_next(entry);
229 }
230
231 rc = gfx_update(gc);
232 if (rc != EOK)
233 return rc;
234
235 return EOK;
236}
237
238/** Handle panel keyboard event.
239 *
240 * @param panel Panel
241 * @param event Keyboard event
242 * @return ui_claimed iff event was claimed
243 */
244ui_evclaim_t panel_kbd_event(panel_t *panel, kbd_event_t *event)
245{
246 if (!panel->active)
247 return ui_unclaimed;
248
249 if (event->type == KEY_PRESS) {
250 if ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
251 switch (event->key) {
252 case KC_UP:
253 panel_cursor_up(panel);
254 break;
255 case KC_DOWN:
256 panel_cursor_down(panel);
257 break;
258 case KC_HOME:
259 panel_cursor_top(panel);
260 break;
261 case KC_END:
262 panel_cursor_bottom(panel);
263 break;
264 case KC_PAGE_UP:
265 panel_page_up(panel);
266 break;
267 case KC_PAGE_DOWN:
268 panel_page_down(panel);
269 break;
270 default:
271 break;
272 }
273 }
274 }
275
276 return ui_claimed;
277}
278
279/** Handle panel position event.
280 *
281 * @param panel Panel
282 * @param event Position event
283 * @return ui_claimed iff event was claimed
284 */
285ui_evclaim_t panel_pos_event(panel_t *panel, pos_event_t *event)
286{
287 return ui_unclaimed;
288}
289
290/** Get base control for panel.
291 *
292 * @param panel Panel
293 * @return Base UI control
294 */
295ui_control_t *panel_ctl(panel_t *panel)
296{
297 return panel->control;
298}
299
300/** Set panel rectangle.
301 *
302 * @param panel Panel
303 * @param rect Rectangle
304 */
305void panel_set_rect(panel_t *panel, gfx_rect_t *rect)
306{
307 panel->rect = *rect;
308}
309
310/** Get panel page size.
311 *
312 * @param panel Panel
313 * @return Number of entries that fit in panel at the same time.
314 */
315unsigned panel_page_size(panel_t *panel)
316{
317 return panel->rect.p1.y - panel->rect.p0.y - 2;
318}
319
320/** Determine if panel is active.
321 *
322 * @param panel Panel
323 * @return @c true iff panel is active
324 */
325bool panel_is_active(panel_t *panel)
326{
327 return panel->active;
328}
329
330/** Activate panel.
331 *
332 * @param panel Panel
333 */
334void panel_activate(panel_t *panel)
335{
336 panel->active = true;
337 (void) panel_paint(panel);
338}
339
340/** Deactivate panel.
341 *
342 * @param panel Panel
343 */
344void panel_deactivate(panel_t *panel)
345{
346 panel->active = false;
347 (void) panel_paint(panel);
348}
349
350/** Destroy panel control.
351 *
352 * @param arg Argument (panel_t *)
353 */
354void panel_ctl_destroy(void *arg)
355{
356 panel_t *panel = (panel_t *) arg;
357
358 panel_destroy(panel);
359}
360
361/** Paint panel control.
362 *
363 * @param arg Argument (panel_t *)
364 * @return EOK on success or an error code
365 */
366errno_t panel_ctl_paint(void *arg)
367{
368 panel_t *panel = (panel_t *) arg;
369
370 return panel_paint(panel);
371}
372
373/** Handle panel control keyboard event.
374 *
375 * @param arg Argument (panel_t *)
376 * @param kbd_event Keyboard event
377 * @return @c ui_claimed iff the event is claimed
378 */
379ui_evclaim_t panel_ctl_kbd_event(void *arg, kbd_event_t *event)
380{
381 panel_t *panel = (panel_t *) arg;
382
383 return panel_kbd_event(panel, event);
384}
385
386/** Handle panel control position event.
387 *
388 * @param arg Argument (panel_t *)
389 * @param pos_event Position event
390 * @return @c ui_claimed iff the event is claimed
391 */
392ui_evclaim_t panel_ctl_pos_event(void *arg, pos_event_t *event)
393{
394 panel_t *panel = (panel_t *) arg;
395
396 return panel_pos_event(panel, event);
397}
398
399/** Append new panel entry.
400 *
401 * @param panel Panel
402 * @param name File name
403 * @param size File size;
404 * @return EOK on success or an error code
405 */
406errno_t panel_entry_append(panel_t *panel, const char *name, uint64_t size)
407{
408 panel_entry_t *entry;
409
410 entry = calloc(1, sizeof(panel_entry_t));
411 if (entry == NULL)
412 return ENOMEM;
413
414 entry->panel = panel;
415 entry->name = str_dup(name);
416 if (entry->name == NULL) {
417 free(entry);
418 return ENOMEM;
419 }
420
421 entry->size = size;
422 link_initialize(&entry->lentries);
423 list_append(&entry->lentries, &panel->entries);
424 ++panel->entries_cnt;
425 return EOK;
426}
427
428/** Delete panel entry.
429 *
430 * @param entry Panel entry
431 */
432void panel_entry_delete(panel_entry_t *entry)
433{
434 if (entry->panel->cursor == entry)
435 entry->panel->cursor = NULL;
436 if (entry->panel->page == entry)
437 entry->panel->page = NULL;
438
439 list_remove(&entry->lentries);
440 --entry->panel->entries_cnt;
441 free(entry->name);
442 free(entry);
443}
444
445/** Clear panel entry list.
446 *
447 * @param panel Panel
448 */
449void panel_clear_entries(panel_t *panel)
450{
451 panel_entry_t *entry;
452
453 entry = panel_first(panel);
454 while (entry != NULL) {
455 panel_entry_delete(entry);
456 entry = panel_first(panel);
457 }
458}
459
460/** Read directory into panel entry list.
461 *
462 * @param panel Panel
463 * @param dirname Directory path
464 * @return EOK on success or an error code
465 */
466errno_t panel_read_dir(panel_t *panel, const char *dirname)
467{
468 DIR *dir;
469 struct dirent *dirent;
470 errno_t rc;
471
472 dir = opendir(dirname);
473 if (dir == NULL)
474 return errno;
475
476 dirent = readdir(dir);
477 while (dirent != NULL) {
478 rc = panel_entry_append(panel, dirent->d_name, 1);
479 if (rc != EOK)
480 goto error;
481 dirent = readdir(dir);
482 }
483
484 closedir(dir);
485
486 panel->cursor = panel_first(panel);
487 panel->cursor_idx = 0;
488 panel->page = panel_first(panel);
489 panel->page_idx = 0;
490 return EOK;
491error:
492 closedir(dir);
493 return rc;
494}
495
496/** Return first panel entry.
497 *
498 * @panel Panel
499 * @return First panel entry or @c NULL if there are no entries
500 */
501panel_entry_t *panel_first(panel_t *panel)
502{
503 link_t *link;
504
505 link = list_first(&panel->entries);
506 if (link == NULL)
507 return NULL;
508
509 return list_get_instance(link, panel_entry_t, lentries);
510}
511
512/** Return last panel entry.
513 *
514 * @panel Panel
515 * @return Last panel entry or @c NULL if there are no entries
516 */
517panel_entry_t *panel_last(panel_t *panel)
518{
519 link_t *link;
520
521 link = list_last(&panel->entries);
522 if (link == NULL)
523 return NULL;
524
525 return list_get_instance(link, panel_entry_t, lentries);
526}
527
528/** Return next panel entry.
529 *
530 * @param cur Current entry
531 * @return Next entry or @c NULL if @a cur is the last entry
532 */
533panel_entry_t *panel_next(panel_entry_t *cur)
534{
535 link_t *link;
536
537 link = list_next(&cur->lentries, &cur->panel->entries);
538 if (link == NULL)
539 return NULL;
540
541 return list_get_instance(link, panel_entry_t, lentries);
542}
543
544/** Return previous panel entry.
545 *
546 * @param cur Current entry
547 * @return Previous entry or @c NULL if @a cur is the first entry
548 */
549panel_entry_t *panel_prev(panel_entry_t *cur)
550{
551 link_t *link;
552
553 link = list_prev(&cur->lentries, &cur->panel->entries);
554 if (link == NULL)
555 return NULL;
556
557 return list_get_instance(link, panel_entry_t, lentries);
558}
559
560/** Move cursor to a new position, possibly scrolling.
561 *
562 * @param panel Panel
563 * @param entry New entry under cursor
564 * @param entry_idx Index of new entry under cursor
565 */
566void panel_cursor_move(panel_t *panel, panel_entry_t *entry, size_t entry_idx)
567{
568 gfx_context_t *gc = ui_window_get_gc(panel->window);
569 panel_entry_t *old_cursor;
570 size_t old_idx;
571 size_t rows;
572 panel_entry_t *e;
573 size_t i;
574
575 rows = panel_page_size(panel);
576
577 old_cursor = panel->cursor;
578 old_idx = panel->cursor_idx;
579
580 panel->cursor = entry;
581 panel->cursor_idx = entry_idx;
582
583 if (entry_idx >= panel->page_idx &&
584 entry_idx < panel->page_idx + rows) {
585 /*
586 * If cursor is still on the current page, we're not
587 * scrolling. Just unpaint old cursor and paint new
588 * cursor.
589 */
590 panel_entry_paint(old_cursor, old_idx);
591 panel_entry_paint(panel->cursor, panel->cursor_idx);
592
593 (void) gfx_update(gc);
594 } else {
595 /*
596 * Need to scroll and update all rows.
597 */
598
599 /* Scrolling up */
600 if (entry_idx < panel->page_idx) {
601 panel->page = entry;
602 panel->page_idx = entry_idx;
603 }
604
605 /* Scrolling down */
606 if (entry_idx >= panel->page_idx + rows) {
607 if (entry_idx >= rows) {
608 panel->page_idx = entry_idx - rows + 1;
609 /* Find first page entry (go back rows - 1) */
610 e = entry;
611 for (i = 0; i < rows - 1; i++) {
612 e = panel_prev(e);
613 }
614
615 /* Should be valid */
616 assert(e != NULL);
617 panel->page = e;
618 } else {
619 panel->page = panel_first(panel);
620 panel->page_idx = 0;
621 }
622 }
623
624 (void) panel_paint(panel);
625 }
626}
627
628/** Move cursor one entry up.
629 *
630 * @param panel Panel
631 */
632void panel_cursor_up(panel_t *panel)
633{
634 panel_entry_t *prev;
635 size_t prev_idx;
636
637 prev = panel_prev(panel->cursor);
638 prev_idx = panel->cursor_idx - 1;
639 if (prev != NULL)
640 panel_cursor_move(panel, prev, prev_idx);
641}
642
643/** Move cursor one entry down.
644 *
645 * @param panel Panel
646 */
647void panel_cursor_down(panel_t *panel)
648{
649 panel_entry_t *next;
650 size_t next_idx;
651
652 next = panel_next(panel->cursor);
653 next_idx = panel->cursor_idx + 1;
654 if (next != NULL)
655 panel_cursor_move(panel, next, next_idx);
656}
657
658/** Move cursor to top.
659 *
660 * @param panel Panel
661 */
662void panel_cursor_top(panel_t *panel)
663{
664 panel_cursor_move(panel, panel_first(panel), 0);
665}
666
667/** Move cursor to bottom.
668 *
669 * @param panel Panel
670 */
671void panel_cursor_bottom(panel_t *panel)
672{
673 panel_cursor_move(panel, panel_last(panel), panel->entries_cnt - 1);
674}
675
676/** Move one page up.
677 *
678 * @param panel Panel
679 */
680void panel_page_up(panel_t *panel)
681{
682 gfx_context_t *gc = ui_window_get_gc(panel->window);
683 panel_entry_t *old_page;
684 panel_entry_t *old_cursor;
685 size_t old_idx;
686 size_t rows;
687 panel_entry_t *entry;
688 size_t i;
689
690 rows = panel_page_size(panel);
691
692 old_page = panel->page;
693 old_cursor = panel->cursor;
694 old_idx = panel->cursor_idx;
695
696 /* Move page by rows entries up (if possible) */
697 for (i = 0; i < rows; i++) {
698 entry = panel_prev(panel->page);
699 if (entry != NULL) {
700 panel->page = entry;
701 --panel->page_idx;
702 }
703 }
704
705 /* Move cursor by rows entries up (if possible) */
706
707 for (i = 0; i < rows; i++) {
708 entry = panel_prev(panel->cursor);
709 if (entry != NULL) {
710 panel->cursor = entry;
711 --panel->cursor_idx;
712 }
713 }
714
715 if (panel->page != old_page) {
716 /* We have scrolled. Need to repaint all entries */
717 (void) panel_paint(panel);
718 } else if (panel->cursor != old_cursor) {
719 /* No scrolling, but cursor has moved */
720 panel_entry_paint(old_cursor, old_idx);
721 panel_entry_paint(panel->cursor, panel->cursor_idx);
722
723 (void) gfx_update(gc);
724 }
725}
726
727/** Move one page down.
728 *
729 * @param panel Panel
730 */
731void panel_page_down(panel_t *panel)
732{
733 gfx_context_t *gc = ui_window_get_gc(panel->window);
734 panel_entry_t *old_page;
735 panel_entry_t *old_cursor;
736 size_t old_idx;
737 size_t max_idx;
738 size_t rows;
739 panel_entry_t *entry;
740 size_t i;
741
742 rows = panel_page_size(panel);
743
744 old_page = panel->page;
745 old_cursor = panel->cursor;
746 old_idx = panel->cursor_idx;
747 max_idx = panel->entries_cnt - rows;
748
749 /* Move page by rows entries down (if possible) */
750 for (i = 0; i < rows; i++) {
751 entry = panel_next(panel->page);
752 /* Do not scroll that results in a short page */
753 if (entry != NULL && panel->page_idx < max_idx) {
754 panel->page = entry;
755 ++panel->page_idx;
756 }
757 }
758
759 /* Move cursor by rows entries down (if possible) */
760
761 for (i = 0; i < rows; i++) {
762 entry = panel_next(panel->cursor);
763 if (entry != NULL) {
764 panel->cursor = entry;
765 ++panel->cursor_idx;
766 }
767 }
768
769 if (panel->page != old_page) {
770 /* We have scrolled. Need to repaint all entries */
771 (void) panel_paint(panel);
772 } else if (panel->cursor != old_cursor) {
773 /* No scrolling, but cursor has moved */
774 panel_entry_paint(old_cursor, old_idx);
775 panel_entry_paint(panel->cursor, panel->cursor_idx);
776
777 (void) gfx_update(gc);
778 }
779}
780
781/** @}
782 */
Note: See TracBrowser for help on using the repository browser.