source: mainline/uspace/lib/ui/src/filelist.c@ 2a230b6

Last change on this file since 2a230b6 was 7cf5ddb, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Generic UI list control

Derived from file list, now file list is based on UI list.
Whew!

  • Property mode set to 100644
File size: 16.0 KB
Line 
1/*
2 * Copyright (c) 2023 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 control.
33 *
34 * Displays a file listing.
35 */
36
37#include <dirent.h>
38#include <errno.h>
39#include <stdlib.h>
40#include <str.h>
41#include <ui/control.h>
42#include <ui/filelist.h>
43#include <ui/list.h>
44#include <ui/resource.h>
45#include <vfs/vfs.h>
46#include <qsort.h>
47#include "../private/filelist.h"
48#include "../private/resource.h"
49
50static void ui_file_list_ctl_destroy(void *);
51static errno_t ui_file_list_ctl_paint(void *);
52static ui_evclaim_t ui_file_list_ctl_kbd_event(void *, kbd_event_t *);
53static ui_evclaim_t ui_file_list_ctl_pos_event(void *, pos_event_t *);
54
55/** List control ops */
56ui_control_ops_t ui_file_list_ctl_ops = {
57 .destroy = ui_file_list_ctl_destroy,
58 .paint = ui_file_list_ctl_paint,
59 .kbd_event = ui_file_list_ctl_kbd_event,
60 .pos_event = ui_file_list_ctl_pos_event
61};
62
63static void ui_file_list_list_activate_req(ui_list_t *, void *);
64static void ui_file_list_list_selected(ui_list_entry_t *, void *);
65
66/** List callbacks */
67ui_list_cb_t ui_file_list_list_cb = {
68 .activate_req = ui_file_list_list_activate_req,
69 .selected = ui_file_list_list_selected,
70 .compare = ui_file_list_list_compare,
71};
72
73/** Create file list.
74 *
75 * @param window Containing window
76 * @param active @c true iff file list should be active
77 * @param rflist Place to store pointer to new file list
78 * @return EOK on success or an error code
79 */
80errno_t ui_file_list_create(ui_window_t *window, bool active,
81 ui_file_list_t **rflist)
82{
83 ui_file_list_t *flist;
84 errno_t rc;
85
86 flist = calloc(1, sizeof(ui_file_list_t));
87 if (flist == NULL)
88 return ENOMEM;
89
90 rc = ui_control_new(&ui_file_list_ctl_ops, (void *)flist,
91 &flist->control);
92 if (rc != EOK)
93 goto error;
94
95 rc = gfx_color_new_ega(0x0f, &flist->dir_color);
96 if (rc != EOK)
97 goto error;
98
99 rc = gfx_color_new_ega(0x0a, &flist->svc_color);
100 if (rc != EOK)
101 goto error;
102
103 rc = ui_list_create(window, active, &flist->list);
104 if (rc != EOK)
105 goto error;
106
107 ui_list_set_cb(flist->list, &ui_file_list_list_cb, (void *)flist);
108
109 flist->window = window;
110 *rflist = flist;
111 return EOK;
112error:
113 ui_control_delete(flist->control);
114 if (flist->dir_color != NULL)
115 gfx_color_delete(flist->dir_color);
116 if (flist->svc_color != NULL)
117 gfx_color_delete(flist->svc_color);
118 free(flist);
119 return rc;
120}
121
122/** Destroy file list.
123 *
124 * @param flist File list
125 */
126void ui_file_list_destroy(ui_file_list_t *flist)
127{
128 ui_file_list_clear_entries(flist);
129 ui_list_destroy(flist->list);
130 ui_control_delete(flist->control);
131 free(flist);
132}
133
134/** Set file list callbacks.
135 *
136 * @param flist File list
137 * @param cb Callbacks
138 * @param arg Argument to callback functions
139 */
140void ui_file_list_set_cb(ui_file_list_t *flist, ui_file_list_cb_t *cb, void *arg)
141{
142 flist->cb = cb;
143 flist->cb_arg = arg;
144}
145
146/** Get base control for file list.
147 *
148 * @param flist File list
149 * @return Base UI control
150 */
151ui_control_t *ui_file_list_ctl(ui_file_list_t *flist)
152{
153 return flist->control;
154}
155
156/** Set file list rectangle.
157 *
158 * @param flist File list
159 * @param rect Rectangle
160 */
161void ui_file_list_set_rect(ui_file_list_t *flist, gfx_rect_t *rect)
162{
163 ui_list_set_rect(flist->list, rect);
164}
165
166/** Determine if file list is active.
167 *
168 * @param flist File list
169 * @return @c true iff file list is active
170 */
171bool ui_file_list_is_active(ui_file_list_t *flist)
172{
173 return ui_list_is_active(flist->list);
174}
175
176/** Activate file list.
177 *
178 * @param flist File list
179 *
180 * @return EOK on success or an error code
181 */
182errno_t ui_file_list_activate(ui_file_list_t *flist)
183{
184 errno_t rc;
185
186 if (flist->dir != NULL) {
187 rc = vfs_cwd_set(flist->dir);
188 if (rc != EOK)
189 return rc;
190 }
191
192 return ui_list_activate(flist->list);
193}
194
195/** Deactivate file list.
196 *
197 * @param flist File list
198 */
199void ui_file_list_deactivate(ui_file_list_t *flist)
200{
201 ui_list_deactivate(flist->list);
202}
203
204/** Initialize file list entry attributes.
205 *
206 * @param attr Attributes
207 */
208void ui_file_list_entry_attr_init(ui_file_list_entry_attr_t *attr)
209{
210 memset(attr, 0, sizeof(*attr));
211}
212
213/** Append new file list entry.
214 *
215 * @param flist File list
216 * @param attr Entry attributes
217 * @return EOK on success or an error code
218 */
219errno_t ui_file_list_entry_append(ui_file_list_t *flist, ui_file_list_entry_attr_t *attr)
220{
221 ui_file_list_entry_t *entry;
222 ui_list_entry_attr_t lattr;
223 ui_list_entry_t *lentry;
224 ui_resource_t *res;
225 char *caption;
226 errno_t rc;
227 int rv;
228
229 res = ui_window_get_res(flist->window);
230
231 entry = calloc(1, sizeof(ui_file_list_entry_t));
232 if (entry == NULL)
233 return ENOMEM;
234
235 entry->flist = flist;
236 entry->name = str_dup(attr->name);
237 if (entry->name == NULL) {
238 free(entry);
239 return ENOMEM;
240 }
241
242 entry->size = attr->size;
243 entry->isdir = attr->isdir;
244 entry->svc = attr->svc;
245
246 if (attr->isdir && !res->textmode) {
247 rv = asprintf(&caption, "[%s]", attr->name);
248 if (rv < 0)
249 caption = NULL;
250 } else {
251 caption = str_dup(attr->name);
252 }
253
254 if (caption == NULL) {
255 free(entry->name);
256 free(entry);
257 return ENOMEM;
258 }
259
260 lattr.caption = caption;
261 lattr.arg = (void *)entry;
262 lattr.color = NULL;
263 lattr.bgcolor = NULL;
264
265 if (res->textmode) {
266 if (attr->isdir) {
267 lattr.color = flist->dir_color;
268 lattr.bgcolor = flist->dir_color;
269 } else if (attr->svc != 0) {
270 lattr.color = flist->svc_color;
271 lattr.bgcolor = flist->svc_color;
272 }
273 }
274
275 rc = ui_list_entry_append(flist->list, &lattr, &lentry);
276 if (rc != EOK) {
277 free(caption);
278 free(entry->name);
279 free(entry);
280 return rc;
281 }
282
283 free(caption);
284 entry->entry = lentry;
285 return EOK;
286}
287
288/** Delete file list entry.
289 *
290 * @param entry File list entry
291 */
292void ui_file_list_entry_delete(ui_file_list_entry_t *entry)
293{
294 ui_list_entry_delete(entry->entry);
295 free(entry->name);
296 free(entry);
297}
298
299/** Clear file list entry list.
300 *
301 * @param flist File list
302 */
303void ui_file_list_clear_entries(ui_file_list_t *flist)
304{
305 ui_file_list_entry_t *entry;
306
307 entry = ui_file_list_first(flist);
308 while (entry != NULL) {
309 ui_file_list_entry_delete(entry);
310 entry = ui_file_list_first(flist);
311 }
312}
313
314/** Read directory into file list entry list.
315 *
316 * @param flist File list
317 * @param dirname Directory path
318 * @return EOK on success or an error code
319 */
320errno_t ui_file_list_read_dir(ui_file_list_t *flist, const char *dirname)
321{
322 DIR *dir;
323 struct dirent *dirent;
324 vfs_stat_t finfo;
325 char newdir[256];
326 char *ndir = NULL;
327 ui_file_list_entry_attr_t attr;
328 ui_file_list_entry_t *cur;
329 ui_file_list_entry_t *next;
330 char *olddn;
331 errno_t rc;
332
333 rc = vfs_cwd_set(dirname);
334 if (rc != EOK)
335 return rc;
336
337 rc = vfs_cwd_get(newdir, sizeof(newdir));
338 if (rc != EOK)
339 return rc;
340
341 ndir = str_dup(newdir);
342 if (ndir == NULL)
343 return ENOMEM;
344
345 dir = opendir(".");
346 if (dir == NULL) {
347 rc = errno;
348 goto error;
349 }
350
351 if (str_cmp(ndir, "/") != 0) {
352 /* Need to add a synthetic up-dir entry */
353 ui_file_list_entry_attr_init(&attr);
354 attr.name = "..";
355 attr.isdir = true;
356
357 rc = ui_file_list_entry_append(flist, &attr);
358 if (rc != EOK)
359 goto error;
360 }
361
362 dirent = readdir(dir);
363 while (dirent != NULL) {
364 rc = vfs_stat_path(dirent->d_name, &finfo);
365 if (rc != EOK) {
366 /* Possibly a stale entry */
367 dirent = readdir(dir);
368 continue;
369 }
370
371 ui_file_list_entry_attr_init(&attr);
372 attr.name = dirent->d_name;
373 attr.size = finfo.size;
374 attr.isdir = finfo.is_directory;
375 attr.svc = finfo.service;
376
377 rc = ui_file_list_entry_append(flist, &attr);
378 if (rc != EOK)
379 goto error;
380
381 dirent = readdir(dir);
382 }
383
384 closedir(dir);
385
386 rc = ui_file_list_sort(flist);
387 if (rc != EOK)
388 goto error;
389
390 /* Moving up? */
391 if (str_cmp(dirname, "..") == 0) {
392 /* Get the last component of old path */
393 olddn = str_rchr(flist->dir, '/');
394 if (olddn != NULL && *olddn != '\0') {
395 /* Find corresponding entry */
396 ++olddn;
397 cur = ui_file_list_first(flist);
398 next = ui_file_list_next(cur);
399 while (next != NULL && str_cmp(next->name, olddn) <= 0 &&
400 next->isdir) {
401 cur = next;
402 next = ui_file_list_next(cur);
403 }
404
405 /* Center on the entry */
406 ui_list_cursor_center(flist->list, cur->entry);
407 }
408 }
409
410 free(flist->dir);
411 flist->dir = ndir;
412
413 return EOK;
414error:
415 (void) vfs_cwd_set(flist->dir);
416 if (ndir != NULL)
417 free(ndir);
418 if (dir != NULL)
419 closedir(dir);
420 return rc;
421}
422
423/** Sort file list entries.
424 *
425 * @param flist File list
426 * @return EOK on success, ENOMEM if out of memory
427 */
428errno_t ui_file_list_sort(ui_file_list_t *flist)
429{
430 return ui_list_sort(flist->list);
431}
432
433/** Compare two list entries within file list entries.
434 *
435 * @param ea First UI list entry
436 * @param eb Second UI list entry
437 * @return <0, =0, >=0 if a < b, a == b, a > b, resp.
438 */
439int ui_file_list_list_compare(ui_list_entry_t *ea, ui_list_entry_t *eb)
440{
441 ui_file_list_entry_t *a;
442 ui_file_list_entry_t *b;
443 int dcmp;
444
445 a = (ui_file_list_entry_t *)ui_list_entry_get_arg(ea);
446 b = (ui_file_list_entry_t *)ui_list_entry_get_arg(eb);
447
448 /* Sort directories first */
449 dcmp = b->isdir - a->isdir;
450 if (dcmp != 0)
451 return dcmp;
452
453 return str_cmp(a->name, b->name);
454}
455
456/** Return first file list entry.
457 *
458 * @param flist File list
459 * @return First file list entry or @c NULL if there are no entries
460 */
461ui_file_list_entry_t *ui_file_list_first(ui_file_list_t *flist)
462{
463 ui_list_entry_t *lentry;
464
465 lentry = ui_list_first(flist->list);
466 if (lentry == NULL)
467 return NULL;
468
469 return (ui_file_list_entry_t *)ui_list_entry_get_arg(lentry);
470}
471
472/** Return last file list entry.
473 *
474 * @param flist File list
475 * @return Last file list entry or @c NULL if there are no entries
476 */
477ui_file_list_entry_t *ui_file_list_last(ui_file_list_t *flist)
478{
479 ui_list_entry_t *lentry;
480
481 lentry = ui_list_last(flist->list);
482 if (lentry == NULL)
483 return NULL;
484
485 return (ui_file_list_entry_t *)ui_list_entry_get_arg(lentry);
486}
487
488/** Return next file list entry.
489 *
490 * @param cur Current entry
491 * @return Next entry or @c NULL if @a cur is the last entry
492 */
493ui_file_list_entry_t *ui_file_list_next(ui_file_list_entry_t *cur)
494{
495 ui_list_entry_t *lentry;
496
497 lentry = ui_list_next(cur->entry);
498 if (lentry == NULL)
499 return NULL;
500
501 return (ui_file_list_entry_t *)ui_list_entry_get_arg(lentry);
502}
503
504/** Return previous file list entry.
505 *
506 * @param cur Current entry
507 * @return Previous entry or @c NULL if @a cur is the first entry
508 */
509ui_file_list_entry_t *ui_file_list_prev(ui_file_list_entry_t *cur)
510{
511 ui_list_entry_t *lentry;
512
513 lentry = ui_list_prev(cur->entry);
514 if (lentry == NULL)
515 return NULL;
516
517 return (ui_file_list_entry_t *)ui_list_entry_get_arg(lentry);
518}
519
520/** Get entry under cursor.
521 *
522 * @param flist File list
523 * @return Current cursor
524 */
525ui_file_list_entry_t *ui_file_list_get_cursor(ui_file_list_t *flist)
526{
527 ui_list_entry_t *entry;
528
529 entry = ui_list_get_cursor(flist->list);
530 if (entry == NULL)
531 return NULL;
532
533 return (ui_file_list_entry_t *)ui_list_entry_get_arg(entry);
534}
535
536/** Open file list entry.
537 *
538 * Perform Open action on a file list entry (e.g. switch to a subdirectory).
539 *
540 * @param flist File list
541 * @param entry File list entry
542 *
543 * @return EOK on success or an error code
544 */
545errno_t ui_file_list_open(ui_file_list_t *flist, ui_file_list_entry_t *entry)
546{
547 if (entry->isdir)
548 return ui_file_list_open_dir(flist, entry);
549 else if (entry->svc == 0)
550 return ui_file_list_open_file(flist, entry);
551 else
552 return EOK;
553}
554
555/** Open file list directory entry.
556 *
557 * Perform Open action on a directory entry (i.e. switch to the directory).
558 *
559 * @param flist File list
560 * @param entry File list entry (which is a directory)
561 *
562 * @return EOK on success or an error code
563 */
564errno_t ui_file_list_open_dir(ui_file_list_t *flist,
565 ui_file_list_entry_t *entry)
566{
567 char *dirname;
568 errno_t rc;
569
570 assert(entry->isdir);
571
572 /*
573 * Need to copy out name before we free the entry below
574 * via ui_file_list_clear_entries().
575 */
576 dirname = str_dup(entry->name);
577 if (dirname == NULL)
578 return ENOMEM;
579
580 ui_file_list_clear_entries(flist);
581
582 rc = ui_file_list_read_dir(flist, dirname);
583 if (rc != EOK) {
584 free(dirname);
585 return rc;
586 }
587
588 free(dirname);
589
590 rc = ui_file_list_paint(flist);
591 if (rc != EOK)
592 return rc;
593
594 return EOK;
595}
596
597/** Open file list file entry.
598 *
599 * Perform Open action on a file entry (i.e. try running it).
600 *
601 * @param flist File list
602 * @param entry File list entry (which is a file)
603 *
604 * @return EOK on success or an error code
605 */
606errno_t ui_file_list_open_file(ui_file_list_t *flist,
607 ui_file_list_entry_t *entry)
608{
609 ui_file_list_selected(flist, entry->name);
610 return EOK;
611}
612
613/** Request file list activation.
614 *
615 * Call back to request file list activation.
616 *
617 * @param flist File list
618 */
619void ui_file_list_activate_req(ui_file_list_t *flist)
620{
621 if (flist->cb != NULL && flist->cb->activate_req != NULL)
622 flist->cb->activate_req(flist, flist->cb_arg);
623}
624
625/** Call back when a file is selected.
626 *
627 * @param flist File list
628 * @param fname File name
629 */
630void ui_file_list_selected(ui_file_list_t *flist, const char *fname)
631{
632 if (flist->cb != NULL && flist->cb->selected != NULL)
633 flist->cb->selected(flist, flist->cb_arg, fname);
634}
635
636/** Paint file list.
637 *
638 * @param flist File list
639 * @return EOK on success or an error code.
640 */
641errno_t ui_file_list_paint(ui_file_list_t *flist)
642{
643 return ui_control_paint(ui_list_ctl(flist->list));
644}
645
646/** Destroy file list control.
647 *
648 * @param arg Argument (ui_list_t *)
649 */
650void ui_file_list_ctl_destroy(void *arg)
651{
652 ui_file_list_t *flist = (ui_file_list_t *) arg;
653
654 ui_file_list_destroy(flist);
655}
656
657/** Paint file list control.
658 *
659 * @param arg Argument (ui_file_list_t *)
660 * @return EOK on success or an error code
661 */
662errno_t ui_file_list_ctl_paint(void *arg)
663{
664 ui_file_list_t *flist = (ui_file_list_t *) arg;
665
666 return ui_file_list_paint(flist);
667}
668
669/** Handle file list control keyboard event.
670 *
671 * @param arg Argument (ui_file_list_t *)
672 * @param kbd_event Keyboard event
673 * @return @c ui_claimed iff the event is claimed
674 */
675ui_evclaim_t ui_file_list_ctl_kbd_event(void *arg, kbd_event_t *event)
676{
677 ui_file_list_t *flist = (ui_file_list_t *) arg;
678
679 return ui_control_kbd_event(ui_list_ctl(flist->list), event);
680}
681
682/** Handle file list control position event.
683 *
684 * @param arg Argument (ui_file_list_t *)
685 * @param pos_event Position event
686 * @return @c ui_claimed iff the event is claimed
687 */
688ui_evclaim_t ui_file_list_ctl_pos_event(void *arg, pos_event_t *event)
689{
690 ui_file_list_t *flist = (ui_file_list_t *) arg;
691
692 return ui_control_pos_event(ui_list_ctl(flist->list), event);
693}
694
695/** Activate request callback handler for UI list within file list.
696 *
697 * @param list UI list
698 * @param arg Argument (File list)
699 */
700static void ui_file_list_list_activate_req(ui_list_t *list, void *arg)
701{
702 ui_file_list_t *flist = (ui_file_list_t *)arg;
703
704 ui_file_list_activate_req(flist);
705}
706
707/** Entry selected callback handler for UI list within file list.
708 *
709 * @param entr Activated UI list entry
710 * @param arg Argument (File list entry)
711 */
712static void ui_file_list_list_selected(ui_list_entry_t *entry, void *arg)
713{
714 ui_file_list_entry_t *fentry = (void *)arg;
715
716 (void) ui_file_list_open(fentry->flist, fentry);
717}
718
719/** @}
720 */
Note: See TracBrowser for help on using the repository browser.