Index: uspace/app/nav/panel.c
===================================================================
--- uspace/app/nav/panel.c	(revision 458301502f6afa8083247442aa9832ffb402f9f9)
+++ uspace/app/nav/panel.c	(revision 54ddb59be14baa078affd298a0ddbf53724d7b29)
@@ -1,4 +1,4 @@
 /*
- * Copyright (c) 2021 Jiri Svoboda
+ * Copyright (c) 2022 Jiri Svoboda
  * All rights reserved.
  *
@@ -35,16 +35,13 @@
  */
 
-#include <dirent.h>
 #include <errno.h>
 #include <gfx/render.h>
 #include <gfx/text.h>
 #include <stdlib.h>
-#include <str.h>
 #include <task.h>
 #include <ui/control.h>
+#include <ui/filelist.h>
 #include <ui/paint.h>
 #include <ui/resource.h>
-#include <vfs/vfs.h>
-#include <qsort.h>
 #include "panel.h"
 #include "nav.h"
@@ -56,5 +53,5 @@
 
 /** Panel control ops */
-ui_control_ops_t panel_ctl_ops = {
+static ui_control_ops_t panel_ctl_ops = {
 	.destroy = panel_ctl_destroy,
 	.paint = panel_ctl_paint,
@@ -63,4 +60,11 @@
 };
 
+static void panel_flist_selected(ui_file_list_t *, void *, const char *);
+
+/** Panel file list callbacks */
+static ui_file_list_cb_t panel_flist_cb = {
+	.selected = panel_flist_selected
+};
+
 /** Create panel.
  *
@@ -90,23 +94,15 @@
 		goto error;
 
-	rc = gfx_color_new_ega(0x30, &panel->curs_color);
+	rc = gfx_color_new_ega(0x0f, &panel->act_border_color);
 	if (rc != EOK)
 		goto error;
 
-	rc = gfx_color_new_ega(0x0f, &panel->act_border_color);
+	rc = ui_file_list_create(window, active, &panel->flist);
 	if (rc != EOK)
 		goto error;
 
-	rc = gfx_color_new_ega(0x0f, &panel->dir_color);
-	if (rc != EOK)
-		goto error;
-
-	rc = gfx_color_new_ega(0x0a, &panel->svc_color);
-	if (rc != EOK)
-		goto error;
+	ui_file_list_set_cb(panel->flist, &panel_flist_cb, (void *)panel);
 
 	panel->window = window;
-	list_initialize(&panel->entries);
-	panel->entries_cnt = 0;
 	panel->active = active;
 	*rpanel = panel;
@@ -115,12 +111,8 @@
 	if (panel->color != NULL)
 		gfx_color_delete(panel->color);
-	if (panel->curs_color != NULL)
-		gfx_color_delete(panel->curs_color);
 	if (panel->act_border_color != NULL)
 		gfx_color_delete(panel->act_border_color);
-	if (panel->dir_color != NULL)
-		gfx_color_delete(panel->dir_color);
-	if (panel->svc_color != NULL)
-		gfx_color_delete(panel->svc_color);
+	if (panel->flist != NULL)
+		ui_file_list_destroy(panel->flist);
 	ui_control_delete(panel->control);
 	free(panel);
@@ -135,9 +127,5 @@
 {
 	gfx_color_delete(panel->color);
-	gfx_color_delete(panel->curs_color);
 	gfx_color_delete(panel->act_border_color);
-	gfx_color_delete(panel->dir_color);
-	gfx_color_delete(panel->svc_color);
-	panel_clear_entries(panel);
 	ui_control_delete(panel->control);
 	free(panel);
@@ -156,89 +144,15 @@
 }
 
-/** Paint panel entry.
- *
- * @param entry Panel entry
- * @param entry_idx Entry index (within list of entries)
- * @return EOK on success or an error code
- */
-errno_t panel_entry_paint(panel_entry_t *entry, size_t entry_idx)
-{
-	panel_t *panel = entry->panel;
+/** Paint panel.
+ *
+ * @param panel Panel
+ */
+errno_t panel_paint(panel_t *panel)
+{
 	gfx_context_t *gc = ui_window_get_gc(panel->window);
 	ui_resource_t *res = ui_window_get_res(panel->window);
-	gfx_font_t *font = ui_resource_get_font(res);
-	gfx_text_fmt_t fmt;
-	gfx_coord2_t pos;
-	gfx_rect_t rect;
-	size_t rows;
-	errno_t rc;
-
-	gfx_text_fmt_init(&fmt);
-	fmt.font = font;
-	rows = panel_page_size(panel);
-
-	/* Do not display entry outside of current page */
-	if (entry_idx < panel->page_idx ||
-	    entry_idx >= panel->page_idx + rows)
-		return EOK;
-
-	pos.x = panel->rect.p0.x + 1;
-	pos.y = panel->rect.p0.y + 1 + entry_idx - panel->page_idx;
-
-	if (entry == panel->cursor && panel->active)
-		fmt.color = panel->curs_color;
-	else if (entry->isdir)
-		fmt.color = panel->dir_color;
-	else if (entry->svc != 0)
-		fmt.color = panel->svc_color;
-	else
-		fmt.color = panel->color;
-
-	/* Draw entry background */
-	rect.p0 = pos;
-	rect.p1.x = panel->rect.p1.x - 1;
-	rect.p1.y = rect.p0.y + 1;
-
-	rc = gfx_set_color(gc, fmt.color);
-	if (rc != EOK)
-		return rc;
-
-	rc = gfx_fill_rect(gc, &rect);
-	if (rc != EOK)
-		return rc;
-
-	/*
-	 * Make sure name does not overflow the entry rectangle.
-	 *
-	 * XXX We probably want to measure the text width, and,
-	 * if it's too long, use gfx_text_find_pos() to find where
-	 * it should be cut off (and append some sort of overflow
-	 * marker.
-	 */
-	rc = gfx_set_clip_rect(gc, &rect);
-	if (rc != EOK)
-		return rc;
-
-	rc = gfx_puttext(&pos, &fmt, entry->name);
-	if (rc != EOK) {
-		(void) gfx_set_clip_rect(gc, NULL);
-		return rc;
-	}
-
-	return gfx_set_clip_rect(gc, NULL);
-}
-
-/** Paint panel.
- *
- * @param panel Panel
- */
-errno_t panel_paint(panel_t *panel)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	ui_resource_t *res = ui_window_get_res(panel->window);
-	panel_entry_t *entry;
 	ui_box_style_t bstyle;
 	gfx_color_t *bcolor;
-	int i, lines;
+	ui_control_t *ctl;
 	errno_t rc;
 
@@ -263,16 +177,8 @@
 		return rc;
 
-	lines = panel_page_size(panel);
-	i = 0;
-
-	entry = panel->page;
-	while (entry != NULL && i < lines) {
-		rc = panel_entry_paint(entry, panel->page_idx + i);
-		if (rc != EOK)
-			return rc;
-
-		++i;
-		entry = panel_next(entry);
-	}
+	ctl = ui_file_list_ctl(panel->flist);
+	rc = ui_control_paint(ctl);
+	if (rc != EOK)
+		return rc;
 
 	rc = gfx_update(gc);
@@ -291,38 +197,11 @@
 ui_evclaim_t panel_kbd_event(panel_t *panel, kbd_event_t *event)
 {
+	ui_control_t *ctl;
+
 	if (!panel->active)
 		return ui_unclaimed;
 
-	if (event->type == KEY_PRESS) {
-		if ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
-			switch (event->key) {
-			case KC_UP:
-				panel_cursor_up(panel);
-				break;
-			case KC_DOWN:
-				panel_cursor_down(panel);
-				break;
-			case KC_HOME:
-				panel_cursor_top(panel);
-				break;
-			case KC_END:
-				panel_cursor_bottom(panel);
-				break;
-			case KC_PAGE_UP:
-				panel_page_up(panel);
-				break;
-			case KC_PAGE_DOWN:
-				panel_page_down(panel);
-				break;
-			case KC_ENTER:
-				panel_open(panel, panel->cursor);
-				break;
-			default:
-				break;
-			}
-		}
-	}
-
-	return ui_claimed;
+	ctl = ui_file_list_ctl(panel->flist);
+	return ui_control_kbd_event(ctl, event);
 }
 
@@ -336,8 +215,5 @@
 {
 	gfx_coord2_t pos;
-	gfx_rect_t irect;
-	panel_entry_t *entry;
-	size_t entry_idx;
-	int n;
+	ui_control_t *ctl;
 
 	pos.x = event->hpos;
@@ -349,68 +225,35 @@
 		panel_activate_req(panel);
 
+	ctl = ui_file_list_ctl(panel->flist);
+	return ui_control_pos_event(ctl, event);
+}
+
+/** Get base control for panel.
+ *
+ * @param panel Panel
+ * @return Base UI control
+ */
+ui_control_t *panel_ctl(panel_t *panel)
+{
+	return panel->control;
+}
+
+/** Set panel rectangle.
+ *
+ * @param panel Panel
+ * @param rect Rectangle
+ */
+void panel_set_rect(panel_t *panel, gfx_rect_t *rect)
+{
+	gfx_rect_t irect;
+
+	panel->rect = *rect;
+
 	irect.p0.x = panel->rect.p0.x + 1;
 	irect.p0.y = panel->rect.p0.y + 1;
-	irect.p1.x = panel->rect.p1.x - 1;
+	irect.p1.x = panel->rect.p1.x;
 	irect.p1.y = panel->rect.p1.y - 1;
 
-	if (event->type == POS_PRESS || event->type == POS_DCLICK) {
-		/* Did we click on one of the entries? */
-		if (gfx_pix_inside_rect(&pos, &irect)) {
-			/* Index within page */
-			n = pos.y - irect.p0.y;
-
-			/* Entry and its index within entire listing */
-			entry = panel_page_nth_entry(panel, n, &entry_idx);
-
-			if (event->type == POS_PRESS) {
-				/* Move to the entry found */
-				panel_cursor_move(panel, entry, entry_idx);
-			} else {
-				/* event->type == POS_DCLICK */
-				panel_open(panel, entry);
-			}
-		} else {
-			/* It's in the border. */
-			if (event->type == POS_PRESS) {
-				/* Top or bottom half? */
-				if (pos.y >= (irect.p0.y + irect.p1.y) / 2)
-					panel_page_down(panel);
-				else
-					panel_page_up(panel);
-			}
-		}
-	}
-
-	return ui_claimed;
-}
-
-/** Get base control for panel.
- *
- * @param panel Panel
- * @return Base UI control
- */
-ui_control_t *panel_ctl(panel_t *panel)
-{
-	return panel->control;
-}
-
-/** Set panel rectangle.
- *
- * @param panel Panel
- * @param rect Rectangle
- */
-void panel_set_rect(panel_t *panel, gfx_rect_t *rect)
-{
-	panel->rect = *rect;
-}
-
-/** Get panel page size.
- *
- * @param panel Panel
- * @return Number of entries that fit in panel at the same time.
- */
-unsigned panel_page_size(panel_t *panel)
-{
-	return panel->rect.p1.y - panel->rect.p0.y - 2;
+	ui_file_list_set_rect(panel->flist, &irect);
 }
 
@@ -435,9 +278,7 @@
 	errno_t rc;
 
-	if (panel->dir != NULL) {
-		rc = vfs_cwd_set(panel->dir);
-		if (rc != EOK)
-			return rc;
-	}
+	rc = ui_file_list_activate(panel->flist);
+	if (rc != EOK)
+		return rc;
 
 	panel->active = true;
@@ -452,15 +293,7 @@
 void panel_deactivate(panel_t *panel)
 {
+	ui_file_list_deactivate(panel->flist);
 	panel->active = false;
 	(void) panel_paint(panel);
-}
-
-/** Initialize panel entry attributes.
- *
- * @param attr Attributes
- */
-void panel_entry_attr_init(panel_entry_attr_t *attr)
-{
-	memset(attr, 0, sizeof(*attr));
 }
 
@@ -514,66 +347,4 @@
 }
 
-/** Append new panel entry.
- *
- * @param panel Panel
- * @param attr Entry attributes
- * @return EOK on success or an error code
- */
-errno_t panel_entry_append(panel_t *panel, panel_entry_attr_t *attr)
-{
-	panel_entry_t *entry;
-
-	entry = calloc(1, sizeof(panel_entry_t));
-	if (entry == NULL)
-		return ENOMEM;
-
-	entry->panel = panel;
-	entry->name = str_dup(attr->name);
-	if (entry->name == NULL) {
-		free(entry);
-		return ENOMEM;
-	}
-
-	entry->size = attr->size;
-	entry->isdir = attr->isdir;
-	entry->svc = attr->svc;
-	link_initialize(&entry->lentries);
-	list_append(&entry->lentries, &panel->entries);
-	++panel->entries_cnt;
-	return EOK;
-}
-
-/** Delete panel entry.
- *
- * @param entry Panel entry
- */
-void panel_entry_delete(panel_entry_t *entry)
-{
-	if (entry->panel->cursor == entry)
-		entry->panel->cursor = NULL;
-	if (entry->panel->page == entry)
-		entry->panel->page = NULL;
-
-	list_remove(&entry->lentries);
-	--entry->panel->entries_cnt;
-	free((char *) entry->name);
-	free(entry);
-}
-
-/** Clear panel entry list.
- *
- * @param panel Panel
- */
-void panel_clear_entries(panel_t *panel)
-{
-	panel_entry_t *entry;
-
-	entry = panel_first(panel);
-	while (entry != NULL) {
-		panel_entry_delete(entry);
-		entry = panel_first(panel);
-	}
-}
-
 /** Read directory into panel entry list.
  *
@@ -584,597 +355,29 @@
 errno_t panel_read_dir(panel_t *panel, const char *dirname)
 {
-	DIR *dir;
-	struct dirent *dirent;
-	vfs_stat_t finfo;
-	char newdir[256];
-	char *ndir = NULL;
-	panel_entry_attr_t attr;
-	panel_entry_t *next;
-	panel_entry_t *prev;
-	char *olddn;
-	size_t pg_size;
-	size_t max_idx;
-	size_t i;
-	errno_t rc;
-
-	rc = vfs_cwd_set(dirname);
-	if (rc != EOK)
-		return rc;
-
-	rc = vfs_cwd_get(newdir, sizeof(newdir));
-	if (rc != EOK)
-		return rc;
-
-	ndir = str_dup(newdir);
-	if (ndir == NULL)
-		return ENOMEM;
-
-	dir = opendir(".");
-	if (dir == NULL) {
-		rc = errno;
-		goto error;
-	}
-
-	if (str_cmp(ndir, "/") != 0) {
-		/* Need to add a synthetic up-dir entry */
-		panel_entry_attr_init(&attr);
-		attr.name = "..";
-		attr.isdir = true;
-
-		rc = panel_entry_append(panel, &attr);
-		if (rc != EOK)
-			goto error;
-	}
-
-	dirent = readdir(dir);
-	while (dirent != NULL) {
-		rc = vfs_stat_path(dirent->d_name, &finfo);
-		if (rc != EOK) {
-			/* Possibly a stale entry */
-			dirent = readdir(dir);
-			continue;
-		}
-
-		panel_entry_attr_init(&attr);
-		attr.name = dirent->d_name;
-		attr.size = finfo.size;
-		attr.isdir = finfo.is_directory;
-		attr.svc = finfo.service;
-
-		rc = panel_entry_append(panel, &attr);
-		if (rc != EOK)
-			goto error;
-
-		dirent = readdir(dir);
-	}
-
-	closedir(dir);
-
-	rc = panel_sort(panel);
-	if (rc != EOK)
-		goto error;
-
-	panel->cursor = panel_first(panel);
-	panel->cursor_idx = 0;
-	panel->page = panel_first(panel);
-	panel->page_idx = 0;
-
-	/* Moving up? */
-	if (str_cmp(dirname, "..") == 0) {
-		/* Get the last component of old path */
-		olddn = str_rchr(panel->dir, '/');
-		if (olddn != NULL && *olddn != '\0') {
-			/* Find corresponding entry */
-			++olddn;
-			next = panel_next(panel->cursor);
-			while (next != NULL && str_cmp(next->name, olddn) <= 0 &&
-			    next->isdir) {
-				panel->cursor = next;
-				++panel->cursor_idx;
-				next = panel_next(panel->cursor);
-			}
-
-			/* Move page so that cursor is in the center */
-			panel->page = panel->cursor;
-			panel->page_idx = panel->cursor_idx;
-
-			pg_size = panel_page_size(panel);
-
-			for (i = 0; i < pg_size / 2; i++) {
-				prev = panel_prev(panel->page);
-				if (prev == NULL)
-					break;
-
-				panel->page = prev;
-				--panel->page_idx;
-			}
-
-			/* Make sure page is not beyond the end if possible */
-			if (panel->entries_cnt > pg_size)
-				max_idx = panel->entries_cnt - pg_size;
-			else
-				max_idx = 0;
-
-			while (panel->page_idx > 0 && panel->page_idx > max_idx) {
-				prev = panel_prev(panel->page);
-				if (prev == NULL)
-					break;
-
-				panel->page = prev;
-				--panel->page_idx;
-			}
-		}
-	}
-
-	free(panel->dir);
-	panel->dir = ndir;
-
-	return EOK;
-error:
-	(void) vfs_cwd_set(panel->dir);
-	if (ndir != NULL)
-		free(ndir);
-	if (dir != NULL)
-		closedir(dir);
-	return rc;
-}
-
-/** Sort panel entries.
- *
- * @param panel Panel
- * @return EOK on success, ENOMEM if out of memory
- */
-errno_t panel_sort(panel_t *panel)
-{
-	panel_entry_t **emap;
-	panel_entry_t *entry;
-	size_t i;
-
-	/* Create an array to hold pointer to each entry */
-	emap = calloc(panel->entries_cnt, sizeof(panel_entry_t *));
-	if (emap == NULL)
-		return ENOMEM;
-
-	/* Write entry pointers to array */
-	entry = panel_first(panel);
-	i = 0;
-	while (entry != NULL) {
-		assert(i < panel->entries_cnt);
-		emap[i++] = entry;
-		entry = panel_next(entry);
-	}
-
-	/* Sort the array of pointers */
-	qsort(emap, panel->entries_cnt, sizeof(panel_entry_t *),
-	    panel_entry_ptr_cmp);
-
-	/* Unlink entries from entry list */
-	entry = panel_first(panel);
-	while (entry != NULL) {
-		list_remove(&entry->lentries);
-		entry = panel_first(panel);
-	}
-
-	/* Add entries back to entry list sorted */
-	for (i = 0; i < panel->entries_cnt; i++)
-		list_append(&emap[i]->lentries, &panel->entries);
-
-	free(emap);
-	return EOK;
-}
-
-/** Compare two panel entries indirectly referenced by pointers.
- *
- * @param pa Pointer to pointer to first entry
- * @param pb Pointer to pointer to second entry
- * @return <0, =0, >=0 if pa < b, pa == pb, pa > pb, resp.
- */
-int panel_entry_ptr_cmp(const void *pa, const void *pb)
-{
-	panel_entry_t *a = *(panel_entry_t **)pa;
-	panel_entry_t *b = *(panel_entry_t **)pb;
-	int dcmp;
-
-	/* Sort directories first */
-	dcmp = b->isdir - a->isdir;
-	if (dcmp != 0)
-		return dcmp;
-
-	return str_cmp(a->name, b->name);
-}
-
-/** Return first panel entry.
- *
- * @panel Panel
- * @return First panel entry or @c NULL if there are no entries
- */
-panel_entry_t *panel_first(panel_t *panel)
-{
-	link_t *link;
-
-	link = list_first(&panel->entries);
-	if (link == NULL)
-		return NULL;
-
-	return list_get_instance(link, panel_entry_t, lentries);
-}
-
-/** Return last panel entry.
- *
- * @panel Panel
- * @return Last panel entry or @c NULL if there are no entries
- */
-panel_entry_t *panel_last(panel_t *panel)
-{
-	link_t *link;
-
-	link = list_last(&panel->entries);
-	if (link == NULL)
-		return NULL;
-
-	return list_get_instance(link, panel_entry_t, lentries);
-}
-
-/** Return next panel entry.
- *
- * @param cur Current entry
- * @return Next entry or @c NULL if @a cur is the last entry
- */
-panel_entry_t *panel_next(panel_entry_t *cur)
-{
-	link_t *link;
-
-	link = list_next(&cur->lentries, &cur->panel->entries);
-	if (link == NULL)
-		return NULL;
-
-	return list_get_instance(link, panel_entry_t, lentries);
-}
-
-/** Return previous panel entry.
- *
- * @param cur Current entry
- * @return Previous entry or @c NULL if @a cur is the first entry
- */
-panel_entry_t *panel_prev(panel_entry_t *cur)
-{
-	link_t *link;
-
-	link = list_prev(&cur->lentries, &cur->panel->entries);
-	if (link == NULL)
-		return NULL;
-
-	return list_get_instance(link, panel_entry_t, lentries);
-}
-
-/** Find the n-th entry of the current panel page.
- *
- * If the page is short and has less than n+1 entries, return the last entry.
- *
- * @param panel Panel
- * @param n Which entry to get (starting from 0)
- * @param ridx Place to store index (within listing) of the entry
- * @return n-th entry of the page
- */
-panel_entry_t *panel_page_nth_entry(panel_t *panel, size_t n, size_t *ridx)
-{
-	panel_entry_t *entry;
-	panel_entry_t *next;
-	size_t i;
-	size_t idx;
-
-	assert(n < panel_page_size(panel));
-
-	entry = panel->page;
-	idx = panel->page_idx;
-	for (i = 0; i < n; i++) {
-		next = panel_next(entry);
-		if (next == NULL)
-			break;
-
-		entry = next;
-		++idx;
-	}
-
-	*ridx = idx;
-	return entry;
-}
-
-/** Move cursor to a new position, possibly scrolling.
- *
- * @param panel Panel
- * @param entry New entry under cursor
- * @param entry_idx Index of new entry under cursor
- */
-void panel_cursor_move(panel_t *panel, panel_entry_t *entry, size_t entry_idx)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	panel_entry_t *old_cursor;
-	size_t old_idx;
-	size_t rows;
-	panel_entry_t *e;
-	size_t i;
-
-	rows = panel_page_size(panel);
-
-	old_cursor = panel->cursor;
-	old_idx = panel->cursor_idx;
-
-	panel->cursor = entry;
-	panel->cursor_idx = entry_idx;
-
-	if (entry_idx >= panel->page_idx &&
-	    entry_idx < panel->page_idx + rows) {
-		/*
-		 * If cursor is still on the current page, we're not
-		 * scrolling. Just unpaint old cursor and paint new
-		 * cursor.
-		 */
-		panel_entry_paint(old_cursor, old_idx);
-		panel_entry_paint(panel->cursor, panel->cursor_idx);
-
-		(void) gfx_update(gc);
-	} else {
-		/*
-		 * Need to scroll and update all rows.
-		 */
-
-		/* Scrolling up */
-		if (entry_idx < panel->page_idx) {
-			panel->page = entry;
-			panel->page_idx = entry_idx;
-		}
-
-		/* Scrolling down */
-		if (entry_idx >= panel->page_idx + rows) {
-			if (entry_idx >= rows) {
-				panel->page_idx = entry_idx - rows + 1;
-				/* Find first page entry (go back rows - 1) */
-				e = entry;
-				for (i = 0; i < rows - 1; i++) {
-					e = panel_prev(e);
-				}
-
-				/* Should be valid */
-				assert(e != NULL);
-				panel->page = e;
-			} else {
-				panel->page = panel_first(panel);
-				panel->page_idx = 0;
-			}
-		}
-
-		(void) panel_paint(panel);
-	}
-}
-
-/** Move cursor one entry up.
- *
- * @param panel Panel
- */
-void panel_cursor_up(panel_t *panel)
-{
-	panel_entry_t *prev;
-	size_t prev_idx;
-
-	prev = panel_prev(panel->cursor);
-	prev_idx = panel->cursor_idx - 1;
-	if (prev != NULL)
-		panel_cursor_move(panel, prev, prev_idx);
-}
-
-/** Move cursor one entry down.
- *
- * @param panel Panel
- */
-void panel_cursor_down(panel_t *panel)
-{
-	panel_entry_t *next;
-	size_t next_idx;
-
-	next = panel_next(panel->cursor);
-	next_idx = panel->cursor_idx + 1;
-	if (next != NULL)
-		panel_cursor_move(panel, next, next_idx);
-}
-
-/** Move cursor to top.
- *
- * @param panel Panel
- */
-void panel_cursor_top(panel_t *panel)
-{
-	panel_cursor_move(panel, panel_first(panel), 0);
-}
-
-/** Move cursor to bottom.
- *
- * @param panel Panel
- */
-void panel_cursor_bottom(panel_t *panel)
-{
-	panel_cursor_move(panel, panel_last(panel), panel->entries_cnt - 1);
-}
-
-/** Move one page up.
- *
- * @param panel Panel
- */
-void panel_page_up(panel_t *panel)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	panel_entry_t *old_page;
-	panel_entry_t *old_cursor;
-	size_t old_idx;
-	size_t rows;
-	panel_entry_t *entry;
-	size_t i;
-
-	rows = panel_page_size(panel);
-
-	old_page = panel->page;
-	old_cursor = panel->cursor;
-	old_idx = panel->cursor_idx;
-
-	/* Move page by rows entries up (if possible) */
-	for (i = 0; i < rows; i++) {
-		entry = panel_prev(panel->page);
-		if (entry != NULL) {
-			panel->page = entry;
-			--panel->page_idx;
-		}
-	}
-
-	/* Move cursor by rows entries up (if possible) */
-
-	for (i = 0; i < rows; i++) {
-		entry = panel_prev(panel->cursor);
-		if (entry != NULL) {
-			panel->cursor = entry;
-			--panel->cursor_idx;
-		}
-	}
-
-	if (panel->page != old_page) {
-		/* We have scrolled. Need to repaint all entries */
-		(void) panel_paint(panel);
-	} else if (panel->cursor != old_cursor) {
-		/* No scrolling, but cursor has moved */
-		panel_entry_paint(old_cursor, old_idx);
-		panel_entry_paint(panel->cursor, panel->cursor_idx);
-
-		(void) gfx_update(gc);
-	}
-}
-
-/** Move one page down.
- *
- * @param panel Panel
- */
-void panel_page_down(panel_t *panel)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	panel_entry_t *old_page;
-	panel_entry_t *old_cursor;
-	size_t old_idx;
-	size_t max_idx;
-	size_t rows;
-	panel_entry_t *entry;
-	size_t i;
-
-	rows = panel_page_size(panel);
-
-	old_page = panel->page;
-	old_cursor = panel->cursor;
-	old_idx = panel->cursor_idx;
-
-	if (panel->entries_cnt > rows)
-		max_idx = panel->entries_cnt - rows;
-	else
-		max_idx = 0;
-
-	/* Move page by rows entries down (if possible) */
-	for (i = 0; i < rows; i++) {
-		entry = panel_next(panel->page);
-		/* Do not scroll that results in a short page */
-		if (entry != NULL && panel->page_idx < max_idx) {
-			panel->page = entry;
-			++panel->page_idx;
-		}
-	}
-
-	/* Move cursor by rows entries down (if possible) */
-
-	for (i = 0; i < rows; i++) {
-		entry = panel_next(panel->cursor);
-		if (entry != NULL) {
-			panel->cursor = entry;
-			++panel->cursor_idx;
-		}
-	}
-
-	if (panel->page != old_page) {
-		/* We have scrolled. Need to repaint all entries */
-		(void) panel_paint(panel);
-	} else if (panel->cursor != old_cursor) {
-		/* No scrolling, but cursor has moved */
-		panel_entry_paint(old_cursor, old_idx);
-		panel_entry_paint(panel->cursor, panel->cursor_idx);
-
-		(void) gfx_update(gc);
-	}
-}
-
-/** Open panel entry.
- *
- * Perform Open action on a panel entry (e.g. switch to a subdirectory).
- *
- * @param panel Panel
- * @param entry Panel entry
+	return ui_file_list_read_dir(panel->flist, dirname);
+}
+
+/** Request panel activation.
+ *
+ * Call back to request panel activation.
+ *
+ * @param panel Panel
+ */
+void panel_activate_req(panel_t *panel)
+{
+	if (panel->cb != NULL && panel->cb->activate_req != NULL)
+		panel->cb->activate_req(panel->cb_arg, panel);
+}
+
+/** Open panel file entry.
+ *
+ * Perform Open action on a file entry (i.e. try running it).
+ *
+ * @param panel Panel
+ * @param fname File name
  *
  * @return EOK on success or an error code
  */
-errno_t panel_open(panel_t *panel, panel_entry_t *entry)
-{
-	if (entry->isdir)
-		return panel_open_dir(panel, entry);
-	else if (entry->svc == 0)
-		return panel_open_file(panel, entry);
-	else
-		return EOK;
-}
-
-/** Open panel directory entry.
- *
- * Perform Open action on a directory entry (i.e. switch to the directory).
- *
- * @param panel Panel
- * @param entry Panel entry (which is a directory)
- *
- * @return EOK on success or an error code
- */
-errno_t panel_open_dir(panel_t *panel, panel_entry_t *entry)
-{
-	gfx_context_t *gc = ui_window_get_gc(panel->window);
-	char *dirname;
-	errno_t rc;
-
-	assert(entry->isdir);
-
-	/*
-	 * Need to copy out name before we free the entry below
-	 * via panel_clear_entries().
-	 */
-	dirname = str_dup(entry->name);
-	if (dirname == NULL)
-		return ENOMEM;
-
-	panel_clear_entries(panel);
-
-	rc = panel_read_dir(panel, dirname);
-	if (rc != EOK) {
-		free(dirname);
-		return rc;
-	}
-
-	free(dirname);
-
-	rc = panel_paint(panel);
-	if (rc != EOK)
-		return rc;
-
-	return gfx_update(gc);
-}
-
-/** Open panel file entry.
- *
- * Perform Open action on a file entry (i.e. try running it).
- *
- * @param panel Panel
- * @param entry Panel entry (which is a file)
- *
- * @return EOK on success or an error code
- */
-errno_t panel_open_file(panel_t *panel, panel_entry_t *entry)
+static errno_t panel_open_file(panel_t *panel, const char *fname)
 {
 	task_id_t id;
@@ -1185,9 +388,4 @@
 	ui_t *ui;
 
-	/* It's not a directory */
-	assert(!entry->isdir);
-	/* It's not a service-special file */
-	assert(entry->svc == 0);
-
 	ui = ui_window_get_ui(panel->window);
 
@@ -1197,5 +395,5 @@
 		return rc;
 
-	rc = task_spawnl(&id, &wait, entry->name, entry->name, NULL);
+	rc = task_spawnl(&id, &wait, fname, fname, NULL);
 	if (rc != EOK)
 		goto error;
@@ -1218,14 +416,16 @@
 }
 
-/** Request panel activation.
- *
- * Call back to request panel activation.
- *
- * @param panel Panel
- */
-void panel_activate_req(panel_t *panel)
-{
-	if (panel->cb != NULL && panel->cb->activate_req != NULL)
-		panel->cb->activate_req(panel->cb_arg, panel);
+/** File in panel file list was selected.
+ *
+ * @param flist File list
+ * @param arg Argument (panel_t *)
+ * @param fname File name
+ */
+static void panel_flist_selected(ui_file_list_t *flist, void *arg,
+    const char *fname)
+{
+	panel_t *panel = (panel_t *)arg;
+
+	(void) panel_open_file(panel, fname);
 }
 
