source: mainline/uspace/lib/ui/src/filelist.c@ 9e240c1

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

UI display configuration utility

In addition to the command-line utility 'disp', we introduce its UI
equivalent 'display-cfg'. Currently this allows the user to configure
seats in a very comfortable way.

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