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
RevLine 
[11662bd]1/*
[7cf5ddb]2 * Copyright (c) 2023 Jiri Svoboda
[11662bd]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 */
[7cf5ddb]32/** @file File list control.
[11662bd]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>
[7cf5ddb]43#include <ui/list.h>
[11662bd]44#include <ui/resource.h>
45#include <vfs/vfs.h>
46#include <qsort.h>
47#include "../private/filelist.h"
[c0757e1f]48#include "../private/list.h"
[11662bd]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
[7cf5ddb]56/** List control ops */
[11662bd]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
[7cf5ddb]64static void ui_file_list_list_activate_req(ui_list_t *, void *);
65static void ui_file_list_list_selected(ui_list_entry_t *, void *);
[11662bd]66
[7cf5ddb]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,
[453f9645]72};
73
[11662bd]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);
[7cf5ddb]93 if (rc != EOK)
94 goto error;
[11662bd]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
[7cf5ddb]104 rc = ui_list_create(window, active, &flist->list);
[453f9645]105 if (rc != EOK)
106 goto error;
107
[7cf5ddb]108 ui_list_set_cb(flist->list, &ui_file_list_list_cb, (void *)flist);
[453f9645]109
[11662bd]110 flist->window = window;
111 *rflist = flist;
112 return EOK;
113error:
[7cf5ddb]114 ui_control_delete(flist->control);
[11662bd]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);
[7cf5ddb]130 ui_list_destroy(flist->list);
[11662bd]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{
[7cf5ddb]164 ui_list_set_rect(flist->list, rect);
[11662bd]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{
[7cf5ddb]174 return ui_list_is_active(flist->list);
[11662bd]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
[7cf5ddb]193 return ui_list_activate(flist->list);
[11662bd]194}
195
196/** Deactivate file list.
197 *
198 * @param flist File list
199 */
200void ui_file_list_deactivate(ui_file_list_t *flist)
201{
[7cf5ddb]202 ui_list_deactivate(flist->list);
[11662bd]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;
[7cf5ddb]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);
[11662bd]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;
[7cf5ddb]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;
[11662bd]286 return EOK;
287}
288
289/** Delete file list entry.
290 *
291 * @param entry File list entry
292 */
[c0757e1f]293void ui_file_list_entry_destroy(ui_file_list_entry_t *entry)
[11662bd]294{
[c0757e1f]295 ui_list_entry_destroy(entry->entry);
[7cf5ddb]296 free(entry->name);
[11662bd]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) {
[c0757e1f]310 ui_file_list_entry_destroy(entry);
[11662bd]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;
[7cf5ddb]329 ui_file_list_entry_t *cur;
[11662bd]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;
[7cf5ddb]398 cur = ui_file_list_first(flist);
399 next = ui_file_list_next(cur);
[11662bd]400 while (next != NULL && str_cmp(next->name, olddn) <= 0 &&
401 next->isdir) {
[7cf5ddb]402 cur = next;
403 next = ui_file_list_next(cur);
[11662bd]404 }
405
[7cf5ddb]406 /* Center on the entry */
407 ui_list_cursor_center(flist->list, cur->entry);
[11662bd]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{
[7cf5ddb]431 return ui_list_sort(flist->list);
[11662bd]432}
433
[7cf5ddb]434/** Compare two list entries within file list entries.
[11662bd]435 *
[7cf5ddb]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.
[11662bd]439 */
[7cf5ddb]440int ui_file_list_list_compare(ui_list_entry_t *ea, ui_list_entry_t *eb)
[11662bd]441{
[7cf5ddb]442 ui_file_list_entry_t *a;
443 ui_file_list_entry_t *b;
[11662bd]444 int dcmp;
445
[7cf5ddb]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
[11662bd]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{
[7cf5ddb]464 ui_list_entry_t *lentry;
[11662bd]465
[7cf5ddb]466 lentry = ui_list_first(flist->list);
467 if (lentry == NULL)
[11662bd]468 return NULL;
469
[7cf5ddb]470 return (ui_file_list_entry_t *)ui_list_entry_get_arg(lentry);
[11662bd]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{
[7cf5ddb]480 ui_list_entry_t *lentry;
[11662bd]481
[7cf5ddb]482 lentry = ui_list_last(flist->list);
483 if (lentry == NULL)
[11662bd]484 return NULL;
485
[7cf5ddb]486 return (ui_file_list_entry_t *)ui_list_entry_get_arg(lentry);
[11662bd]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{
[7cf5ddb]496 ui_list_entry_t *lentry;
[11662bd]497
[7cf5ddb]498 lentry = ui_list_next(cur->entry);
499 if (lentry == NULL)
[11662bd]500 return NULL;
501
[7cf5ddb]502 return (ui_file_list_entry_t *)ui_list_entry_get_arg(lentry);
[11662bd]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{
[7cf5ddb]512 ui_list_entry_t *lentry;
[11662bd]513
[7cf5ddb]514 lentry = ui_list_prev(cur->entry);
515 if (lentry == NULL)
[11662bd]516 return NULL;
517
[7cf5ddb]518 return (ui_file_list_entry_t *)ui_list_entry_get_arg(lentry);
[11662bd]519}
520
[54ddb59]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{
[7cf5ddb]528 ui_list_entry_t *entry;
[54ddb59]529
[7cf5ddb]530 entry = ui_list_get_cursor(flist->list);
531 if (entry == NULL)
532 return NULL;
[453f9645]533
[7cf5ddb]534 return (ui_file_list_entry_t *)ui_list_entry_get_arg(entry);
[453f9645]535}
536
[11662bd]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
[7cf5ddb]595 return EOK;
[11662bd]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
[7cf5ddb]637/** Paint file list.
[453f9645]638 *
[7cf5ddb]639 * @param flist File list
640 * @return EOK on success or an error code.
[453f9645]641 */
[7cf5ddb]642errno_t ui_file_list_paint(ui_file_list_t *flist)
[453f9645]643{
[7cf5ddb]644 return ui_control_paint(ui_list_ctl(flist->list));
[453f9645]645}
646
[7cf5ddb]647/** Destroy file list control.
[453f9645]648 *
[7cf5ddb]649 * @param arg Argument (ui_list_t *)
[453f9645]650 */
[7cf5ddb]651void ui_file_list_ctl_destroy(void *arg)
[453f9645]652{
[7cf5ddb]653 ui_file_list_t *flist = (ui_file_list_t *) arg;
654
655 ui_file_list_destroy(flist);
[453f9645]656}
657
[7cf5ddb]658/** Paint file list control.
[453f9645]659 *
660 * @param arg Argument (ui_file_list_t *)
[7cf5ddb]661 * @return EOK on success or an error code
[453f9645]662 */
[7cf5ddb]663errno_t ui_file_list_ctl_paint(void *arg)
[453f9645]664{
[7cf5ddb]665 ui_file_list_t *flist = (ui_file_list_t *) arg;
666
667 return ui_file_list_paint(flist);
[453f9645]668}
669
[7cf5ddb]670/** Handle file list control keyboard event.
[453f9645]671 *
672 * @param arg Argument (ui_file_list_t *)
[7cf5ddb]673 * @param kbd_event Keyboard event
674 * @return @c ui_claimed iff the event is claimed
[453f9645]675 */
[7cf5ddb]676ui_evclaim_t ui_file_list_ctl_kbd_event(void *arg, kbd_event_t *event)
[453f9645]677{
[7cf5ddb]678 ui_file_list_t *flist = (ui_file_list_t *) arg;
679
680 return ui_control_kbd_event(ui_list_ctl(flist->list), event);
[453f9645]681}
682
[7cf5ddb]683/** Handle file list control position event.
[453f9645]684 *
685 * @param arg Argument (ui_file_list_t *)
[7cf5ddb]686 * @param pos_event Position event
687 * @return @c ui_claimed iff the event is claimed
[453f9645]688 */
[7cf5ddb]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)
[453f9645]702{
703 ui_file_list_t *flist = (ui_file_list_t *)arg;
704
[7cf5ddb]705 ui_file_list_activate_req(flist);
706}
[453f9645]707
[7cf5ddb]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;
[453f9645]716
[7cf5ddb]717 (void) ui_file_list_open(fentry->flist, fentry);
[453f9645]718}
719
[11662bd]720/** @}
721 */
Note: See TracBrowser for help on using the repository browser.