source: mainline/uspace/app/nav/panel.c@ f59212cc

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

Make sure page is not beyond end of list

Need to correct max_idx correctly for short lists (was getting a
negative number and putting it to a size_t). Need to prevent
page going beyond end of list when moving up a dir and seeking
to the entry of directory we just exited (unless the list is short).

  • Property mode set to 100644
File size: 23.0 KB
RevLine 
[6aa85c1]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
[0e80e40]37#include <dirent.h>
[6aa85c1]38#include <errno.h>
[b36ebb42]39#include <gfx/render.h>
[0e80e40]40#include <gfx/text.h>
[6aa85c1]41#include <stdlib.h>
[61784ed]42#include <str.h>
[01e9991]43#include <task.h>
[b36ebb42]44#include <ui/control.h>
45#include <ui/paint.h>
[0e80e40]46#include <ui/resource.h>
[fa792e8]47#include <vfs/vfs.h>
[1eb0fafe]48#include <qsort.h>
[6aa85c1]49#include "panel.h"
50#include "nav.h"
51
[b36ebb42]52static void panel_ctl_destroy(void *);
53static errno_t panel_ctl_paint(void *);
[be1d74c1]54static ui_evclaim_t panel_ctl_kbd_event(void *, kbd_event_t *);
[b36ebb42]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,
[be1d74c1]61 .kbd_event = panel_ctl_kbd_event,
[b36ebb42]62 .pos_event = panel_ctl_pos_event
63};
64
[6aa85c1]65/** Create panel.
66 *
[b36ebb42]67 * @param window Containing window
[692c7f40]68 * @param active @c true iff panel should be active
[6aa85c1]69 * @param rpanel Place to store pointer to new panel
70 * @return EOK on success or an error code
71 */
[692c7f40]72errno_t panel_create(ui_window_t *window, bool active, panel_t **rpanel)
[6aa85c1]73{
74 panel_t *panel;
[b36ebb42]75 errno_t rc;
[6aa85c1]76
77 panel = calloc(1, sizeof(panel_t));
78 if (panel == NULL)
79 return ENOMEM;
80
[b36ebb42]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
[0e80e40]92 rc = gfx_color_new_ega(0x30, &panel->curs_color);
93 if (rc != EOK)
94 goto error;
95
[692c7f40]96 rc = gfx_color_new_ega(0x0f, &panel->act_border_color);
97 if (rc != EOK)
98 goto error;
99
[fa792e8]100 rc = gfx_color_new_ega(0x0f, &panel->dir_color);
101 if (rc != EOK)
102 goto error;
103
[7aeb52cb]104 rc = gfx_color_new_ega(0x0a, &panel->svc_color);
105 if (rc != EOK)
106 goto error;
107
[b36ebb42]108 panel->window = window;
[61784ed]109 list_initialize(&panel->entries);
[be1d74c1]110 panel->entries_cnt = 0;
[692c7f40]111 panel->active = active;
[6aa85c1]112 *rpanel = panel;
113 return EOK;
[b36ebb42]114error:
[0e80e40]115 if (panel->color != NULL)
116 gfx_color_delete(panel->color);
117 if (panel->curs_color != NULL)
118 gfx_color_delete(panel->curs_color);
[fa792e8]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);
[7aeb52cb]123 if (panel->svc_color != NULL)
124 gfx_color_delete(panel->svc_color);
[b36ebb42]125 ui_control_delete(panel->control);
126 free(panel);
127 return rc;
[6aa85c1]128}
129
130/** Destroy panel.
131 *
132 * @param panel Panel
133 */
134void panel_destroy(panel_t *panel)
135{
[0e80e40]136 gfx_color_delete(panel->color);
137 gfx_color_delete(panel->curs_color);
[692c7f40]138 gfx_color_delete(panel->act_border_color);
[fa792e8]139 gfx_color_delete(panel->dir_color);
[7aeb52cb]140 gfx_color_delete(panel->svc_color);
[0e80e40]141 panel_clear_entries(panel);
[b36ebb42]142 ui_control_delete(panel->control);
[6aa85c1]143 free(panel);
144}
145
[be1d74c1]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);
[8c72f533]165 rows = panel_page_size(panel);
[be1d74c1]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
[692c7f40]175 if (entry == panel->cursor && panel->active)
[be1d74c1]176 fmt.color = panel->curs_color;
[fa792e8]177 else if (entry->isdir)
178 fmt.color = panel->dir_color;
[7aeb52cb]179 else if (entry->svc != 0)
180 fmt.color = panel->svc_color;
[be1d74c1]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
[b36ebb42]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);
[0e80e40]212 gfx_text_fmt_t fmt;
213 panel_entry_t *entry;
[692c7f40]214 ui_box_style_t bstyle;
215 gfx_color_t *bcolor;
[be1d74c1]216 int i, lines;
[b36ebb42]217 errno_t rc;
218
[0e80e40]219 gfx_text_fmt_init(&fmt);
220
[b36ebb42]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
[692c7f40]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);
[b36ebb42]238 if (rc != EOK)
239 return rc;
240
[8c72f533]241 lines = panel_page_size(panel);
[be1d74c1]242 i = 0;
[0e80e40]243
244 entry = panel->page;
[be1d74c1]245 while (entry != NULL && i < lines) {
246 rc = panel_entry_paint(entry, panel->page_idx + i);
[0e80e40]247 if (rc != EOK)
248 return rc;
249
[be1d74c1]250 ++i;
[0e80e40]251 entry = panel_next(entry);
252 }
253
[b36ebb42]254 rc = gfx_update(gc);
255 if (rc != EOK)
256 return rc;
257
258 return EOK;
259}
260
[be1d74c1]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{
[692c7f40]269 if (!panel->active)
270 return ui_unclaimed;
271
[be1d74c1]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;
[8c72f533]287 case KC_PAGE_UP:
288 panel_page_up(panel);
289 break;
290 case KC_PAGE_DOWN:
291 panel_page_down(panel);
292 break;
[4fcc2de]293 case KC_ENTER:
294 panel_open(panel, panel->cursor);
295 break;
[be1d74c1]296 default:
297 break;
298 }
299 }
300 }
301
[692c7f40]302 return ui_claimed;
[be1d74c1]303}
304
[b36ebb42]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
[8c72f533]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
[692c7f40]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
[4fcc2de]359 *
360 * @return EOK on success or an error code
[692c7f40]361 */
[4fcc2de]362errno_t panel_activate(panel_t *panel)
[692c7f40]363{
[4fcc2de]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
[692c7f40]372 panel->active = true;
373 (void) panel_paint(panel);
[4fcc2de]374 return EOK;
[692c7f40]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
[7aeb52cb]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
[b36ebb42]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
[be1d74c1]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
[b36ebb42]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
[61784ed]445/** Append new panel entry.
446 *
447 * @param panel Panel
[7aeb52cb]448 * @param attr Entry attributes
[61784ed]449 * @return EOK on success or an error code
450 */
[7aeb52cb]451errno_t panel_entry_append(panel_t *panel, panel_entry_attr_t *attr)
[61784ed]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;
[7aeb52cb]460 entry->name = str_dup(attr->name);
[61784ed]461 if (entry->name == NULL) {
462 free(entry);
463 return ENOMEM;
464 }
465
[7aeb52cb]466 entry->size = attr->size;
467 entry->isdir = attr->isdir;
468 entry->svc = attr->svc;
[61784ed]469 link_initialize(&entry->lentries);
470 list_append(&entry->lentries, &panel->entries);
[be1d74c1]471 ++panel->entries_cnt;
[61784ed]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{
[0e80e40]481 if (entry->panel->cursor == entry)
482 entry->panel->cursor = NULL;
483 if (entry->panel->page == entry)
484 entry->panel->page = NULL;
485
[61784ed]486 list_remove(&entry->lentries);
[be1d74c1]487 --entry->panel->entries_cnt;
[7aeb52cb]488 free((char *) entry->name);
[61784ed]489 free(entry);
490}
491
[0e80e40]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;
[fa792e8]517 vfs_stat_t finfo;
[4fcc2de]518 char newdir[256];
519 char *ndir = NULL;
[7aeb52cb]520 panel_entry_attr_t attr;
[97c3c59]521 panel_entry_t *next;
522 panel_entry_t *prev;
523 char *olddn;
524 size_t pg_size;
[5bbb4453]525 size_t max_idx;
[97c3c59]526 size_t i;
[0e80e40]527 errno_t rc;
528
[4fcc2de]529 rc = vfs_cwd_set(dirname);
530 if (rc != EOK)
531 return rc;
532
533 rc = vfs_cwd_get(newdir, sizeof(newdir));
534 if (rc != EOK)
535 return rc;
536
537 ndir = str_dup(newdir);
538 if (ndir == NULL)
539 return ENOMEM;
540
541 dir = opendir(".");
542 if (dir == NULL) {
543 rc = errno;
544 goto error;
545 }
546
547 if (str_cmp(ndir, "/") != 0) {
548 /* Need to add a synthetic up-dir entry */
[7aeb52cb]549 panel_entry_attr_init(&attr);
550 attr.name = "..";
551 attr.isdir = true;
552
553 rc = panel_entry_append(panel, &attr);
[4fcc2de]554 if (rc != EOK)
555 goto error;
556 }
[0e80e40]557
558 dirent = readdir(dir);
559 while (dirent != NULL) {
[fa792e8]560 rc = vfs_stat_path(dirent->d_name, &finfo);
[4fcc2de]561 if (rc != EOK) {
562 /* Possibly a stale entry */
563 dirent = readdir(dir);
564 continue;
565 }
[fa792e8]566
[7aeb52cb]567 panel_entry_attr_init(&attr);
568 attr.name = dirent->d_name;
569 attr.size = finfo.size;
570 attr.isdir = finfo.is_directory;
571 attr.svc = finfo.service;
572
573 rc = panel_entry_append(panel, &attr);
[0e80e40]574 if (rc != EOK)
575 goto error;
[fa792e8]576
[0e80e40]577 dirent = readdir(dir);
578 }
579
580 closedir(dir);
581
[1eb0fafe]582 rc = panel_sort(panel);
583 if (rc != EOK)
584 goto error;
585
[0e80e40]586 panel->cursor = panel_first(panel);
[be1d74c1]587 panel->cursor_idx = 0;
[0e80e40]588 panel->page = panel_first(panel);
[be1d74c1]589 panel->page_idx = 0;
[97c3c59]590
591 /* Moving up? */
592 if (str_cmp(dirname, "..") == 0) {
593 /* Get the last component of old path */
594 olddn = str_rchr(panel->dir, '/');
595 if (olddn != NULL && *olddn != '\0') {
596 /* Find corresponding entry */
597 ++olddn;
598 next = panel_next(panel->cursor);
599 while (next != NULL && str_cmp(next->name, olddn) <= 0 &&
600 next->isdir) {
601 panel->cursor = next;
602 ++panel->cursor_idx;
603 next = panel_next(panel->cursor);
604 }
605
606 /* Move page so that cursor is in the center */
607 panel->page = panel->cursor;
608 panel->page_idx = panel->cursor_idx;
609
610 pg_size = panel_page_size(panel);
611
612 for (i = 0; i < pg_size / 2; i++) {
613 prev = panel_prev(panel->page);
614 if (prev == NULL)
615 break;
616
617 panel->page = prev;
618 --panel->page_idx;
619 }
[5bbb4453]620
621 /* Make sure page is not beyond the end if possible */
622 if (panel->entries_cnt > pg_size)
623 max_idx = panel->entries_cnt - pg_size;
624 else
625 max_idx = 0;
626
627 while (panel->page_idx > 0 && panel->page_idx > max_idx) {
628 prev = panel_prev(panel->page);
629 if (prev == NULL)
630 break;
631
632 panel->page = prev;
633 --panel->page_idx;
634 }
[97c3c59]635 }
636 }
637
[4fcc2de]638 free(panel->dir);
639 panel->dir = ndir;
[97c3c59]640
[0e80e40]641 return EOK;
642error:
[4fcc2de]643 (void) vfs_cwd_set(panel->dir);
644 if (ndir != NULL)
645 free(ndir);
646 if (dir != NULL)
647 closedir(dir);
[0e80e40]648 return rc;
649}
650
[1eb0fafe]651/** Sort panel entries.
652 *
653 * @param panel Panel
654 * @return EOK on success, ENOMEM if out of memory
655 */
656errno_t panel_sort(panel_t *panel)
657{
658 panel_entry_t **emap;
659 panel_entry_t *entry;
660 size_t i;
661
662 /* Create an array to hold pointer to each entry */
663 emap = calloc(panel->entries_cnt, sizeof(panel_entry_t *));
664 if (emap == NULL)
665 return ENOMEM;
666
667 /* Write entry pointers to array */
668 entry = panel_first(panel);
669 i = 0;
670 while (entry != NULL) {
671 assert(i < panel->entries_cnt);
672 emap[i++] = entry;
673 entry = panel_next(entry);
674 }
675
676 /* Sort the array of pointers */
677 qsort(emap, panel->entries_cnt, sizeof(panel_entry_t *),
678 panel_entry_ptr_cmp);
679
680 /* Unlink entries from entry list */
681 entry = panel_first(panel);
682 while (entry != NULL) {
683 list_remove(&entry->lentries);
684 entry = panel_first(panel);
685 }
686
687 /* Add entries back to entry list sorted */
688 for (i = 0; i < panel->entries_cnt; i++)
689 list_append(&emap[i]->lentries, &panel->entries);
690
691 free(emap);
692 return EOK;
693}
694
695/** Compare two panel entries indirectly referenced by pointers.
696 *
697 * @param pa Pointer to pointer to first entry
698 * @param pb Pointer to pointer to second entry
699 * @return <0, =0, >=0 if pa < b, pa == pb, pa > pb, resp.
700 */
701int panel_entry_ptr_cmp(const void *pa, const void *pb)
702{
703 panel_entry_t *a = *(panel_entry_t **)pa;
704 panel_entry_t *b = *(panel_entry_t **)pb;
[fa792e8]705 int dcmp;
706
707 /* Sort directories first */
708 dcmp = b->isdir - a->isdir;
709 if (dcmp != 0)
710 return dcmp;
[1eb0fafe]711
712 return str_cmp(a->name, b->name);
713}
714
[61784ed]715/** Return first panel entry.
716 *
717 * @panel Panel
718 * @return First panel entry or @c NULL if there are no entries
719 */
720panel_entry_t *panel_first(panel_t *panel)
721{
722 link_t *link;
723
724 link = list_first(&panel->entries);
725 if (link == NULL)
726 return NULL;
727
728 return list_get_instance(link, panel_entry_t, lentries);
729}
730
[be1d74c1]731/** Return last panel entry.
732 *
733 * @panel Panel
734 * @return Last panel entry or @c NULL if there are no entries
735 */
736panel_entry_t *panel_last(panel_t *panel)
737{
738 link_t *link;
739
740 link = list_last(&panel->entries);
741 if (link == NULL)
742 return NULL;
743
744 return list_get_instance(link, panel_entry_t, lentries);
745}
746
[61784ed]747/** Return next panel entry.
748 *
749 * @param cur Current entry
750 * @return Next entry or @c NULL if @a cur is the last entry
751 */
752panel_entry_t *panel_next(panel_entry_t *cur)
753{
754 link_t *link;
755
756 link = list_next(&cur->lentries, &cur->panel->entries);
757 if (link == NULL)
758 return NULL;
759
760 return list_get_instance(link, panel_entry_t, lentries);
761}
[b36ebb42]762
[be1d74c1]763/** Return previous panel entry.
764 *
765 * @param cur Current entry
766 * @return Previous entry or @c NULL if @a cur is the first entry
767 */
768panel_entry_t *panel_prev(panel_entry_t *cur)
769{
770 link_t *link;
771
772 link = list_prev(&cur->lentries, &cur->panel->entries);
773 if (link == NULL)
774 return NULL;
775
776 return list_get_instance(link, panel_entry_t, lentries);
777}
778
[8c72f533]779/** Move cursor to a new position, possibly scrolling.
780 *
781 * @param panel Panel
782 * @param entry New entry under cursor
783 * @param entry_idx Index of new entry under cursor
784 */
[be1d74c1]785void panel_cursor_move(panel_t *panel, panel_entry_t *entry, size_t entry_idx)
786{
787 gfx_context_t *gc = ui_window_get_gc(panel->window);
788 panel_entry_t *old_cursor;
789 size_t old_idx;
790 size_t rows;
791 panel_entry_t *e;
792 size_t i;
793
[8c72f533]794 rows = panel_page_size(panel);
[be1d74c1]795
796 old_cursor = panel->cursor;
797 old_idx = panel->cursor_idx;
798
799 panel->cursor = entry;
800 panel->cursor_idx = entry_idx;
801
802 if (entry_idx >= panel->page_idx &&
803 entry_idx < panel->page_idx + rows) {
804 /*
805 * If cursor is still on the current page, we're not
806 * scrolling. Just unpaint old cursor and paint new
807 * cursor.
808 */
809 panel_entry_paint(old_cursor, old_idx);
810 panel_entry_paint(panel->cursor, panel->cursor_idx);
811
812 (void) gfx_update(gc);
813 } else {
814 /*
815 * Need to scroll and update all rows.
816 */
817
818 /* Scrolling up */
819 if (entry_idx < panel->page_idx) {
820 panel->page = entry;
821 panel->page_idx = entry_idx;
822 }
823
824 /* Scrolling down */
825 if (entry_idx >= panel->page_idx + rows) {
826 if (entry_idx >= rows) {
827 panel->page_idx = entry_idx - rows + 1;
828 /* Find first page entry (go back rows - 1) */
829 e = entry;
830 for (i = 0; i < rows - 1; i++) {
831 e = panel_prev(e);
832 }
833
834 /* Should be valid */
835 assert(e != NULL);
836 panel->page = e;
837 } else {
838 panel->page = panel_first(panel);
839 panel->page_idx = 0;
840 }
841 }
842
843 (void) panel_paint(panel);
844 }
845}
846
847/** Move cursor one entry up.
848 *
849 * @param panel Panel
850 */
851void panel_cursor_up(panel_t *panel)
852{
853 panel_entry_t *prev;
854 size_t prev_idx;
855
856 prev = panel_prev(panel->cursor);
857 prev_idx = panel->cursor_idx - 1;
858 if (prev != NULL)
859 panel_cursor_move(panel, prev, prev_idx);
860}
861
862/** Move cursor one entry down.
863 *
864 * @param panel Panel
865 */
866void panel_cursor_down(panel_t *panel)
867{
868 panel_entry_t *next;
869 size_t next_idx;
870
871 next = panel_next(panel->cursor);
872 next_idx = panel->cursor_idx + 1;
873 if (next != NULL)
874 panel_cursor_move(panel, next, next_idx);
875}
876
877/** Move cursor to top.
878 *
879 * @param panel Panel
880 */
881void panel_cursor_top(panel_t *panel)
882{
883 panel_cursor_move(panel, panel_first(panel), 0);
884}
885
886/** Move cursor to bottom.
887 *
888 * @param panel Panel
889 */
890void panel_cursor_bottom(panel_t *panel)
891{
892 panel_cursor_move(panel, panel_last(panel), panel->entries_cnt - 1);
893}
894
[8c72f533]895/** Move one page up.
896 *
897 * @param panel Panel
898 */
899void panel_page_up(panel_t *panel)
900{
901 gfx_context_t *gc = ui_window_get_gc(panel->window);
902 panel_entry_t *old_page;
903 panel_entry_t *old_cursor;
904 size_t old_idx;
905 size_t rows;
906 panel_entry_t *entry;
907 size_t i;
908
909 rows = panel_page_size(panel);
910
911 old_page = panel->page;
912 old_cursor = panel->cursor;
913 old_idx = panel->cursor_idx;
914
915 /* Move page by rows entries up (if possible) */
916 for (i = 0; i < rows; i++) {
917 entry = panel_prev(panel->page);
918 if (entry != NULL) {
919 panel->page = entry;
920 --panel->page_idx;
921 }
922 }
923
924 /* Move cursor by rows entries up (if possible) */
925
926 for (i = 0; i < rows; i++) {
927 entry = panel_prev(panel->cursor);
928 if (entry != NULL) {
929 panel->cursor = entry;
930 --panel->cursor_idx;
931 }
932 }
933
934 if (panel->page != old_page) {
935 /* We have scrolled. Need to repaint all entries */
936 (void) panel_paint(panel);
937 } else if (panel->cursor != old_cursor) {
938 /* No scrolling, but cursor has moved */
939 panel_entry_paint(old_cursor, old_idx);
940 panel_entry_paint(panel->cursor, panel->cursor_idx);
941
942 (void) gfx_update(gc);
943 }
944}
945
946/** Move one page down.
947 *
948 * @param panel Panel
949 */
950void panel_page_down(panel_t *panel)
951{
952 gfx_context_t *gc = ui_window_get_gc(panel->window);
953 panel_entry_t *old_page;
954 panel_entry_t *old_cursor;
955 size_t old_idx;
956 size_t max_idx;
957 size_t rows;
958 panel_entry_t *entry;
959 size_t i;
960
961 rows = panel_page_size(panel);
962
963 old_page = panel->page;
964 old_cursor = panel->cursor;
965 old_idx = panel->cursor_idx;
[5bbb4453]966
967 if (panel->entries_cnt > rows)
968 max_idx = panel->entries_cnt - rows;
969 else
970 max_idx = 0;
[8c72f533]971
972 /* Move page by rows entries down (if possible) */
973 for (i = 0; i < rows; i++) {
974 entry = panel_next(panel->page);
975 /* Do not scroll that results in a short page */
976 if (entry != NULL && panel->page_idx < max_idx) {
977 panel->page = entry;
978 ++panel->page_idx;
979 }
980 }
981
982 /* Move cursor by rows entries down (if possible) */
983
984 for (i = 0; i < rows; i++) {
985 entry = panel_next(panel->cursor);
986 if (entry != NULL) {
987 panel->cursor = entry;
988 ++panel->cursor_idx;
989 }
990 }
991
992 if (panel->page != old_page) {
993 /* We have scrolled. Need to repaint all entries */
994 (void) panel_paint(panel);
995 } else if (panel->cursor != old_cursor) {
996 /* No scrolling, but cursor has moved */
997 panel_entry_paint(old_cursor, old_idx);
998 panel_entry_paint(panel->cursor, panel->cursor_idx);
999
1000 (void) gfx_update(gc);
1001 }
1002}
1003
[4fcc2de]1004/** Open panel entry.
1005 *
1006 * Perform Open action on a panel entry (e.g. switch to a subdirectory).
1007 *
1008 * @param panel Panel
1009 * @param entry Panel entry
1010 *
1011 * @return EOK on success or an error code
1012 */
1013errno_t panel_open(panel_t *panel, panel_entry_t *entry)
[01e9991]1014{
1015 if (entry->isdir)
1016 return panel_open_dir(panel, entry);
1017 else if (entry->svc == 0)
1018 return panel_open_file(panel, entry);
1019 else
1020 return EOK;
1021}
1022
1023/** Open panel directory entry.
1024 *
1025 * Perform Open action on a directory entry (i.e. switch to the directory).
1026 *
1027 * @param panel Panel
1028 * @param entry Panel entry (which is a directory)
1029 *
1030 * @return EOK on success or an error code
1031 */
1032errno_t panel_open_dir(panel_t *panel, panel_entry_t *entry)
[4fcc2de]1033{
1034 gfx_context_t *gc = ui_window_get_gc(panel->window);
[97c3c59]1035 char *dirname;
[4fcc2de]1036 errno_t rc;
1037
[01e9991]1038 assert(entry->isdir);
[4fcc2de]1039
[97c3c59]1040 /*
1041 * Need to copy out name before we free the entry below
1042 * via panel_clear_entries().
1043 */
1044 dirname = str_dup(entry->name);
1045 if (dirname == NULL)
1046 return ENOMEM;
1047
[4fcc2de]1048 panel_clear_entries(panel);
1049
[97c3c59]1050 rc = panel_read_dir(panel, dirname);
1051 if (rc != EOK) {
1052 free(dirname);
[4fcc2de]1053 return rc;
[97c3c59]1054 }
1055
1056 free(dirname);
[4fcc2de]1057
1058 rc = panel_paint(panel);
[01e9991]1059 if (rc != EOK)
[4fcc2de]1060 return rc;
1061
1062 return gfx_update(gc);
1063}
1064
[01e9991]1065/** Open panel file entry.
1066 *
1067 * Perform Open action on a file entry (i.e. try running it).
1068 *
1069 * @param panel Panel
1070 * @param entry Panel entry (which is a file)
1071 *
1072 * @return EOK on success or an error code
1073 */
1074errno_t panel_open_file(panel_t *panel, panel_entry_t *entry)
1075{
1076 task_id_t id;
1077 task_wait_t wait;
1078 task_exit_t texit;
1079 int retval;
1080 errno_t rc;
1081
1082 /* It's not a directory */
1083 assert(!entry->isdir);
1084 /* It's not a service-special file */
1085 assert(entry->svc == 0);
1086
1087 rc = task_spawnl(&id, &wait, entry->name, entry->name, NULL);
1088 if (rc != EOK)
1089 return rc;
1090
1091 rc = task_wait(&wait, &texit, &retval);
1092 if ((rc != EOK) || (texit != TASK_EXIT_NORMAL))
1093 return rc;
1094
1095 (void) ui_paint(ui_window_get_ui(panel->window));
1096 return EOK;
1097}
1098
[6aa85c1]1099/** @}
1100 */
Note: See TracBrowser for help on using the repository browser.