source: mainline/uspace/app/nav/panel.c@ be1d74c1

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since be1d74c1 was be1d74c1, checked in by jxsvoboda <5887334+jxsvoboda@…>, 4 years ago

Cursor movement (up, down, to top, to bottom)

  • Property mode set to 100644
File size: 12.7 KB
RevLine 
[6aa85c1]1/*
2 * Copyright (c) 2021 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 nav
30 * @{
31 */
32/** @file Navigator panel.
33 *
34 * Displays a file listing.
35 */
36
[0e80e40]37#include <dirent.h>
[6aa85c1]38#include <errno.h>
[b36ebb42]39#include <gfx/render.h>
[0e80e40]40#include <gfx/text.h>
[6aa85c1]41#include <stdlib.h>
[61784ed]42#include <str.h>
[b36ebb42]43#include <ui/control.h>
44#include <ui/paint.h>
[0e80e40]45#include <ui/resource.h>
[6aa85c1]46#include "panel.h"
47#include "nav.h"
48
[b36ebb42]49static void panel_ctl_destroy(void *);
50static errno_t panel_ctl_paint(void *);
[be1d74c1]51static ui_evclaim_t panel_ctl_kbd_event(void *, kbd_event_t *);
[b36ebb42]52static ui_evclaim_t panel_ctl_pos_event(void *, pos_event_t *);
53
54/** Panel control ops */
55ui_control_ops_t panel_ctl_ops = {
56 .destroy = panel_ctl_destroy,
57 .paint = panel_ctl_paint,
[be1d74c1]58 .kbd_event = panel_ctl_kbd_event,
[b36ebb42]59 .pos_event = panel_ctl_pos_event
60};
61
[6aa85c1]62/** Create panel.
63 *
[b36ebb42]64 * @param window Containing window
[6aa85c1]65 * @param rpanel Place to store pointer to new panel
66 * @return EOK on success or an error code
67 */
[b36ebb42]68errno_t panel_create(ui_window_t *window, panel_t **rpanel)
[6aa85c1]69{
70 panel_t *panel;
[b36ebb42]71 errno_t rc;
[6aa85c1]72
73 panel = calloc(1, sizeof(panel_t));
74 if (panel == NULL)
75 return ENOMEM;
76
[b36ebb42]77 rc = ui_control_new(&panel_ctl_ops, (void *)panel,
78 &panel->control);
79 if (rc != EOK) {
80 free(panel);
81 return rc;
82 }
83
84 rc = gfx_color_new_ega(0x07, &panel->color);
85 if (rc != EOK)
86 goto error;
87
[0e80e40]88 rc = gfx_color_new_ega(0x30, &panel->curs_color);
89 if (rc != EOK)
90 goto error;
91
[b36ebb42]92 panel->window = window;
[61784ed]93 list_initialize(&panel->entries);
[be1d74c1]94 panel->entries_cnt = 0;
[6aa85c1]95 *rpanel = panel;
96 return EOK;
[b36ebb42]97error:
[0e80e40]98 if (panel->color != NULL)
99 gfx_color_delete(panel->color);
100 if (panel->curs_color != NULL)
101 gfx_color_delete(panel->curs_color);
[b36ebb42]102 ui_control_delete(panel->control);
103 free(panel);
104 return rc;
[6aa85c1]105}
106
107/** Destroy panel.
108 *
109 * @param panel Panel
110 */
111void panel_destroy(panel_t *panel)
112{
[0e80e40]113 gfx_color_delete(panel->color);
114 gfx_color_delete(panel->curs_color);
115 panel_clear_entries(panel);
[b36ebb42]116 ui_control_delete(panel->control);
[6aa85c1]117 free(panel);
118}
119
[be1d74c1]120/** Paint panel entry.
121 *
122 * @param entry Panel entry
123 * @param entry_idx Entry index (within list of entries)
124 * @return EOK on success or an error code
125 */
126errno_t panel_entry_paint(panel_entry_t *entry, size_t entry_idx)
127{
128 panel_t *panel = entry->panel;
129 gfx_context_t *gc = ui_window_get_gc(panel->window);
130 ui_resource_t *res = ui_window_get_res(panel->window);
131 gfx_font_t *font = ui_resource_get_font(res);
132 gfx_text_fmt_t fmt;
133 gfx_coord2_t pos;
134 gfx_rect_t rect;
135 size_t rows;
136 errno_t rc;
137
138 gfx_text_fmt_init(&fmt);
139
140 rows = panel->rect.p1.y - panel->rect.p0.y - 2;
141
142 /* Do not display entry outside of current page */
143 if (entry_idx < panel->page_idx ||
144 entry_idx >= panel->page_idx + rows)
145 return EOK;
146
147 pos.x = panel->rect.p0.x + 1;
148 pos.y = panel->rect.p0.y + 1 + entry_idx - panel->page_idx;
149
150 if (entry == panel->cursor)
151 fmt.color = panel->curs_color;
152 else
153 fmt.color = panel->color;
154
155 /* Draw entry background */
156 rect.p0 = pos;
157 rect.p1.x = panel->rect.p1.x - 1;
158 rect.p1.y = rect.p0.y + 1;
159
160 rc = gfx_set_color(gc, fmt.color);
161 if (rc != EOK)
162 return rc;
163
164 rc = gfx_fill_rect(gc, &rect);
165 if (rc != EOK)
166 return rc;
167
168 rc = gfx_puttext(font, &pos, &fmt, entry->name);
169 if (rc != EOK)
170 return rc;
171
172 return EOK;
173}
174
[b36ebb42]175/** Paint panel.
176 *
177 * @param panel Panel
178 */
179errno_t panel_paint(panel_t *panel)
180{
181 gfx_context_t *gc = ui_window_get_gc(panel->window);
182 ui_resource_t *res = ui_window_get_res(panel->window);
[0e80e40]183 gfx_text_fmt_t fmt;
184 panel_entry_t *entry;
[be1d74c1]185 int i, lines;
[b36ebb42]186 errno_t rc;
187
[0e80e40]188 gfx_text_fmt_init(&fmt);
189
[b36ebb42]190 rc = gfx_set_color(gc, panel->color);
191 if (rc != EOK)
192 return rc;
193
194 rc = gfx_fill_rect(gc, &panel->rect);
195 if (rc != EOK)
196 return rc;
197
198 rc = ui_paint_text_box(res, &panel->rect, ui_box_single,
199 panel->color);
200 if (rc != EOK)
201 return rc;
202
[be1d74c1]203 lines = panel->rect.p1.y - panel->rect.p0.y - 2;
204 i = 0;
[0e80e40]205
206 entry = panel->page;
[be1d74c1]207 while (entry != NULL && i < lines) {
208 rc = panel_entry_paint(entry, panel->page_idx + i);
[0e80e40]209 if (rc != EOK)
210 return rc;
211
[be1d74c1]212 ++i;
[0e80e40]213 entry = panel_next(entry);
214 }
215
[b36ebb42]216 rc = gfx_update(gc);
217 if (rc != EOK)
218 return rc;
219
220 return EOK;
221}
222
[be1d74c1]223/** Handle panel keyboard event.
224 *
225 * @param panel Panel
226 * @param event Keyboard event
227 * @return ui_claimed iff event was claimed
228 */
229ui_evclaim_t panel_kbd_event(panel_t *panel, kbd_event_t *event)
230{
231 if (event->type == KEY_PRESS) {
232 if ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
233 switch (event->key) {
234 case KC_UP:
235 panel_cursor_up(panel);
236 break;
237 case KC_DOWN:
238 panel_cursor_down(panel);
239 break;
240 case KC_HOME:
241 panel_cursor_top(panel);
242 break;
243 case KC_END:
244 panel_cursor_bottom(panel);
245 break;
246 default:
247 break;
248 }
249 }
250 }
251
252 return ui_unclaimed;
253}
254
[b36ebb42]255/** Handle panel position event.
256 *
257 * @param panel Panel
258 * @param event Position event
259 * @return ui_claimed iff event was claimed
260 */
261ui_evclaim_t panel_pos_event(panel_t *panel, pos_event_t *event)
262{
263 return ui_unclaimed;
264}
265
266/** Get base control for panel.
267 *
268 * @param panel Panel
269 * @return Base UI control
270 */
271ui_control_t *panel_ctl(panel_t *panel)
272{
273 return panel->control;
274}
275
276/** Set panel rectangle.
277 *
278 * @param panel Panel
279 * @param rect Rectangle
280 */
281void panel_set_rect(panel_t *panel, gfx_rect_t *rect)
282{
283 panel->rect = *rect;
284}
285
286/** Destroy panel control.
287 *
288 * @param arg Argument (panel_t *)
289 */
290void panel_ctl_destroy(void *arg)
291{
292 panel_t *panel = (panel_t *) arg;
293
294 panel_destroy(panel);
295}
296
297/** Paint panel control.
298 *
299 * @param arg Argument (panel_t *)
300 * @return EOK on success or an error code
301 */
302errno_t panel_ctl_paint(void *arg)
303{
304 panel_t *panel = (panel_t *) arg;
305
306 return panel_paint(panel);
307}
308
[be1d74c1]309/** Handle panel control keyboard event.
310 *
311 * @param arg Argument (panel_t *)
312 * @param kbd_event Keyboard event
313 * @return @c ui_claimed iff the event is claimed
314 */
315ui_evclaim_t panel_ctl_kbd_event(void *arg, kbd_event_t *event)
316{
317 panel_t *panel = (panel_t *) arg;
318
319 return panel_kbd_event(panel, event);
320}
321
[b36ebb42]322/** Handle panel control position event.
323 *
324 * @param arg Argument (panel_t *)
325 * @param pos_event Position event
326 * @return @c ui_claimed iff the event is claimed
327 */
328ui_evclaim_t panel_ctl_pos_event(void *arg, pos_event_t *event)
329{
330 panel_t *panel = (panel_t *) arg;
331
332 return panel_pos_event(panel, event);
333}
334
[61784ed]335/** Append new panel entry.
336 *
337 * @param panel Panel
338 * @param name File name
339 * @param size File size;
340 * @return EOK on success or an error code
341 */
342errno_t panel_entry_append(panel_t *panel, const char *name, uint64_t size)
343{
344 panel_entry_t *entry;
345
346 entry = calloc(1, sizeof(panel_entry_t));
347 if (entry == NULL)
348 return ENOMEM;
349
350 entry->panel = panel;
351 entry->name = str_dup(name);
352 if (entry->name == NULL) {
353 free(entry);
354 return ENOMEM;
355 }
356
357 entry->size = size;
358 link_initialize(&entry->lentries);
359 list_append(&entry->lentries, &panel->entries);
[be1d74c1]360 ++panel->entries_cnt;
[61784ed]361 return EOK;
362}
363
364/** Delete panel entry.
365 *
366 * @param entry Panel entry
367 */
368void panel_entry_delete(panel_entry_t *entry)
369{
[0e80e40]370 if (entry->panel->cursor == entry)
371 entry->panel->cursor = NULL;
372 if (entry->panel->page == entry)
373 entry->panel->page = NULL;
374
[61784ed]375 list_remove(&entry->lentries);
[be1d74c1]376 --entry->panel->entries_cnt;
[61784ed]377 free(entry->name);
378 free(entry);
379}
380
[0e80e40]381/** Clear panel entry list.
382 *
383 * @param panel Panel
384 */
385void panel_clear_entries(panel_t *panel)
386{
387 panel_entry_t *entry;
388
389 entry = panel_first(panel);
390 while (entry != NULL) {
391 panel_entry_delete(entry);
392 entry = panel_first(panel);
393 }
394}
395
396/** Read directory into panel entry list.
397 *
398 * @param panel Panel
399 * @param dirname Directory path
400 * @return EOK on success or an error code
401 */
402errno_t panel_read_dir(panel_t *panel, const char *dirname)
403{
404 DIR *dir;
405 struct dirent *dirent;
406 errno_t rc;
407
408 dir = opendir(dirname);
409 if (dir == NULL)
410 return errno;
411
412 dirent = readdir(dir);
413 while (dirent != NULL) {
414 rc = panel_entry_append(panel, dirent->d_name, 1);
415 if (rc != EOK)
416 goto error;
417 dirent = readdir(dir);
418 }
419
420 closedir(dir);
421
422 panel->cursor = panel_first(panel);
[be1d74c1]423 panel->cursor_idx = 0;
[0e80e40]424 panel->page = panel_first(panel);
[be1d74c1]425 panel->page_idx = 0;
[0e80e40]426 return EOK;
427error:
428 closedir(dir);
429 return rc;
430}
431
[61784ed]432/** Return first panel entry.
433 *
434 * @panel Panel
435 * @return First panel entry or @c NULL if there are no entries
436 */
437panel_entry_t *panel_first(panel_t *panel)
438{
439 link_t *link;
440
441 link = list_first(&panel->entries);
442 if (link == NULL)
443 return NULL;
444
445 return list_get_instance(link, panel_entry_t, lentries);
446}
447
[be1d74c1]448/** Return last panel entry.
449 *
450 * @panel Panel
451 * @return Last panel entry or @c NULL if there are no entries
452 */
453panel_entry_t *panel_last(panel_t *panel)
454{
455 link_t *link;
456
457 link = list_last(&panel->entries);
458 if (link == NULL)
459 return NULL;
460
461 return list_get_instance(link, panel_entry_t, lentries);
462}
463
[61784ed]464/** Return next panel entry.
465 *
466 * @param cur Current entry
467 * @return Next entry or @c NULL if @a cur is the last entry
468 */
469panel_entry_t *panel_next(panel_entry_t *cur)
470{
471 link_t *link;
472
473 link = list_next(&cur->lentries, &cur->panel->entries);
474 if (link == NULL)
475 return NULL;
476
477 return list_get_instance(link, panel_entry_t, lentries);
478}
[b36ebb42]479
[be1d74c1]480/** Return previous panel entry.
481 *
482 * @param cur Current entry
483 * @return Previous entry or @c NULL if @a cur is the first entry
484 */
485panel_entry_t *panel_prev(panel_entry_t *cur)
486{
487 link_t *link;
488
489 link = list_prev(&cur->lentries, &cur->panel->entries);
490 if (link == NULL)
491 return NULL;
492
493 return list_get_instance(link, panel_entry_t, lentries);
494}
495
496void panel_cursor_move(panel_t *panel, panel_entry_t *entry, size_t entry_idx)
497{
498 gfx_context_t *gc = ui_window_get_gc(panel->window);
499 panel_entry_t *old_cursor;
500 size_t old_idx;
501 size_t rows;
502 panel_entry_t *e;
503 size_t i;
504
505 rows = panel->rect.p1.y - panel->rect.p0.y - 2;
506
507 old_cursor = panel->cursor;
508 old_idx = panel->cursor_idx;
509
510 panel->cursor = entry;
511 panel->cursor_idx = entry_idx;
512
513 if (entry_idx >= panel->page_idx &&
514 entry_idx < panel->page_idx + rows) {
515 /*
516 * If cursor is still on the current page, we're not
517 * scrolling. Just unpaint old cursor and paint new
518 * cursor.
519 */
520 panel_entry_paint(old_cursor, old_idx);
521 panel_entry_paint(panel->cursor, panel->cursor_idx);
522
523 (void) gfx_update(gc);
524 } else {
525 /*
526 * Need to scroll and update all rows.
527 */
528
529 /* Scrolling up */
530 if (entry_idx < panel->page_idx) {
531 panel->page = entry;
532 panel->page_idx = entry_idx;
533 }
534
535 /* Scrolling down */
536 if (entry_idx >= panel->page_idx + rows) {
537 if (entry_idx >= rows) {
538 panel->page_idx = entry_idx - rows + 1;
539 /* Find first page entry (go back rows - 1) */
540 e = entry;
541 for (i = 0; i < rows - 1; i++) {
542 e = panel_prev(e);
543 }
544
545 /* Should be valid */
546 assert(e != NULL);
547 panel->page = e;
548 } else {
549 panel->page = panel_first(panel);
550 panel->page_idx = 0;
551 }
552 }
553
554 (void) panel_paint(panel);
555 }
556}
557
558/** Move cursor one entry up.
559 *
560 * @param panel Panel
561 */
562void panel_cursor_up(panel_t *panel)
563{
564 panel_entry_t *prev;
565 size_t prev_idx;
566
567 prev = panel_prev(panel->cursor);
568 prev_idx = panel->cursor_idx - 1;
569 if (prev != NULL)
570 panel_cursor_move(panel, prev, prev_idx);
571}
572
573/** Move cursor one entry down.
574 *
575 * @param panel Panel
576 */
577void panel_cursor_down(panel_t *panel)
578{
579 panel_entry_t *next;
580 size_t next_idx;
581
582 next = panel_next(panel->cursor);
583 next_idx = panel->cursor_idx + 1;
584 if (next != NULL)
585 panel_cursor_move(panel, next, next_idx);
586}
587
588/** Move cursor to top.
589 *
590 * @param panel Panel
591 */
592void panel_cursor_top(panel_t *panel)
593{
594 panel_cursor_move(panel, panel_first(panel), 0);
595}
596
597/** Move cursor to bottom.
598 *
599 * @param panel Panel
600 */
601void panel_cursor_bottom(panel_t *panel)
602{
603 panel_cursor_move(panel, panel_last(panel), panel->entries_cnt - 1);
604}
605
[6aa85c1]606/** @}
607 */
Note: See TracBrowser for help on using the repository browser.