source: mainline/uspace/app/nav/panel.c@ 97c3c59

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

When moving up a dir, seek to the directory just exited

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