source: mainline/uspace/lib/ui/src/filelist.c@ 453f9645

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 453f9645 was 453f9645, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Add scroll bar to file list control

  • Property mode set to 100644
File size: 36.2 KB
Line 
1/*
2 * Copyright (c) 2022 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 libui
30 * @{
31 */
32/** @file File list.
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 <ui/control.h>
44#include <ui/filelist.h>
45#include <ui/paint.h>
46#include <ui/resource.h>
47#include <ui/scrollbar.h>
48#include <vfs/vfs.h>
49#include <qsort.h>
50#include "../private/filelist.h"
51#include "../private/resource.h"
52
53static void ui_file_list_ctl_destroy(void *);
54static errno_t ui_file_list_ctl_paint(void *);
55static ui_evclaim_t ui_file_list_ctl_kbd_event(void *, kbd_event_t *);
56static ui_evclaim_t ui_file_list_ctl_pos_event(void *, pos_event_t *);
57
58/** File list control ops */
59ui_control_ops_t ui_file_list_ctl_ops = {
60 .destroy = ui_file_list_ctl_destroy,
61 .paint = ui_file_list_ctl_paint,
62 .kbd_event = ui_file_list_ctl_kbd_event,
63 .pos_event = ui_file_list_ctl_pos_event
64};
65
66enum {
67 file_list_entry_hpad = 2,
68 file_list_entry_vpad = 2,
69 file_list_entry_hpad_text = 1,
70 file_list_entry_vpad_text = 0,
71};
72
73static void ui_file_list_scrollbar_up(ui_scrollbar_t *, void *);
74static void ui_file_list_scrollbar_down(ui_scrollbar_t *, void *);
75static void ui_file_list_scrollbar_page_up(ui_scrollbar_t *, void *);
76static void ui_file_list_scrollbar_page_down(ui_scrollbar_t *, void *);
77static void ui_file_list_scrollbar_moved(ui_scrollbar_t *, void *, gfx_coord_t);
78
79/** File list scrollbar callbacks */
80static ui_scrollbar_cb_t ui_file_list_scrollbar_cb = {
81 .up = ui_file_list_scrollbar_up,
82 .down = ui_file_list_scrollbar_down,
83 .page_up = ui_file_list_scrollbar_page_up,
84 .page_down = ui_file_list_scrollbar_page_down,
85 .moved = ui_file_list_scrollbar_moved
86};
87
88/** Create file list.
89 *
90 * @param window Containing window
91 * @param active @c true iff file list should be active
92 * @param rflist Place to store pointer to new file list
93 * @return EOK on success or an error code
94 */
95errno_t ui_file_list_create(ui_window_t *window, bool active,
96 ui_file_list_t **rflist)
97{
98 ui_file_list_t *flist;
99 errno_t rc;
100
101 flist = calloc(1, sizeof(ui_file_list_t));
102 if (flist == NULL)
103 return ENOMEM;
104
105 rc = ui_control_new(&ui_file_list_ctl_ops, (void *)flist,
106 &flist->control);
107 if (rc != EOK) {
108 free(flist);
109 return rc;
110 }
111
112 rc = gfx_color_new_ega(0x0f, &flist->dir_color);
113 if (rc != EOK)
114 goto error;
115
116 rc = gfx_color_new_ega(0x0a, &flist->svc_color);
117 if (rc != EOK)
118 goto error;
119
120 rc = ui_scrollbar_create(ui_window_get_ui(window), window,
121 ui_sbd_vert, &flist->scrollbar);
122 if (rc != EOK)
123 goto error;
124
125 ui_scrollbar_set_cb(flist->scrollbar, &ui_file_list_scrollbar_cb,
126 (void *) flist);
127
128 flist->window = window;
129 list_initialize(&flist->entries);
130 flist->entries_cnt = 0;
131 flist->active = active;
132
133 *rflist = flist;
134 return EOK;
135error:
136 if (flist->dir_color != NULL)
137 gfx_color_delete(flist->dir_color);
138 if (flist->svc_color != NULL)
139 gfx_color_delete(flist->svc_color);
140 ui_control_delete(flist->control);
141 free(flist);
142 return rc;
143}
144
145/** Destroy file list.
146 *
147 * @param flist File list
148 */
149void ui_file_list_destroy(ui_file_list_t *flist)
150{
151 ui_file_list_clear_entries(flist);
152 ui_control_delete(flist->control);
153 free(flist);
154}
155
156/** Set file list callbacks.
157 *
158 * @param flist File list
159 * @param cb Callbacks
160 * @param arg Argument to callback functions
161 */
162void ui_file_list_set_cb(ui_file_list_t *flist, ui_file_list_cb_t *cb, void *arg)
163{
164 flist->cb = cb;
165 flist->cb_arg = arg;
166}
167
168/** Get height of file list entry.
169 *
170 * @param flist File list
171 * @return Entry height in pixels
172 */
173gfx_coord_t ui_file_list_entry_height(ui_file_list_t *flist)
174{
175 ui_resource_t *res;
176 gfx_font_metrics_t metrics;
177 gfx_coord_t height;
178 gfx_coord_t vpad;
179
180 res = ui_window_get_res(flist->window);
181
182 if (res->textmode) {
183 vpad = file_list_entry_vpad_text;
184 } else {
185 vpad = file_list_entry_vpad;
186 }
187
188 /* Normal menu entry */
189 gfx_font_get_metrics(res->font, &metrics);
190 height = metrics.ascent + metrics.descent + 1;
191
192 return height + 2 * vpad;
193}
194
195/** Paint file list entry.
196 *
197 * @param entry File list entry
198 * @param entry_idx Entry index (within list of entries)
199 * @return EOK on success or an error code
200 */
201errno_t ui_file_list_entry_paint(ui_file_list_entry_t *entry, size_t entry_idx)
202{
203 ui_file_list_t *flist = entry->flist;
204 gfx_context_t *gc = ui_window_get_gc(flist->window);
205 ui_resource_t *res = ui_window_get_res(flist->window);
206 gfx_font_t *font = ui_resource_get_font(res);
207 gfx_text_fmt_t fmt;
208 gfx_coord2_t pos;
209 gfx_rect_t rect;
210 gfx_rect_t lrect;
211 gfx_rect_t crect;
212 gfx_color_t *bgcolor;
213 char *caption;
214 gfx_coord_t hpad, vpad;
215 gfx_coord_t line_height;
216 size_t rows;
217 errno_t rc;
218 int rv;
219
220 line_height = ui_file_list_entry_height(flist);
221 ui_file_list_inside_rect(entry->flist, &lrect);
222
223 gfx_text_fmt_init(&fmt);
224 fmt.font = font;
225 rows = ui_file_list_page_size(flist) + 1;
226
227 /* Do not display entry outside of current page */
228 if (entry_idx < flist->page_idx ||
229 entry_idx >= flist->page_idx + rows)
230 return EOK;
231
232 if (res->textmode) {
233 hpad = file_list_entry_hpad_text;
234 vpad = file_list_entry_vpad_text;
235 } else {
236 hpad = file_list_entry_hpad;
237 vpad = file_list_entry_vpad;
238 }
239
240 pos.x = lrect.p0.x;
241 pos.y = lrect.p0.y + line_height * (entry_idx - flist->page_idx);
242
243 if (entry == flist->cursor && flist->active) {
244 fmt.color = res->entry_sel_text_fg_color;
245 bgcolor = res->entry_sel_text_bg_color;
246 } else if (entry->isdir) {
247 if (res->textmode) {
248 fmt.color = flist->dir_color;
249 bgcolor = flist->dir_color;
250 } else {
251 fmt.color = res->entry_fg_color;
252 bgcolor = res->entry_bg_color;
253 }
254 } else if (entry->svc != 0) {
255 if (res->textmode) {
256 fmt.color = flist->svc_color;
257 bgcolor = flist->svc_color;
258 } else {
259 fmt.color = res->entry_fg_color;
260 bgcolor = res->entry_bg_color;
261 }
262 } else {
263 fmt.color = res->entry_fg_color;
264 bgcolor = res->entry_bg_color;
265 }
266
267 /* Draw entry background */
268 rect.p0 = pos;
269 rect.p1.x = lrect.p1.x;
270 rect.p1.y = rect.p0.y + line_height;
271
272 /* Clip to file list interior */
273 gfx_rect_clip(&rect, &lrect, &crect);
274
275 rc = gfx_set_color(gc, bgcolor);
276 if (rc != EOK)
277 return rc;
278
279 rc = gfx_fill_rect(gc, &crect);
280 if (rc != EOK)
281 return rc;
282
283 /*
284 * Make sure name does not overflow the entry rectangle.
285 *
286 * XXX We probably want to measure the text width, and,
287 * if it's too long, use gfx_text_find_pos() to find where
288 * it should be cut off (and append some sort of overflow
289 * marker.
290 */
291 rc = gfx_set_clip_rect(gc, &crect);
292 if (rc != EOK)
293 return rc;
294
295 pos.x += hpad;
296 pos.y += vpad;
297
298 if (res->textmode || !entry->isdir) {
299 rc = gfx_puttext(&pos, &fmt, entry->name);
300 if (rc != EOK) {
301 (void) gfx_set_clip_rect(gc, NULL);
302 return rc;
303 }
304 } else {
305 /*
306 * XXX This is mostly a hack to distinguish directories
307 * util a better solution is available. (E.g. a Size
308 * column where we can put <dir>, an icon.)
309 */
310 rv = asprintf(&caption, "[%s]", entry->name);
311 if (rv < 0) {
312 (void) gfx_set_clip_rect(gc, NULL);
313 return rc;
314 }
315
316 rc = gfx_puttext(&pos, &fmt, caption);
317 if (rc != EOK) {
318 free(caption);
319 (void) gfx_set_clip_rect(gc, NULL);
320 return rc;
321 }
322
323 free(caption);
324 }
325
326 return gfx_set_clip_rect(gc, NULL);
327}
328
329/** Paint file list.
330 *
331 * @param flist File list
332 */
333errno_t ui_file_list_paint(ui_file_list_t *flist)
334{
335 gfx_context_t *gc = ui_window_get_gc(flist->window);
336 ui_resource_t *res = ui_window_get_res(flist->window);
337 ui_file_list_entry_t *entry;
338 int i, lines;
339 errno_t rc;
340
341 rc = gfx_set_color(gc, res->entry_bg_color);
342 if (rc != EOK)
343 return rc;
344
345 rc = gfx_fill_rect(gc, &flist->rect);
346 if (rc != EOK)
347 return rc;
348
349 if (!res->textmode) {
350 rc = ui_paint_inset_frame(res, &flist->rect, NULL);
351 if (rc != EOK)
352 return rc;
353 }
354
355 lines = ui_file_list_page_size(flist) + 1;
356 i = 0;
357
358 entry = flist->page;
359 while (entry != NULL && i < lines) {
360 rc = ui_file_list_entry_paint(entry, flist->page_idx + i);
361 if (rc != EOK)
362 return rc;
363
364 ++i;
365 entry = ui_file_list_next(entry);
366 }
367
368 rc = ui_scrollbar_paint(flist->scrollbar);
369 if (rc != EOK)
370 return rc;
371
372 rc = gfx_update(gc);
373 if (rc != EOK)
374 return rc;
375
376 return EOK;
377}
378
379/** Handle file list keyboard event.
380 *
381 * @param flist File list
382 * @param event Keyboard event
383 * @return ui_claimed iff event was claimed
384 */
385ui_evclaim_t ui_file_list_kbd_event(ui_file_list_t *flist, kbd_event_t *event)
386{
387 if (!flist->active)
388 return ui_unclaimed;
389
390 if (event->type == KEY_PRESS) {
391 if ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
392 switch (event->key) {
393 case KC_UP:
394 ui_file_list_cursor_up(flist);
395 break;
396 case KC_DOWN:
397 ui_file_list_cursor_down(flist);
398 break;
399 case KC_HOME:
400 ui_file_list_cursor_top(flist);
401 break;
402 case KC_END:
403 ui_file_list_cursor_bottom(flist);
404 break;
405 case KC_PAGE_UP:
406 ui_file_list_page_up(flist);
407 break;
408 case KC_PAGE_DOWN:
409 ui_file_list_page_down(flist);
410 break;
411 case KC_ENTER:
412 ui_file_list_open(flist, flist->cursor);
413 break;
414 default:
415 break;
416 }
417 }
418 }
419
420 return ui_claimed;
421}
422
423/** Handle file list position event.
424 *
425 * @param flist File list
426 * @param event Position event
427 * @return ui_claimed iff event was claimed
428 */
429ui_evclaim_t ui_file_list_pos_event(ui_file_list_t *flist, pos_event_t *event)
430{
431 gfx_coord2_t pos;
432 gfx_rect_t irect;
433 ui_file_list_entry_t *entry;
434 gfx_coord_t line_height;
435 size_t entry_idx;
436 ui_evclaim_t claim;
437 int n;
438
439 claim = ui_scrollbar_pos_event(flist->scrollbar, event);
440 if (claim == ui_claimed)
441 return ui_claimed;
442
443 line_height = ui_file_list_entry_height(flist);
444
445 pos.x = event->hpos;
446 pos.y = event->vpos;
447 if (!gfx_pix_inside_rect(&pos, &flist->rect))
448 return ui_unclaimed;
449
450 if (!flist->active && event->type == POS_PRESS)
451 ui_file_list_activate_req(flist);
452
453 if (event->type == POS_PRESS || event->type == POS_DCLICK) {
454 ui_file_list_inside_rect(flist, &irect);
455
456 /* Did we click on one of the entries? */
457 if (gfx_pix_inside_rect(&pos, &irect)) {
458 /* Index within page */
459 n = (pos.y - irect.p0.y) / line_height;
460
461 /* Entry and its index within entire listing */
462 entry = ui_file_list_page_nth_entry(flist, n, &entry_idx);
463 if (entry == NULL)
464 return ui_claimed;
465
466 if (event->type == POS_PRESS) {
467 /* Move to the entry found */
468 ui_file_list_cursor_move(flist, entry, entry_idx);
469 } else {
470 /* event->type == POS_DCLICK */
471 ui_file_list_open(flist, entry);
472 }
473 } else {
474 /* It's in the border. */
475 if (event->type == POS_PRESS) {
476 /* Top or bottom half? */
477 if (pos.y >= (irect.p0.y + irect.p1.y) / 2)
478 ui_file_list_page_down(flist);
479 else
480 ui_file_list_page_up(flist);
481 }
482 }
483 }
484
485 return ui_claimed;
486}
487
488/** Get base control for file list.
489 *
490 * @param flist File list
491 * @return Base UI control
492 */
493ui_control_t *ui_file_list_ctl(ui_file_list_t *flist)
494{
495 return flist->control;
496}
497
498/** Set file list rectangle.
499 *
500 * @param flist File list
501 * @param rect Rectangle
502 */
503void ui_file_list_set_rect(ui_file_list_t *flist, gfx_rect_t *rect)
504{
505 gfx_rect_t srect;
506
507 flist->rect = *rect;
508
509 ui_file_list_scrollbar_rect(flist, &srect);
510 ui_scrollbar_set_rect(flist->scrollbar, &srect);
511}
512
513/** Get file list page size.
514 *
515 * @param flist File list
516 * @return Number of entries that fit in flist at the same time.
517 */
518unsigned ui_file_list_page_size(ui_file_list_t *flist)
519{
520 gfx_coord_t line_height;
521 gfx_rect_t irect;
522
523 line_height = ui_file_list_entry_height(flist);
524 ui_file_list_inside_rect(flist, &irect);
525 return (irect.p1.y - irect.p0.y) / line_height;
526}
527
528/** Get file list interior rectangle.
529 *
530 * @param flist File list
531 * @param irect Place to store interior rectangle
532 */
533void ui_file_list_inside_rect(ui_file_list_t *flist, gfx_rect_t *irect)
534{
535 ui_resource_t *res = ui_window_get_res(flist->window);
536 gfx_rect_t rect;
537 gfx_coord_t width;
538
539 if (res->textmode) {
540 rect = flist->rect;
541 } else {
542 ui_paint_get_inset_frame_inside(res, &flist->rect, &rect);
543 }
544
545 if (res->textmode) {
546 width = 1;
547 } else {
548 width = 23;
549 }
550
551 irect->p0 = rect.p0;
552 irect->p1.x = rect.p1.x - width;
553 irect->p1.y = rect.p1.y;
554}
555
556/** Get file list scrollbar rectangle.
557 *
558 * @param flist File list
559 * @param irect Place to store interior rectangle
560 */
561void ui_file_list_scrollbar_rect(ui_file_list_t *flist, gfx_rect_t *srect)
562{
563 ui_resource_t *res = ui_window_get_res(flist->window);
564 gfx_rect_t rect;
565 gfx_coord_t width;
566
567 if (res->textmode) {
568 rect = flist->rect;
569 } else {
570 ui_paint_get_inset_frame_inside(res, &flist->rect, &rect);
571 }
572
573 if (res->textmode) {
574 width = 1;
575 } else {
576 width = 23;
577 }
578
579 srect->p0.x = rect.p1.x - width;
580 srect->p0.y = rect.p0.y;
581 srect->p1 = rect.p1;
582}
583
584/** Compute new position for file list scrollbar thumb.
585 *
586 * @param flist File list
587 * @return New position
588 */
589gfx_coord_t ui_file_list_scrollbar_pos(ui_file_list_t *flist)
590{
591 size_t entries;
592 size_t pglen;
593 size_t sbar_len;
594
595 entries = list_count(&flist->entries);
596 pglen = ui_file_list_page_size(flist);
597 sbar_len = ui_scrollbar_move_length(flist->scrollbar);
598
599 if (entries > pglen)
600 return sbar_len * flist->page_idx / (entries - pglen);
601 else
602 return 0;
603}
604
605/** Update file list scrollbar position.
606 *
607 * @param flist File list
608 */
609void ui_file_list_scrollbar_update(ui_file_list_t *flist)
610{
611 ui_scrollbar_set_pos(flist->scrollbar,
612 ui_file_list_scrollbar_pos(flist));
613}
614
615/** Determine if file list is active.
616 *
617 * @param flist File list
618 * @return @c true iff file list is active
619 */
620bool ui_file_list_is_active(ui_file_list_t *flist)
621{
622 return flist->active;
623}
624
625/** Activate file list.
626 *
627 * @param flist File list
628 *
629 * @return EOK on success or an error code
630 */
631errno_t ui_file_list_activate(ui_file_list_t *flist)
632{
633 errno_t rc;
634
635 if (flist->dir != NULL) {
636 rc = vfs_cwd_set(flist->dir);
637 if (rc != EOK)
638 return rc;
639 }
640
641 flist->active = true;
642 (void) ui_file_list_paint(flist);
643 return EOK;
644}
645
646/** Deactivate file list.
647 *
648 * @param flist File list
649 */
650void ui_file_list_deactivate(ui_file_list_t *flist)
651{
652 flist->active = false;
653 (void) ui_file_list_paint(flist);
654}
655
656/** Initialize file list entry attributes.
657 *
658 * @param attr Attributes
659 */
660void ui_file_list_entry_attr_init(ui_file_list_entry_attr_t *attr)
661{
662 memset(attr, 0, sizeof(*attr));
663}
664
665/** Destroy file list control.
666 *
667 * @param arg Argument (ui_file_list_t *)
668 */
669void ui_file_list_ctl_destroy(void *arg)
670{
671 ui_file_list_t *flist = (ui_file_list_t *) arg;
672
673 ui_file_list_destroy(flist);
674}
675
676/** Paint file list control.
677 *
678 * @param arg Argument (ui_file_list_t *)
679 * @return EOK on success or an error code
680 */
681errno_t ui_file_list_ctl_paint(void *arg)
682{
683 ui_file_list_t *flist = (ui_file_list_t *) arg;
684
685 return ui_file_list_paint(flist);
686}
687
688/** Handle file list control keyboard event.
689 *
690 * @param arg Argument (ui_file_list_t *)
691 * @param kbd_event Keyboard event
692 * @return @c ui_claimed iff the event is claimed
693 */
694ui_evclaim_t ui_file_list_ctl_kbd_event(void *arg, kbd_event_t *event)
695{
696 ui_file_list_t *flist = (ui_file_list_t *) arg;
697
698 return ui_file_list_kbd_event(flist, event);
699}
700
701/** Handle file list control position event.
702 *
703 * @param arg Argument (ui_file_list_t *)
704 * @param pos_event Position event
705 * @return @c ui_claimed iff the event is claimed
706 */
707ui_evclaim_t ui_file_list_ctl_pos_event(void *arg, pos_event_t *event)
708{
709 ui_file_list_t *flist = (ui_file_list_t *) arg;
710
711 return ui_file_list_pos_event(flist, event);
712}
713
714/** Append new file list entry.
715 *
716 * @param flist File list
717 * @param attr Entry attributes
718 * @return EOK on success or an error code
719 */
720errno_t ui_file_list_entry_append(ui_file_list_t *flist, ui_file_list_entry_attr_t *attr)
721{
722 ui_file_list_entry_t *entry;
723
724 entry = calloc(1, sizeof(ui_file_list_entry_t));
725 if (entry == NULL)
726 return ENOMEM;
727
728 entry->flist = flist;
729 entry->name = str_dup(attr->name);
730 if (entry->name == NULL) {
731 free(entry);
732 return ENOMEM;
733 }
734
735 entry->size = attr->size;
736 entry->isdir = attr->isdir;
737 entry->svc = attr->svc;
738 link_initialize(&entry->lentries);
739 list_append(&entry->lentries, &flist->entries);
740 ++flist->entries_cnt;
741 return EOK;
742}
743
744/** Delete file list entry.
745 *
746 * @param entry File list entry
747 */
748void ui_file_list_entry_delete(ui_file_list_entry_t *entry)
749{
750 if (entry->flist->cursor == entry)
751 entry->flist->cursor = NULL;
752 if (entry->flist->page == entry)
753 entry->flist->page = NULL;
754
755 list_remove(&entry->lentries);
756 --entry->flist->entries_cnt;
757 free((char *) entry->name);
758 free(entry);
759}
760
761/** Clear file list entry list.
762 *
763 * @param flist File list
764 */
765void ui_file_list_clear_entries(ui_file_list_t *flist)
766{
767 ui_file_list_entry_t *entry;
768
769 entry = ui_file_list_first(flist);
770 while (entry != NULL) {
771 ui_file_list_entry_delete(entry);
772 entry = ui_file_list_first(flist);
773 }
774}
775
776/** Read directory into file list entry list.
777 *
778 * @param flist File list
779 * @param dirname Directory path
780 * @return EOK on success or an error code
781 */
782errno_t ui_file_list_read_dir(ui_file_list_t *flist, const char *dirname)
783{
784 DIR *dir;
785 struct dirent *dirent;
786 vfs_stat_t finfo;
787 char newdir[256];
788 char *ndir = NULL;
789 ui_file_list_entry_attr_t attr;
790 ui_file_list_entry_t *next;
791 ui_file_list_entry_t *prev;
792 char *olddn;
793 size_t pg_size;
794 size_t max_idx;
795 size_t i;
796 errno_t rc;
797
798 rc = vfs_cwd_set(dirname);
799 if (rc != EOK)
800 return rc;
801
802 rc = vfs_cwd_get(newdir, sizeof(newdir));
803 if (rc != EOK)
804 return rc;
805
806 ndir = str_dup(newdir);
807 if (ndir == NULL)
808 return ENOMEM;
809
810 dir = opendir(".");
811 if (dir == NULL) {
812 rc = errno;
813 goto error;
814 }
815
816 if (str_cmp(ndir, "/") != 0) {
817 /* Need to add a synthetic up-dir entry */
818 ui_file_list_entry_attr_init(&attr);
819 attr.name = "..";
820 attr.isdir = true;
821
822 rc = ui_file_list_entry_append(flist, &attr);
823 if (rc != EOK)
824 goto error;
825 }
826
827 dirent = readdir(dir);
828 while (dirent != NULL) {
829 rc = vfs_stat_path(dirent->d_name, &finfo);
830 if (rc != EOK) {
831 /* Possibly a stale entry */
832 dirent = readdir(dir);
833 continue;
834 }
835
836 ui_file_list_entry_attr_init(&attr);
837 attr.name = dirent->d_name;
838 attr.size = finfo.size;
839 attr.isdir = finfo.is_directory;
840 attr.svc = finfo.service;
841
842 rc = ui_file_list_entry_append(flist, &attr);
843 if (rc != EOK)
844 goto error;
845
846 dirent = readdir(dir);
847 }
848
849 closedir(dir);
850
851 rc = ui_file_list_sort(flist);
852 if (rc != EOK)
853 goto error;
854
855 flist->cursor = ui_file_list_first(flist);
856 flist->cursor_idx = 0;
857 flist->page = ui_file_list_first(flist);
858 flist->page_idx = 0;
859
860 /* Moving up? */
861 if (str_cmp(dirname, "..") == 0) {
862 /* Get the last component of old path */
863 olddn = str_rchr(flist->dir, '/');
864 if (olddn != NULL && *olddn != '\0') {
865 /* Find corresponding entry */
866 ++olddn;
867 next = ui_file_list_next(flist->cursor);
868 while (next != NULL && str_cmp(next->name, olddn) <= 0 &&
869 next->isdir) {
870 flist->cursor = next;
871 ++flist->cursor_idx;
872 next = ui_file_list_next(flist->cursor);
873 }
874
875 /* Move page so that cursor is in the center */
876 flist->page = flist->cursor;
877 flist->page_idx = flist->cursor_idx;
878
879 pg_size = ui_file_list_page_size(flist);
880
881 for (i = 0; i < pg_size / 2; i++) {
882 prev = ui_file_list_prev(flist->page);
883 if (prev == NULL)
884 break;
885
886 flist->page = prev;
887 --flist->page_idx;
888 }
889
890 /* Make sure page is not beyond the end if possible */
891 if (flist->entries_cnt > pg_size)
892 max_idx = flist->entries_cnt - pg_size;
893 else
894 max_idx = 0;
895
896 while (flist->page_idx > 0 && flist->page_idx > max_idx) {
897 prev = ui_file_list_prev(flist->page);
898 if (prev == NULL)
899 break;
900
901 flist->page = prev;
902 --flist->page_idx;
903 }
904 }
905 }
906
907 free(flist->dir);
908 flist->dir = ndir;
909
910 return EOK;
911error:
912 (void) vfs_cwd_set(flist->dir);
913 if (ndir != NULL)
914 free(ndir);
915 if (dir != NULL)
916 closedir(dir);
917 return rc;
918}
919
920/** Sort file list entries.
921 *
922 * @param flist File list
923 * @return EOK on success, ENOMEM if out of memory
924 */
925errno_t ui_file_list_sort(ui_file_list_t *flist)
926{
927 ui_file_list_entry_t **emap;
928 ui_file_list_entry_t *entry;
929 size_t i;
930
931 /* Create an array to hold pointer to each entry */
932 emap = calloc(flist->entries_cnt, sizeof(ui_file_list_entry_t *));
933 if (emap == NULL)
934 return ENOMEM;
935
936 /* Write entry pointers to array */
937 entry = ui_file_list_first(flist);
938 i = 0;
939 while (entry != NULL) {
940 assert(i < flist->entries_cnt);
941 emap[i++] = entry;
942 entry = ui_file_list_next(entry);
943 }
944
945 /* Sort the array of pointers */
946 qsort(emap, flist->entries_cnt, sizeof(ui_file_list_entry_t *),
947 ui_file_list_entry_ptr_cmp);
948
949 /* Unlink entries from entry list */
950 entry = ui_file_list_first(flist);
951 while (entry != NULL) {
952 list_remove(&entry->lentries);
953 entry = ui_file_list_first(flist);
954 }
955
956 /* Add entries back to entry list sorted */
957 for (i = 0; i < flist->entries_cnt; i++)
958 list_append(&emap[i]->lentries, &flist->entries);
959
960 free(emap);
961 return EOK;
962}
963
964/** Compare two file list entries indirectly referenced by pointers.
965 *
966 * @param pa Pointer to pointer to first entry
967 * @param pb Pointer to pointer to second entry
968 * @return <0, =0, >=0 if pa < b, pa == pb, pa > pb, resp.
969 */
970int ui_file_list_entry_ptr_cmp(const void *pa, const void *pb)
971{
972 ui_file_list_entry_t *a = *(ui_file_list_entry_t **)pa;
973 ui_file_list_entry_t *b = *(ui_file_list_entry_t **)pb;
974 int dcmp;
975
976 /* Sort directories first */
977 dcmp = b->isdir - a->isdir;
978 if (dcmp != 0)
979 return dcmp;
980
981 return str_cmp(a->name, b->name);
982}
983
984/** Return first file list entry.
985 *
986 * @param flist File list
987 * @return First file list entry or @c NULL if there are no entries
988 */
989ui_file_list_entry_t *ui_file_list_first(ui_file_list_t *flist)
990{
991 link_t *link;
992
993 link = list_first(&flist->entries);
994 if (link == NULL)
995 return NULL;
996
997 return list_get_instance(link, ui_file_list_entry_t, lentries);
998}
999
1000/** Return last file list entry.
1001 *
1002 * @param flist File list
1003 * @return Last file list entry or @c NULL if there are no entries
1004 */
1005ui_file_list_entry_t *ui_file_list_last(ui_file_list_t *flist)
1006{
1007 link_t *link;
1008
1009 link = list_last(&flist->entries);
1010 if (link == NULL)
1011 return NULL;
1012
1013 return list_get_instance(link, ui_file_list_entry_t, lentries);
1014}
1015
1016/** Return next file list entry.
1017 *
1018 * @param cur Current entry
1019 * @return Next entry or @c NULL if @a cur is the last entry
1020 */
1021ui_file_list_entry_t *ui_file_list_next(ui_file_list_entry_t *cur)
1022{
1023 link_t *link;
1024
1025 link = list_next(&cur->lentries, &cur->flist->entries);
1026 if (link == NULL)
1027 return NULL;
1028
1029 return list_get_instance(link, ui_file_list_entry_t, lentries);
1030}
1031
1032/** Return previous file list entry.
1033 *
1034 * @param cur Current entry
1035 * @return Previous entry or @c NULL if @a cur is the first entry
1036 */
1037ui_file_list_entry_t *ui_file_list_prev(ui_file_list_entry_t *cur)
1038{
1039 link_t *link;
1040
1041 link = list_prev(&cur->lentries, &cur->flist->entries);
1042 if (link == NULL)
1043 return NULL;
1044
1045 return list_get_instance(link, ui_file_list_entry_t, lentries);
1046}
1047
1048/** Find the n-th entry of the current file list page.
1049 *
1050 * @param flist File list
1051 * @param n Which entry to get (starting from 0)
1052 * @param ridx Place to store index (within listing) of the entry
1053 * @return n-th entry of the page
1054 */
1055ui_file_list_entry_t *ui_file_list_page_nth_entry(ui_file_list_t *flist,
1056 size_t n, size_t *ridx)
1057{
1058 ui_file_list_entry_t *entry;
1059 size_t i;
1060 size_t idx;
1061
1062 assert(n <= ui_file_list_page_size(flist));
1063
1064 entry = flist->page;
1065 if (entry == NULL)
1066 return NULL;
1067
1068 idx = flist->page_idx;
1069 for (i = 0; i < n; i++) {
1070 entry = ui_file_list_next(entry);
1071 if (entry == NULL)
1072 return NULL;
1073
1074 ++idx;
1075 }
1076
1077 *ridx = idx;
1078 return entry;
1079}
1080
1081/** Move cursor to a new position, possibly scrolling.
1082 *
1083 * @param flist File list
1084 * @param entry New entry under cursor
1085 * @param entry_idx Index of new entry under cursor
1086 */
1087void ui_file_list_cursor_move(ui_file_list_t *flist,
1088 ui_file_list_entry_t *entry, size_t entry_idx)
1089{
1090 gfx_context_t *gc = ui_window_get_gc(flist->window);
1091 ui_file_list_entry_t *old_cursor;
1092 size_t old_idx;
1093 size_t rows;
1094 ui_file_list_entry_t *e;
1095 size_t i;
1096
1097 rows = ui_file_list_page_size(flist);
1098
1099 old_cursor = flist->cursor;
1100 old_idx = flist->cursor_idx;
1101
1102 flist->cursor = entry;
1103 flist->cursor_idx = entry_idx;
1104
1105 if (entry_idx >= flist->page_idx &&
1106 entry_idx < flist->page_idx + rows) {
1107 /*
1108 * If cursor is still on the current page, we're not
1109 * scrolling. Just unpaint old cursor and paint new
1110 * cursor.
1111 */
1112 ui_file_list_entry_paint(old_cursor, old_idx);
1113 ui_file_list_entry_paint(flist->cursor, flist->cursor_idx);
1114
1115 (void) gfx_update(gc);
1116 } else {
1117 /*
1118 * Need to scroll and update all rows.
1119 */
1120
1121 /* Scrolling up */
1122 if (entry_idx < flist->page_idx) {
1123 flist->page = entry;
1124 flist->page_idx = entry_idx;
1125 }
1126
1127 /* Scrolling down */
1128 if (entry_idx >= flist->page_idx + rows) {
1129 if (entry_idx >= rows) {
1130 flist->page_idx = entry_idx - rows + 1;
1131 /* Find first page entry (go back rows - 1) */
1132 e = entry;
1133 for (i = 0; i < rows - 1; i++) {
1134 e = ui_file_list_prev(e);
1135 }
1136
1137 /* Should be valid */
1138 assert(e != NULL);
1139 flist->page = e;
1140 } else {
1141 flist->page = ui_file_list_first(flist);
1142 flist->page_idx = 0;
1143 }
1144 }
1145
1146 ui_file_list_scrollbar_update(flist);
1147 (void) ui_file_list_paint(flist);
1148 }
1149}
1150
1151/** Move cursor one entry up.
1152 *
1153 * @param flist File list
1154 */
1155void ui_file_list_cursor_up(ui_file_list_t *flist)
1156{
1157 ui_file_list_entry_t *prev;
1158 size_t prev_idx;
1159
1160 prev = ui_file_list_prev(flist->cursor);
1161 prev_idx = flist->cursor_idx - 1;
1162 if (prev != NULL)
1163 ui_file_list_cursor_move(flist, prev, prev_idx);
1164}
1165
1166/** Move cursor one entry down.
1167 *
1168 * @param flist File list
1169 */
1170void ui_file_list_cursor_down(ui_file_list_t *flist)
1171{
1172 ui_file_list_entry_t *next;
1173 size_t next_idx;
1174
1175 next = ui_file_list_next(flist->cursor);
1176 next_idx = flist->cursor_idx + 1;
1177 if (next != NULL)
1178 ui_file_list_cursor_move(flist, next, next_idx);
1179}
1180
1181/** Move cursor to top.
1182 *
1183 * @param flist File list
1184 */
1185void ui_file_list_cursor_top(ui_file_list_t *flist)
1186{
1187 ui_file_list_cursor_move(flist, ui_file_list_first(flist), 0);
1188}
1189
1190/** Move cursor to bottom.
1191 *
1192 * @param flist File list
1193 */
1194void ui_file_list_cursor_bottom(ui_file_list_t *flist)
1195{
1196 ui_file_list_cursor_move(flist, ui_file_list_last(flist),
1197 flist->entries_cnt - 1);
1198}
1199
1200/** Move cursor one page up.
1201 *
1202 * @param flist File list
1203 */
1204void ui_file_list_page_up(ui_file_list_t *flist)
1205{
1206 gfx_context_t *gc = ui_window_get_gc(flist->window);
1207 ui_file_list_entry_t *old_page;
1208 ui_file_list_entry_t *old_cursor;
1209 size_t old_idx;
1210 size_t rows;
1211 ui_file_list_entry_t *entry;
1212 size_t i;
1213
1214 rows = ui_file_list_page_size(flist);
1215
1216 old_page = flist->page;
1217 old_cursor = flist->cursor;
1218 old_idx = flist->cursor_idx;
1219
1220 /* Move page by rows entries up (if possible) */
1221 for (i = 0; i < rows; i++) {
1222 entry = ui_file_list_prev(flist->page);
1223 if (entry != NULL) {
1224 flist->page = entry;
1225 --flist->page_idx;
1226 }
1227 }
1228
1229 /* Move cursor by rows entries up (if possible) */
1230
1231 for (i = 0; i < rows; i++) {
1232 entry = ui_file_list_prev(flist->cursor);
1233 if (entry != NULL) {
1234 flist->cursor = entry;
1235 --flist->cursor_idx;
1236 }
1237 }
1238
1239 if (flist->page != old_page) {
1240 /* We have scrolled. Need to repaint all entries */
1241 ui_file_list_scrollbar_update(flist);
1242 (void) ui_file_list_paint(flist);
1243 } else if (flist->cursor != old_cursor) {
1244 /* No scrolling, but cursor has moved */
1245 ui_file_list_entry_paint(old_cursor, old_idx);
1246 ui_file_list_entry_paint(flist->cursor, flist->cursor_idx);
1247
1248 (void) gfx_update(gc);
1249 }
1250}
1251
1252/** Move cursor one page down.
1253 *
1254 * @param flist File list
1255 */
1256void ui_file_list_page_down(ui_file_list_t *flist)
1257{
1258 gfx_context_t *gc = ui_window_get_gc(flist->window);
1259 ui_file_list_entry_t *old_page;
1260 ui_file_list_entry_t *old_cursor;
1261 size_t old_idx;
1262 size_t max_idx;
1263 size_t rows;
1264 ui_file_list_entry_t *entry;
1265 size_t i;
1266
1267 rows = ui_file_list_page_size(flist);
1268
1269 old_page = flist->page;
1270 old_cursor = flist->cursor;
1271 old_idx = flist->cursor_idx;
1272
1273 if (flist->entries_cnt > rows)
1274 max_idx = flist->entries_cnt - rows;
1275 else
1276 max_idx = 0;
1277
1278 /* Move page by rows entries down (if possible) */
1279 for (i = 0; i < rows; i++) {
1280 entry = ui_file_list_next(flist->page);
1281 /* Do not scroll that results in a short page */
1282 if (entry != NULL && flist->page_idx < max_idx) {
1283 flist->page = entry;
1284 ++flist->page_idx;
1285 }
1286 }
1287
1288 /* Move cursor by rows entries down (if possible) */
1289
1290 for (i = 0; i < rows; i++) {
1291 entry = ui_file_list_next(flist->cursor);
1292 if (entry != NULL) {
1293 flist->cursor = entry;
1294 ++flist->cursor_idx;
1295 }
1296 }
1297
1298 if (flist->page != old_page) {
1299 /* We have scrolled. Need to repaint all entries */
1300 ui_file_list_scrollbar_update(flist);
1301 (void) ui_file_list_paint(flist);
1302 } else if (flist->cursor != old_cursor) {
1303 /* No scrolling, but cursor has moved */
1304 ui_file_list_entry_paint(old_cursor, old_idx);
1305 ui_file_list_entry_paint(flist->cursor, flist->cursor_idx);
1306
1307 (void) gfx_update(gc);
1308 }
1309}
1310
1311/** Scroll one entry up.
1312 *
1313 * @param flist File list
1314 */
1315void ui_file_list_scroll_up(ui_file_list_t *flist)
1316{
1317 ui_file_list_entry_t *prev;
1318
1319 prev = ui_file_list_prev(flist->page);
1320 if (prev == NULL)
1321 return;
1322
1323 flist->page = prev;
1324 assert(flist->page_idx > 0);
1325 --flist->page_idx;
1326
1327 ui_file_list_scrollbar_update(flist);
1328 (void) ui_file_list_paint(flist);
1329}
1330
1331/** Scroll one entry down.
1332 *
1333 * @param flist File list
1334 */
1335void ui_file_list_scroll_down(ui_file_list_t *flist)
1336{
1337 ui_file_list_entry_t *next;
1338 ui_file_list_entry_t *pgend;
1339 size_t i;
1340 size_t rows;
1341
1342 next = ui_file_list_next(flist->page);
1343 if (next == NULL)
1344 return;
1345
1346 rows = ui_file_list_page_size(flist);
1347
1348 /* Find last page entry */
1349 pgend = flist->page;
1350 for (i = 0; i < rows && pgend != NULL; i++) {
1351 pgend = ui_file_list_next(pgend);
1352 }
1353
1354 /* Scroll down by one entry, if the page remains full */
1355 if (pgend != NULL) {
1356 flist->page = next;
1357 ++flist->page_idx;
1358 }
1359
1360 ui_file_list_scrollbar_update(flist);
1361 (void) ui_file_list_paint(flist);
1362}
1363
1364/** Scroll one page up.
1365 *
1366 * @param flist File list
1367 */
1368void ui_file_list_scroll_page_up(ui_file_list_t *flist)
1369{
1370 ui_file_list_entry_t *prev;
1371 size_t i;
1372 size_t rows;
1373
1374 prev = ui_file_list_prev(flist->page);
1375 if (prev == NULL)
1376 return;
1377
1378 rows = ui_file_list_page_size(flist);
1379
1380 for (i = 0; i < rows && prev != NULL; i++) {
1381 flist->page = prev;
1382 assert(flist->page_idx > 0);
1383 --flist->page_idx;
1384 prev = ui_file_list_prev(prev);
1385 }
1386
1387 ui_file_list_scrollbar_update(flist);
1388 (void) ui_file_list_paint(flist);
1389}
1390
1391/** Scroll one page down.
1392 *
1393 * @param flist File list
1394 */
1395void ui_file_list_scroll_page_down(ui_file_list_t *flist)
1396{
1397 ui_file_list_entry_t *next;
1398 ui_file_list_entry_t *pgend;
1399 size_t i;
1400 size_t rows;
1401
1402 next = ui_file_list_next(flist->page);
1403 if (next == NULL)
1404 return;
1405
1406 rows = ui_file_list_page_size(flist);
1407
1408 /* Find last page entry */
1409 pgend = flist->page;
1410 for (i = 0; i < rows && pgend != NULL; i++) {
1411 pgend = ui_file_list_next(pgend);
1412 }
1413
1414 /* Scroll by up to 'rows' entries, keeping the page full */
1415 for (i = 0; i < rows && pgend != NULL; i++) {
1416 flist->page = next;
1417 ++flist->page_idx;
1418 next = ui_file_list_next(next);
1419 pgend = ui_file_list_next(pgend);
1420 }
1421
1422 ui_file_list_scrollbar_update(flist);
1423 (void) ui_file_list_paint(flist);
1424}
1425
1426/** Scroll to a specific entry
1427 *
1428 * @param flist File list
1429 * @param page_idx New index of first entry on the page
1430 */
1431void ui_file_list_scroll_pos(ui_file_list_t *flist, size_t page_idx)
1432{
1433 ui_file_list_entry_t *entry;
1434 size_t i;
1435
1436 entry = ui_file_list_first(flist);
1437 for (i = 0; i < page_idx; i++) {
1438 entry = ui_file_list_next(entry);
1439 assert(entry != NULL);
1440 }
1441
1442 flist->page = entry;
1443 flist->page_idx = page_idx;
1444
1445 (void) ui_file_list_paint(flist);
1446}
1447
1448/** Open file list entry.
1449 *
1450 * Perform Open action on a file list entry (e.g. switch to a subdirectory).
1451 *
1452 * @param flist File list
1453 * @param entry File list entry
1454 *
1455 * @return EOK on success or an error code
1456 */
1457errno_t ui_file_list_open(ui_file_list_t *flist, ui_file_list_entry_t *entry)
1458{
1459 if (entry->isdir)
1460 return ui_file_list_open_dir(flist, entry);
1461 else if (entry->svc == 0)
1462 return ui_file_list_open_file(flist, entry);
1463 else
1464 return EOK;
1465}
1466
1467/** Open file list directory entry.
1468 *
1469 * Perform Open action on a directory entry (i.e. switch to the directory).
1470 *
1471 * @param flist File list
1472 * @param entry File list entry (which is a directory)
1473 *
1474 * @return EOK on success or an error code
1475 */
1476errno_t ui_file_list_open_dir(ui_file_list_t *flist,
1477 ui_file_list_entry_t *entry)
1478{
1479 gfx_context_t *gc = ui_window_get_gc(flist->window);
1480 char *dirname;
1481 errno_t rc;
1482
1483 assert(entry->isdir);
1484
1485 /*
1486 * Need to copy out name before we free the entry below
1487 * via ui_file_list_clear_entries().
1488 */
1489 dirname = str_dup(entry->name);
1490 if (dirname == NULL)
1491 return ENOMEM;
1492
1493 ui_file_list_clear_entries(flist);
1494
1495 rc = ui_file_list_read_dir(flist, dirname);
1496 if (rc != EOK) {
1497 free(dirname);
1498 return rc;
1499 }
1500
1501 free(dirname);
1502
1503 rc = ui_file_list_paint(flist);
1504 if (rc != EOK)
1505 return rc;
1506
1507 return gfx_update(gc);
1508}
1509
1510/** Open file list file entry.
1511 *
1512 * Perform Open action on a file entry (i.e. try running it).
1513 *
1514 * @param flist File list
1515 * @param entry File list entry (which is a file)
1516 *
1517 * @return EOK on success or an error code
1518 */
1519errno_t ui_file_list_open_file(ui_file_list_t *flist,
1520 ui_file_list_entry_t *entry)
1521{
1522 ui_file_list_selected(flist, entry->name);
1523 return EOK;
1524}
1525
1526/** Request file list activation.
1527 *
1528 * Call back to request file list activation.
1529 *
1530 * @param flist File list
1531 */
1532void ui_file_list_activate_req(ui_file_list_t *flist)
1533{
1534 if (flist->cb != NULL && flist->cb->activate_req != NULL)
1535 flist->cb->activate_req(flist, flist->cb_arg);
1536}
1537
1538/** Call back when a file is selected.
1539 *
1540 * @param flist File list
1541 * @param fname File name
1542 */
1543void ui_file_list_selected(ui_file_list_t *flist, const char *fname)
1544{
1545 if (flist->cb != NULL && flist->cb->selected != NULL)
1546 flist->cb->selected(flist, flist->cb_arg, fname);
1547}
1548
1549/** File list scrollbar up button pressed.
1550 *
1551 * @param scrollbar Scrollbar
1552 * @param arg Argument (ui_file_list_t *)
1553 */
1554static void ui_file_list_scrollbar_up(ui_scrollbar_t *scrollbar, void *arg)
1555{
1556 ui_file_list_t *flist = (ui_file_list_t *)arg;
1557 ui_file_list_scroll_up(flist);
1558}
1559
1560/** File list scrollbar down button pressed.
1561 *
1562 * @param scrollbar Scrollbar
1563 * @param arg Argument (ui_file_list_t *)
1564 */
1565static void ui_file_list_scrollbar_down(ui_scrollbar_t *scrollbar, void *arg)
1566{
1567 ui_file_list_t *flist = (ui_file_list_t *)arg;
1568 ui_file_list_scroll_down(flist);
1569}
1570
1571/** File list scrollbar page up pressed.
1572 *
1573 * @param scrollbar Scrollbar
1574 * @param arg Argument (ui_file_list_t *)
1575 */
1576static void ui_file_list_scrollbar_page_up(ui_scrollbar_t *scrollbar, void *arg)
1577{
1578 ui_file_list_t *flist = (ui_file_list_t *)arg;
1579 ui_file_list_scroll_page_up(flist);
1580}
1581
1582/** File list scrollbar page down pressed.
1583 *
1584 * @param scrollbar Scrollbar
1585 * @param arg Argument (ui_file_list_t *)
1586 */
1587static void ui_file_list_scrollbar_page_down(ui_scrollbar_t *scrollbar,
1588 void *arg)
1589{
1590 ui_file_list_t *flist = (ui_file_list_t *)arg;
1591 ui_file_list_scroll_page_down(flist);
1592}
1593
1594/** File list scrollbar moved.
1595 *
1596 * @param scrollbar Scrollbar
1597 * @param arg Argument (ui_file_list_t *)
1598 */
1599static void ui_file_list_scrollbar_moved(ui_scrollbar_t *scrollbar, void *arg,
1600 gfx_coord_t pos)
1601{
1602 ui_file_list_t *flist = (ui_file_list_t *)arg;
1603 size_t entries;
1604 size_t pglen;
1605 size_t sbar_len;
1606 size_t pgstart;
1607
1608 entries = list_count(&flist->entries);
1609 pglen = ui_file_list_page_size(flist);
1610 sbar_len = ui_scrollbar_move_length(flist->scrollbar);
1611
1612 if (entries > pglen)
1613 pgstart = (entries - pglen) * pos / (sbar_len - 1);
1614 else
1615 pgstart = 0;
1616
1617 ui_file_list_scroll_pos(flist, pgstart);
1618}
1619
1620/** @}
1621 */
Note: See TracBrowser for help on using the repository browser.