source: mainline/uspace/app/nav/panel.c@ 1455ea1

Last change on this file since 1455ea1 was 1455ea1, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Simple way to prevent file names from overflowing the panel

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