source: mainline/uspace/app/nav/panel.c@ 1aa8c86

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 1aa8c86 was 8edec53, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Support double-click

Needed to open Navigator entries using the mouse.

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