source: mainline/uspace/app/nav/panel.c@ 39ab17c

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

Activating panel using the mouse

  • Property mode set to 100644
File size: 24.0 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 <task.h>
44#include <ui/control.h>
45#include <ui/paint.h>
46#include <ui/resource.h>
47#include <vfs/vfs.h>
48#include <qsort.h>
49#include "panel.h"
50#include "nav.h"
51
52static void panel_ctl_destroy(void *);
53static errno_t panel_ctl_paint(void *);
54static ui_evclaim_t panel_ctl_kbd_event(void *, kbd_event_t *);
55static ui_evclaim_t panel_ctl_pos_event(void *, pos_event_t *);
56
57/** Panel control ops */
58ui_control_ops_t panel_ctl_ops = {
59 .destroy = panel_ctl_destroy,
60 .paint = panel_ctl_paint,
61 .kbd_event = panel_ctl_kbd_event,
62 .pos_event = panel_ctl_pos_event
63};
64
65/** Create panel.
66 *
67 * @param window Containing window
68 * @param active @c true iff panel should be active
69 * @param rpanel Place to store pointer to new panel
70 * @return EOK on success or an error code
71 */
72errno_t panel_create(ui_window_t *window, bool active, panel_t **rpanel)
73{
74 panel_t *panel;
75 errno_t rc;
76
77 panel = calloc(1, sizeof(panel_t));
78 if (panel == NULL)
79 return ENOMEM;
80
81 rc = ui_control_new(&panel_ctl_ops, (void *)panel,
82 &panel->control);
83 if (rc != EOK) {
84 free(panel);
85 return rc;
86 }
87
88 rc = gfx_color_new_ega(0x07, &panel->color);
89 if (rc != EOK)
90 goto error;
91
92 rc = gfx_color_new_ega(0x30, &panel->curs_color);
93 if (rc != EOK)
94 goto error;
95
96 rc = gfx_color_new_ega(0x0f, &panel->act_border_color);
97 if (rc != EOK)
98 goto error;
99
100 rc = gfx_color_new_ega(0x0f, &panel->dir_color);
101 if (rc != EOK)
102 goto error;
103
104 rc = gfx_color_new_ega(0x0a, &panel->svc_color);
105 if (rc != EOK)
106 goto error;
107
108 panel->window = window;
109 list_initialize(&panel->entries);
110 panel->entries_cnt = 0;
111 panel->active = active;
112 *rpanel = panel;
113 return EOK;
114error:
115 if (panel->color != NULL)
116 gfx_color_delete(panel->color);
117 if (panel->curs_color != NULL)
118 gfx_color_delete(panel->curs_color);
119 if (panel->act_border_color != NULL)
120 gfx_color_delete(panel->act_border_color);
121 if (panel->dir_color != NULL)
122 gfx_color_delete(panel->dir_color);
123 if (panel->svc_color != NULL)
124 gfx_color_delete(panel->svc_color);
125 ui_control_delete(panel->control);
126 free(panel);
127 return rc;
128}
129
130/** Destroy panel.
131 *
132 * @param panel Panel
133 */
134void panel_destroy(panel_t *panel)
135{
136 gfx_color_delete(panel->color);
137 gfx_color_delete(panel->curs_color);
138 gfx_color_delete(panel->act_border_color);
139 gfx_color_delete(panel->dir_color);
140 gfx_color_delete(panel->svc_color);
141 panel_clear_entries(panel);
142 ui_control_delete(panel->control);
143 free(panel);
144}
145
146/** Set panel callbacks.
147 *
148 * @param panel Panel
149 * @param cb Callbacks
150 * @param arg Argument to callback functions
151 */
152void panel_set_cb(panel_t *panel, panel_cb_t *cb, void *arg)
153{
154 panel->cb = cb;
155 panel->cb_arg = arg;
156}
157
158/** Paint panel entry.
159 *
160 * @param entry Panel entry
161 * @param entry_idx Entry index (within list of entries)
162 * @return EOK on success or an error code
163 */
164errno_t panel_entry_paint(panel_entry_t *entry, size_t entry_idx)
165{
166 panel_t *panel = entry->panel;
167 gfx_context_t *gc = ui_window_get_gc(panel->window);
168 ui_resource_t *res = ui_window_get_res(panel->window);
169 gfx_font_t *font = ui_resource_get_font(res);
170 gfx_text_fmt_t fmt;
171 gfx_coord2_t pos;
172 gfx_rect_t rect;
173 size_t rows;
174 errno_t rc;
175
176 gfx_text_fmt_init(&fmt);
177 rows = panel_page_size(panel);
178
179 /* Do not display entry outside of current page */
180 if (entry_idx < panel->page_idx ||
181 entry_idx >= panel->page_idx + rows)
182 return EOK;
183
184 pos.x = panel->rect.p0.x + 1;
185 pos.y = panel->rect.p0.y + 1 + entry_idx - panel->page_idx;
186
187 if (entry == panel->cursor && panel->active)
188 fmt.color = panel->curs_color;
189 else if (entry->isdir)
190 fmt.color = panel->dir_color;
191 else if (entry->svc != 0)
192 fmt.color = panel->svc_color;
193 else
194 fmt.color = panel->color;
195
196 /* Draw entry background */
197 rect.p0 = pos;
198 rect.p1.x = panel->rect.p1.x - 1;
199 rect.p1.y = rect.p0.y + 1;
200
201 rc = gfx_set_color(gc, fmt.color);
202 if (rc != EOK)
203 return rc;
204
205 rc = gfx_fill_rect(gc, &rect);
206 if (rc != EOK)
207 return rc;
208
209 rc = gfx_puttext(font, &pos, &fmt, entry->name);
210 if (rc != EOK)
211 return rc;
212
213 return EOK;
214}
215
216/** Paint panel.
217 *
218 * @param panel Panel
219 */
220errno_t panel_paint(panel_t *panel)
221{
222 gfx_context_t *gc = ui_window_get_gc(panel->window);
223 ui_resource_t *res = ui_window_get_res(panel->window);
224 gfx_text_fmt_t fmt;
225 panel_entry_t *entry;
226 ui_box_style_t bstyle;
227 gfx_color_t *bcolor;
228 int i, lines;
229 errno_t rc;
230
231 gfx_text_fmt_init(&fmt);
232
233 rc = gfx_set_color(gc, panel->color);
234 if (rc != EOK)
235 return rc;
236
237 rc = gfx_fill_rect(gc, &panel->rect);
238 if (rc != EOK)
239 return rc;
240
241 if (panel->active) {
242 bstyle = ui_box_double;
243 bcolor = panel->act_border_color;
244 } else {
245 bstyle = ui_box_single;
246 bcolor = panel->color;
247 }
248
249 rc = ui_paint_text_box(res, &panel->rect, bstyle, bcolor);
250 if (rc != EOK)
251 return rc;
252
253 lines = panel_page_size(panel);
254 i = 0;
255
256 entry = panel->page;
257 while (entry != NULL && i < lines) {
258 rc = panel_entry_paint(entry, panel->page_idx + i);
259 if (rc != EOK)
260 return rc;
261
262 ++i;
263 entry = panel_next(entry);
264 }
265
266 rc = gfx_update(gc);
267 if (rc != EOK)
268 return rc;
269
270 return EOK;
271}
272
273/** Handle panel keyboard event.
274 *
275 * @param panel Panel
276 * @param event Keyboard event
277 * @return ui_claimed iff event was claimed
278 */
279ui_evclaim_t panel_kbd_event(panel_t *panel, kbd_event_t *event)
280{
281 if (!panel->active)
282 return ui_unclaimed;
283
284 if (event->type == KEY_PRESS) {
285 if ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
286 switch (event->key) {
287 case KC_UP:
288 panel_cursor_up(panel);
289 break;
290 case KC_DOWN:
291 panel_cursor_down(panel);
292 break;
293 case KC_HOME:
294 panel_cursor_top(panel);
295 break;
296 case KC_END:
297 panel_cursor_bottom(panel);
298 break;
299 case KC_PAGE_UP:
300 panel_page_up(panel);
301 break;
302 case KC_PAGE_DOWN:
303 panel_page_down(panel);
304 break;
305 case KC_ENTER:
306 panel_open(panel, panel->cursor);
307 break;
308 default:
309 break;
310 }
311 }
312 }
313
314 return ui_claimed;
315}
316
317/** Handle panel position event.
318 *
319 * @param panel Panel
320 * @param event Position event
321 * @return ui_claimed iff event was claimed
322 */
323ui_evclaim_t panel_pos_event(panel_t *panel, pos_event_t *event)
324{
325 gfx_coord2_t pos;
326
327 pos.x = event->hpos;
328 pos.y = event->vpos;
329 if (!gfx_pix_inside_rect(&pos, &panel->rect))
330 return ui_unclaimed;
331
332 if (!panel->active) {
333 panel_activate_req(panel);
334 }
335
336 return ui_claimed;
337}
338
339/** Get base control for panel.
340 *
341 * @param panel Panel
342 * @return Base UI control
343 */
344ui_control_t *panel_ctl(panel_t *panel)
345{
346 return panel->control;
347}
348
349/** Set panel rectangle.
350 *
351 * @param panel Panel
352 * @param rect Rectangle
353 */
354void panel_set_rect(panel_t *panel, gfx_rect_t *rect)
355{
356 panel->rect = *rect;
357}
358
359/** Get panel page size.
360 *
361 * @param panel Panel
362 * @return Number of entries that fit in panel at the same time.
363 */
364unsigned panel_page_size(panel_t *panel)
365{
366 return panel->rect.p1.y - panel->rect.p0.y - 2;
367}
368
369/** Determine if panel is active.
370 *
371 * @param panel Panel
372 * @return @c true iff panel is active
373 */
374bool panel_is_active(panel_t *panel)
375{
376 return panel->active;
377}
378
379/** Activate panel.
380 *
381 * @param panel Panel
382 *
383 * @return EOK on success or an error code
384 */
385errno_t panel_activate(panel_t *panel)
386{
387 errno_t rc;
388
389 if (panel->dir != NULL) {
390 rc = vfs_cwd_set(panel->dir);
391 if (rc != EOK)
392 return rc;
393 }
394
395 panel->active = true;
396 (void) panel_paint(panel);
397 return EOK;
398}
399
400/** Deactivate panel.
401 *
402 * @param panel Panel
403 */
404void panel_deactivate(panel_t *panel)
405{
406 panel->active = false;
407 (void) panel_paint(panel);
408}
409
410/** Initialize panel entry attributes.
411 *
412 * @param attr Attributes
413 */
414void panel_entry_attr_init(panel_entry_attr_t *attr)
415{
416 memset(attr, 0, sizeof(*attr));
417}
418
419/** Destroy panel control.
420 *
421 * @param arg Argument (panel_t *)
422 */
423void panel_ctl_destroy(void *arg)
424{
425 panel_t *panel = (panel_t *) arg;
426
427 panel_destroy(panel);
428}
429
430/** Paint panel control.
431 *
432 * @param arg Argument (panel_t *)
433 * @return EOK on success or an error code
434 */
435errno_t panel_ctl_paint(void *arg)
436{
437 panel_t *panel = (panel_t *) arg;
438
439 return panel_paint(panel);
440}
441
442/** Handle panel control keyboard event.
443 *
444 * @param arg Argument (panel_t *)
445 * @param kbd_event Keyboard event
446 * @return @c ui_claimed iff the event is claimed
447 */
448ui_evclaim_t panel_ctl_kbd_event(void *arg, kbd_event_t *event)
449{
450 panel_t *panel = (panel_t *) arg;
451
452 return panel_kbd_event(panel, event);
453}
454
455/** Handle panel control position event.
456 *
457 * @param arg Argument (panel_t *)
458 * @param pos_event Position event
459 * @return @c ui_claimed iff the event is claimed
460 */
461ui_evclaim_t panel_ctl_pos_event(void *arg, pos_event_t *event)
462{
463 panel_t *panel = (panel_t *) arg;
464
465 return panel_pos_event(panel, event);
466}
467
468/** Append new panel entry.
469 *
470 * @param panel Panel
471 * @param attr Entry attributes
472 * @return EOK on success or an error code
473 */
474errno_t panel_entry_append(panel_t *panel, panel_entry_attr_t *attr)
475{
476 panel_entry_t *entry;
477
478 entry = calloc(1, sizeof(panel_entry_t));
479 if (entry == NULL)
480 return ENOMEM;
481
482 entry->panel = panel;
483 entry->name = str_dup(attr->name);
484 if (entry->name == NULL) {
485 free(entry);
486 return ENOMEM;
487 }
488
489 entry->size = attr->size;
490 entry->isdir = attr->isdir;
491 entry->svc = attr->svc;
492 link_initialize(&entry->lentries);
493 list_append(&entry->lentries, &panel->entries);
494 ++panel->entries_cnt;
495 return EOK;
496}
497
498/** Delete panel entry.
499 *
500 * @param entry Panel entry
501 */
502void panel_entry_delete(panel_entry_t *entry)
503{
504 if (entry->panel->cursor == entry)
505 entry->panel->cursor = NULL;
506 if (entry->panel->page == entry)
507 entry->panel->page = NULL;
508
509 list_remove(&entry->lentries);
510 --entry->panel->entries_cnt;
511 free((char *) entry->name);
512 free(entry);
513}
514
515/** Clear panel entry list.
516 *
517 * @param panel Panel
518 */
519void panel_clear_entries(panel_t *panel)
520{
521 panel_entry_t *entry;
522
523 entry = panel_first(panel);
524 while (entry != NULL) {
525 panel_entry_delete(entry);
526 entry = panel_first(panel);
527 }
528}
529
530/** Read directory into panel entry list.
531 *
532 * @param panel Panel
533 * @param dirname Directory path
534 * @return EOK on success or an error code
535 */
536errno_t panel_read_dir(panel_t *panel, const char *dirname)
537{
538 DIR *dir;
539 struct dirent *dirent;
540 vfs_stat_t finfo;
541 char newdir[256];
542 char *ndir = NULL;
543 panel_entry_attr_t attr;
544 panel_entry_t *next;
545 panel_entry_t *prev;
546 char *olddn;
547 size_t pg_size;
548 size_t max_idx;
549 size_t i;
550 errno_t rc;
551
552 rc = vfs_cwd_set(dirname);
553 if (rc != EOK)
554 return rc;
555
556 rc = vfs_cwd_get(newdir, sizeof(newdir));
557 if (rc != EOK)
558 return rc;
559
560 ndir = str_dup(newdir);
561 if (ndir == NULL)
562 return ENOMEM;
563
564 dir = opendir(".");
565 if (dir == NULL) {
566 rc = errno;
567 goto error;
568 }
569
570 if (str_cmp(ndir, "/") != 0) {
571 /* Need to add a synthetic up-dir entry */
572 panel_entry_attr_init(&attr);
573 attr.name = "..";
574 attr.isdir = true;
575
576 rc = panel_entry_append(panel, &attr);
577 if (rc != EOK)
578 goto error;
579 }
580
581 dirent = readdir(dir);
582 while (dirent != NULL) {
583 rc = vfs_stat_path(dirent->d_name, &finfo);
584 if (rc != EOK) {
585 /* Possibly a stale entry */
586 dirent = readdir(dir);
587 continue;
588 }
589
590 panel_entry_attr_init(&attr);
591 attr.name = dirent->d_name;
592 attr.size = finfo.size;
593 attr.isdir = finfo.is_directory;
594 attr.svc = finfo.service;
595
596 rc = panel_entry_append(panel, &attr);
597 if (rc != EOK)
598 goto error;
599
600 dirent = readdir(dir);
601 }
602
603 closedir(dir);
604
605 rc = panel_sort(panel);
606 if (rc != EOK)
607 goto error;
608
609 panel->cursor = panel_first(panel);
610 panel->cursor_idx = 0;
611 panel->page = panel_first(panel);
612 panel->page_idx = 0;
613
614 /* Moving up? */
615 if (str_cmp(dirname, "..") == 0) {
616 /* Get the last component of old path */
617 olddn = str_rchr(panel->dir, '/');
618 if (olddn != NULL && *olddn != '\0') {
619 /* Find corresponding entry */
620 ++olddn;
621 next = panel_next(panel->cursor);
622 while (next != NULL && str_cmp(next->name, olddn) <= 0 &&
623 next->isdir) {
624 panel->cursor = next;
625 ++panel->cursor_idx;
626 next = panel_next(panel->cursor);
627 }
628
629 /* Move page so that cursor is in the center */
630 panel->page = panel->cursor;
631 panel->page_idx = panel->cursor_idx;
632
633 pg_size = panel_page_size(panel);
634
635 for (i = 0; i < pg_size / 2; i++) {
636 prev = panel_prev(panel->page);
637 if (prev == NULL)
638 break;
639
640 panel->page = prev;
641 --panel->page_idx;
642 }
643
644 /* Make sure page is not beyond the end if possible */
645 if (panel->entries_cnt > pg_size)
646 max_idx = panel->entries_cnt - pg_size;
647 else
648 max_idx = 0;
649
650 while (panel->page_idx > 0 && panel->page_idx > max_idx) {
651 prev = panel_prev(panel->page);
652 if (prev == NULL)
653 break;
654
655 panel->page = prev;
656 --panel->page_idx;
657 }
658 }
659 }
660
661 free(panel->dir);
662 panel->dir = ndir;
663
664 return EOK;
665error:
666 (void) vfs_cwd_set(panel->dir);
667 if (ndir != NULL)
668 free(ndir);
669 if (dir != NULL)
670 closedir(dir);
671 return rc;
672}
673
674/** Sort panel entries.
675 *
676 * @param panel Panel
677 * @return EOK on success, ENOMEM if out of memory
678 */
679errno_t panel_sort(panel_t *panel)
680{
681 panel_entry_t **emap;
682 panel_entry_t *entry;
683 size_t i;
684
685 /* Create an array to hold pointer to each entry */
686 emap = calloc(panel->entries_cnt, sizeof(panel_entry_t *));
687 if (emap == NULL)
688 return ENOMEM;
689
690 /* Write entry pointers to array */
691 entry = panel_first(panel);
692 i = 0;
693 while (entry != NULL) {
694 assert(i < panel->entries_cnt);
695 emap[i++] = entry;
696 entry = panel_next(entry);
697 }
698
699 /* Sort the array of pointers */
700 qsort(emap, panel->entries_cnt, sizeof(panel_entry_t *),
701 panel_entry_ptr_cmp);
702
703 /* Unlink entries from entry list */
704 entry = panel_first(panel);
705 while (entry != NULL) {
706 list_remove(&entry->lentries);
707 entry = panel_first(panel);
708 }
709
710 /* Add entries back to entry list sorted */
711 for (i = 0; i < panel->entries_cnt; i++)
712 list_append(&emap[i]->lentries, &panel->entries);
713
714 free(emap);
715 return EOK;
716}
717
718/** Compare two panel entries indirectly referenced by pointers.
719 *
720 * @param pa Pointer to pointer to first entry
721 * @param pb Pointer to pointer to second entry
722 * @return <0, =0, >=0 if pa < b, pa == pb, pa > pb, resp.
723 */
724int panel_entry_ptr_cmp(const void *pa, const void *pb)
725{
726 panel_entry_t *a = *(panel_entry_t **)pa;
727 panel_entry_t *b = *(panel_entry_t **)pb;
728 int dcmp;
729
730 /* Sort directories first */
731 dcmp = b->isdir - a->isdir;
732 if (dcmp != 0)
733 return dcmp;
734
735 return str_cmp(a->name, b->name);
736}
737
738/** Return first panel entry.
739 *
740 * @panel Panel
741 * @return First panel entry or @c NULL if there are no entries
742 */
743panel_entry_t *panel_first(panel_t *panel)
744{
745 link_t *link;
746
747 link = list_first(&panel->entries);
748 if (link == NULL)
749 return NULL;
750
751 return list_get_instance(link, panel_entry_t, lentries);
752}
753
754/** Return last panel entry.
755 *
756 * @panel Panel
757 * @return Last panel entry or @c NULL if there are no entries
758 */
759panel_entry_t *panel_last(panel_t *panel)
760{
761 link_t *link;
762
763 link = list_last(&panel->entries);
764 if (link == NULL)
765 return NULL;
766
767 return list_get_instance(link, panel_entry_t, lentries);
768}
769
770/** Return next panel entry.
771 *
772 * @param cur Current entry
773 * @return Next entry or @c NULL if @a cur is the last entry
774 */
775panel_entry_t *panel_next(panel_entry_t *cur)
776{
777 link_t *link;
778
779 link = list_next(&cur->lentries, &cur->panel->entries);
780 if (link == NULL)
781 return NULL;
782
783 return list_get_instance(link, panel_entry_t, lentries);
784}
785
786/** Return previous panel entry.
787 *
788 * @param cur Current entry
789 * @return Previous entry or @c NULL if @a cur is the first entry
790 */
791panel_entry_t *panel_prev(panel_entry_t *cur)
792{
793 link_t *link;
794
795 link = list_prev(&cur->lentries, &cur->panel->entries);
796 if (link == NULL)
797 return NULL;
798
799 return list_get_instance(link, panel_entry_t, lentries);
800}
801
802/** Move cursor to a new position, possibly scrolling.
803 *
804 * @param panel Panel
805 * @param entry New entry under cursor
806 * @param entry_idx Index of new entry under cursor
807 */
808void panel_cursor_move(panel_t *panel, panel_entry_t *entry, size_t entry_idx)
809{
810 gfx_context_t *gc = ui_window_get_gc(panel->window);
811 panel_entry_t *old_cursor;
812 size_t old_idx;
813 size_t rows;
814 panel_entry_t *e;
815 size_t i;
816
817 rows = panel_page_size(panel);
818
819 old_cursor = panel->cursor;
820 old_idx = panel->cursor_idx;
821
822 panel->cursor = entry;
823 panel->cursor_idx = entry_idx;
824
825 if (entry_idx >= panel->page_idx &&
826 entry_idx < panel->page_idx + rows) {
827 /*
828 * If cursor is still on the current page, we're not
829 * scrolling. Just unpaint old cursor and paint new
830 * cursor.
831 */
832 panel_entry_paint(old_cursor, old_idx);
833 panel_entry_paint(panel->cursor, panel->cursor_idx);
834
835 (void) gfx_update(gc);
836 } else {
837 /*
838 * Need to scroll and update all rows.
839 */
840
841 /* Scrolling up */
842 if (entry_idx < panel->page_idx) {
843 panel->page = entry;
844 panel->page_idx = entry_idx;
845 }
846
847 /* Scrolling down */
848 if (entry_idx >= panel->page_idx + rows) {
849 if (entry_idx >= rows) {
850 panel->page_idx = entry_idx - rows + 1;
851 /* Find first page entry (go back rows - 1) */
852 e = entry;
853 for (i = 0; i < rows - 1; i++) {
854 e = panel_prev(e);
855 }
856
857 /* Should be valid */
858 assert(e != NULL);
859 panel->page = e;
860 } else {
861 panel->page = panel_first(panel);
862 panel->page_idx = 0;
863 }
864 }
865
866 (void) panel_paint(panel);
867 }
868}
869
870/** Move cursor one entry up.
871 *
872 * @param panel Panel
873 */
874void panel_cursor_up(panel_t *panel)
875{
876 panel_entry_t *prev;
877 size_t prev_idx;
878
879 prev = panel_prev(panel->cursor);
880 prev_idx = panel->cursor_idx - 1;
881 if (prev != NULL)
882 panel_cursor_move(panel, prev, prev_idx);
883}
884
885/** Move cursor one entry down.
886 *
887 * @param panel Panel
888 */
889void panel_cursor_down(panel_t *panel)
890{
891 panel_entry_t *next;
892 size_t next_idx;
893
894 next = panel_next(panel->cursor);
895 next_idx = panel->cursor_idx + 1;
896 if (next != NULL)
897 panel_cursor_move(panel, next, next_idx);
898}
899
900/** Move cursor to top.
901 *
902 * @param panel Panel
903 */
904void panel_cursor_top(panel_t *panel)
905{
906 panel_cursor_move(panel, panel_first(panel), 0);
907}
908
909/** Move cursor to bottom.
910 *
911 * @param panel Panel
912 */
913void panel_cursor_bottom(panel_t *panel)
914{
915 panel_cursor_move(panel, panel_last(panel), panel->entries_cnt - 1);
916}
917
918/** Move one page up.
919 *
920 * @param panel Panel
921 */
922void panel_page_up(panel_t *panel)
923{
924 gfx_context_t *gc = ui_window_get_gc(panel->window);
925 panel_entry_t *old_page;
926 panel_entry_t *old_cursor;
927 size_t old_idx;
928 size_t rows;
929 panel_entry_t *entry;
930 size_t i;
931
932 rows = panel_page_size(panel);
933
934 old_page = panel->page;
935 old_cursor = panel->cursor;
936 old_idx = panel->cursor_idx;
937
938 /* Move page by rows entries up (if possible) */
939 for (i = 0; i < rows; i++) {
940 entry = panel_prev(panel->page);
941 if (entry != NULL) {
942 panel->page = entry;
943 --panel->page_idx;
944 }
945 }
946
947 /* Move cursor by rows entries up (if possible) */
948
949 for (i = 0; i < rows; i++) {
950 entry = panel_prev(panel->cursor);
951 if (entry != NULL) {
952 panel->cursor = entry;
953 --panel->cursor_idx;
954 }
955 }
956
957 if (panel->page != old_page) {
958 /* We have scrolled. Need to repaint all entries */
959 (void) panel_paint(panel);
960 } else if (panel->cursor != old_cursor) {
961 /* No scrolling, but cursor has moved */
962 panel_entry_paint(old_cursor, old_idx);
963 panel_entry_paint(panel->cursor, panel->cursor_idx);
964
965 (void) gfx_update(gc);
966 }
967}
968
969/** Move one page down.
970 *
971 * @param panel Panel
972 */
973void panel_page_down(panel_t *panel)
974{
975 gfx_context_t *gc = ui_window_get_gc(panel->window);
976 panel_entry_t *old_page;
977 panel_entry_t *old_cursor;
978 size_t old_idx;
979 size_t max_idx;
980 size_t rows;
981 panel_entry_t *entry;
982 size_t i;
983
984 rows = panel_page_size(panel);
985
986 old_page = panel->page;
987 old_cursor = panel->cursor;
988 old_idx = panel->cursor_idx;
989
990 if (panel->entries_cnt > rows)
991 max_idx = panel->entries_cnt - rows;
992 else
993 max_idx = 0;
994
995 /* Move page by rows entries down (if possible) */
996 for (i = 0; i < rows; i++) {
997 entry = panel_next(panel->page);
998 /* Do not scroll that results in a short page */
999 if (entry != NULL && panel->page_idx < max_idx) {
1000 panel->page = entry;
1001 ++panel->page_idx;
1002 }
1003 }
1004
1005 /* Move cursor by rows entries down (if possible) */
1006
1007 for (i = 0; i < rows; i++) {
1008 entry = panel_next(panel->cursor);
1009 if (entry != NULL) {
1010 panel->cursor = entry;
1011 ++panel->cursor_idx;
1012 }
1013 }
1014
1015 if (panel->page != old_page) {
1016 /* We have scrolled. Need to repaint all entries */
1017 (void) panel_paint(panel);
1018 } else if (panel->cursor != old_cursor) {
1019 /* No scrolling, but cursor has moved */
1020 panel_entry_paint(old_cursor, old_idx);
1021 panel_entry_paint(panel->cursor, panel->cursor_idx);
1022
1023 (void) gfx_update(gc);
1024 }
1025}
1026
1027/** Open panel entry.
1028 *
1029 * Perform Open action on a panel entry (e.g. switch to a subdirectory).
1030 *
1031 * @param panel Panel
1032 * @param entry Panel entry
1033 *
1034 * @return EOK on success or an error code
1035 */
1036errno_t panel_open(panel_t *panel, panel_entry_t *entry)
1037{
1038 if (entry->isdir)
1039 return panel_open_dir(panel, entry);
1040 else if (entry->svc == 0)
1041 return panel_open_file(panel, entry);
1042 else
1043 return EOK;
1044}
1045
1046/** Open panel directory entry.
1047 *
1048 * Perform Open action on a directory entry (i.e. switch to the directory).
1049 *
1050 * @param panel Panel
1051 * @param entry Panel entry (which is a directory)
1052 *
1053 * @return EOK on success or an error code
1054 */
1055errno_t panel_open_dir(panel_t *panel, panel_entry_t *entry)
1056{
1057 gfx_context_t *gc = ui_window_get_gc(panel->window);
1058 char *dirname;
1059 errno_t rc;
1060
1061 assert(entry->isdir);
1062
1063 /*
1064 * Need to copy out name before we free the entry below
1065 * via panel_clear_entries().
1066 */
1067 dirname = str_dup(entry->name);
1068 if (dirname == NULL)
1069 return ENOMEM;
1070
1071 panel_clear_entries(panel);
1072
1073 rc = panel_read_dir(panel, dirname);
1074 if (rc != EOK) {
1075 free(dirname);
1076 return rc;
1077 }
1078
1079 free(dirname);
1080
1081 rc = panel_paint(panel);
1082 if (rc != EOK)
1083 return rc;
1084
1085 return gfx_update(gc);
1086}
1087
1088/** Open panel file entry.
1089 *
1090 * Perform Open action on a file entry (i.e. try running it).
1091 *
1092 * @param panel Panel
1093 * @param entry Panel entry (which is a file)
1094 *
1095 * @return EOK on success or an error code
1096 */
1097errno_t panel_open_file(panel_t *panel, panel_entry_t *entry)
1098{
1099 task_id_t id;
1100 task_wait_t wait;
1101 task_exit_t texit;
1102 int retval;
1103 errno_t rc;
1104 ui_t *ui;
1105
1106 /* It's not a directory */
1107 assert(!entry->isdir);
1108 /* It's not a service-special file */
1109 assert(entry->svc == 0);
1110
1111 ui = ui_window_get_ui(panel->window);
1112
1113 /* Free up and clean console for the child task. */
1114 rc = ui_suspend(ui);
1115 if (rc != EOK)
1116 return rc;
1117
1118 rc = task_spawnl(&id, &wait, entry->name, entry->name, NULL);
1119 if (rc != EOK)
1120 goto error;
1121
1122 rc = task_wait(&wait, &texit, &retval);
1123 if ((rc != EOK) || (texit != TASK_EXIT_NORMAL))
1124 goto error;
1125
1126 /* Resume UI operation */
1127 rc = ui_resume(ui);
1128 if (rc != EOK)
1129 return rc;
1130
1131 (void) ui_paint(ui_window_get_ui(panel->window));
1132 return EOK;
1133error:
1134 (void) ui_resume(ui);
1135 (void) ui_paint(ui_window_get_ui(panel->window));
1136 return rc;
1137}
1138
1139/** Request panel activation.
1140 *
1141 * Call back to request panel activation.
1142 *
1143 * @param panel Panel
1144 */
1145void panel_activate_req(panel_t *panel)
1146{
1147 if (panel->cb != NULL && panel->cb->activate_req != NULL)
1148 panel->cb->activate_req(panel->cb_arg, panel);
1149}
1150
1151/** @}
1152 */
Note: See TracBrowser for help on using the repository browser.