source: mainline/uspace/app/nav/nav.c

Last change on this file was b336bfd8, checked in by Jiri Svoboda <jiri@…>, 5 months ago

Start text editor if open action is used on .txt file.

  • Property mode set to 100644
File size: 11.2 KB
RevLine 
[748c8bd]1/*
[accdf882]2 * Copyright (c) 2025 Jiri Svoboda
[748c8bd]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.
33 *
34 * HelenOS file manager.
35 */
36
37#include <gfx/coord.h>
38#include <stdio.h>
[5d466a1]39#include <stdlib.h>
[748c8bd]40#include <str.h>
[accdf882]41#include <task.h>
[748c8bd]42#include <ui/fixed.h>
[54ddb59]43#include <ui/filelist.h>
[748c8bd]44#include <ui/resource.h>
45#include <ui/ui.h>
46#include <ui/window.h>
[5d466a1]47#include "menu.h"
[748c8bd]48#include "nav.h"
[b36ebb42]49#include "panel.h"
[748c8bd]50
[accdf882]51#define EDITOR_CMD "/app/edit"
52
[748c8bd]53static void wnd_close(ui_window_t *, void *);
[9f7e9bb]54static void wnd_kbd(ui_window_t *, void *, kbd_event_t *);
[748c8bd]55
56static ui_window_cb_t window_cb = {
[9f7e9bb]57 .close = wnd_close,
58 .kbd = wnd_kbd
[748c8bd]59};
60
[f59212cc]61static void navigator_file_open(void *);
[accdf882]62static void navigator_file_edit(void *);
[f59212cc]63static void navigator_file_exit(void *);
64
65static nav_menu_cb_t navigator_menu_cb = {
66 .file_open = navigator_file_open,
[accdf882]67 .file_edit = navigator_file_edit,
[f59212cc]68 .file_exit = navigator_file_exit
69};
70
[39ab17c]71static void navigator_panel_activate_req(void *, panel_t *);
[b336bfd8]72static void navigator_panel_file_open(void *, panel_t *, const char *);
[39ab17c]73
74static panel_cb_t navigator_panel_cb = {
[b336bfd8]75 .activate_req = navigator_panel_activate_req,
76 .file_open = navigator_panel_file_open
[39ab17c]77};
78
[748c8bd]79/** Window close button was clicked.
80 *
81 * @param window Window
82 * @param arg Argument (navigator)
83 */
84static void wnd_close(ui_window_t *window, void *arg)
85{
86 navigator_t *navigator = (navigator_t *) arg;
87
88 ui_quit(navigator->ui);
89}
90
[9f7e9bb]91/** Window keyboard event handler.
92 *
93 * @param window Window
94 * @param arg Argument (navigator)
95 * @param event Keyboard event
96 */
97static void wnd_kbd(ui_window_t *window, void *arg, kbd_event_t *event)
98{
99 navigator_t *navigator = (navigator_t *) arg;
100
101 if (event->type == KEY_PRESS &&
102 ((event->mods & KM_ALT) == 0) &&
103 ((event->mods & KM_SHIFT) == 0) &&
104 (event->mods & KM_CTRL) != 0) {
[692c7f40]105 switch (event->key) {
[accdf882]106 case KC_E:
107 navigator_file_edit((void *)navigator);
108 break;
[692c7f40]109 case KC_Q:
[9f7e9bb]110 ui_quit(navigator->ui);
[692c7f40]111 break;
112 default:
113 break;
114 }
115 }
116
117 if (event->type == KEY_PRESS &&
118 ((event->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0)) {
119 switch (event->key) {
120 case KC_TAB:
121 navigator_switch_panel(navigator);
122 break;
123 default:
124 break;
125 }
[9f7e9bb]126 }
127
128 ui_window_def_kbd(window, event);
129}
130
[5d466a1]131/** Create navigator.
132 *
133 * @param display_spec Display specification
134 * @param rnavigator Place to store pointer to new navigator
135 * @return EOK on success or ane error code
136 */
[6aa85c1]137errno_t navigator_create(const char *display_spec,
[5d466a1]138 navigator_t **rnavigator)
[748c8bd]139{
[5d466a1]140 navigator_t *navigator;
[748c8bd]141 ui_wnd_params_t params;
[b36ebb42]142 gfx_rect_t rect;
[8edec53]143 gfx_rect_t arect;
144 gfx_coord_t pw;
[b36ebb42]145 unsigned i;
[748c8bd]146 errno_t rc;
147
[5d466a1]148 navigator = calloc(1, sizeof(navigator_t));
149 if (navigator == NULL)
150 return ENOMEM;
151
152 rc = ui_create(display_spec, &navigator->ui);
[748c8bd]153 if (rc != EOK) {
154 printf("Error creating UI on display %s.\n", display_spec);
[5d466a1]155 goto error;
[748c8bd]156 }
157
158 ui_wnd_params_init(&params);
159 params.caption = "Navigator";
[5d466a1]160 params.style &= ~ui_wds_decorated;
161 params.placement = ui_wnd_place_full_screen;
[748c8bd]162
[5d466a1]163 rc = ui_window_create(navigator->ui, &params, &navigator->window);
[748c8bd]164 if (rc != EOK) {
165 printf("Error creating window.\n");
[5d466a1]166 goto error;
[748c8bd]167 }
168
[5d466a1]169 ui_window_set_cb(navigator->window, &window_cb, (void *) navigator);
[8edec53]170 ui_window_get_app_rect(navigator->window, &arect);
[748c8bd]171
[5d466a1]172 rc = ui_fixed_create(&navigator->fixed);
[748c8bd]173 if (rc != EOK) {
174 printf("Error creating fixed layout.\n");
[5d466a1]175 goto error;
[748c8bd]176 }
177
[5d466a1]178 ui_window_add(navigator->window, ui_fixed_ctl(navigator->fixed));
[748c8bd]179
[6aa85c1]180 rc = nav_menu_create(navigator->window, &navigator->menu);
[5d466a1]181 if (rc != EOK)
182 goto error;
[748c8bd]183
[f59212cc]184 nav_menu_set_cb(navigator->menu, &navigator_menu_cb,
185 (void *)navigator);
186
[6aa85c1]187 rc = ui_fixed_add(navigator->fixed, nav_menu_ctl(navigator->menu));
188 if (rc != EOK) {
189 printf("Error adding control to layout.\n");
190 return rc;
191 }
192
[8edec53]193 /* Panel width */
194 pw = (arect.p1.x - arect.p0.x) / 2;
195
[b36ebb42]196 for (i = 0; i < 2; i++) {
[692c7f40]197 rc = panel_create(navigator->window, i == 0,
198 &navigator->panel[i]);
[b36ebb42]199 if (rc != EOK)
200 goto error;
201
[8edec53]202 rect.p0.x = arect.p0.x + pw * i;
203 rect.p0.y = arect.p0.y + 1;
204 rect.p1.x = arect.p0.x + pw * (i + 1);
205 rect.p1.y = arect.p1.y - 1;
[b36ebb42]206 panel_set_rect(navigator->panel[i], &rect);
207
[39ab17c]208 panel_set_cb(navigator->panel[i], &navigator_panel_cb,
209 navigator);
210
[b36ebb42]211 rc = ui_fixed_add(navigator->fixed,
212 panel_ctl(navigator->panel[i]));
213 if (rc != EOK) {
214 printf("Error adding control to layout.\n");
[0e80e40]215 goto error;
216 }
217
218 rc = panel_read_dir(navigator->panel[i], ".");
219 if (rc != EOK) {
220 printf("Error reading directory.\n");
221 goto error;
[b36ebb42]222 }
223 }
224
[5d466a1]225 rc = ui_window_paint(navigator->window);
[748c8bd]226 if (rc != EOK) {
[5d466a1]227 printf("Error painting window.\n");
228 goto error;
[748c8bd]229 }
230
[5d466a1]231 *rnavigator = navigator;
232 return EOK;
233error:
234 navigator_destroy(navigator);
235 return rc;
236}
[748c8bd]237
[6aa85c1]238void navigator_destroy(navigator_t *navigator)
[5d466a1]239{
[b36ebb42]240 unsigned i;
241
242 for (i = 0; i < 2; i++) {
[1aa8c86]243 if (navigator->panel[i] != NULL) {
244 ui_fixed_remove(navigator->fixed,
245 panel_ctl(navigator->panel[i]));
246 panel_destroy(navigator->panel[i]);
247 }
[b36ebb42]248 }
249
[1aa8c86]250 if (navigator->menu != NULL) {
251 ui_fixed_remove(navigator->fixed, nav_menu_ctl(navigator->menu));
[5d466a1]252 nav_menu_destroy(navigator->menu);
[1aa8c86]253 }
254
[5d466a1]255 if (navigator->window != NULL)
256 ui_window_destroy(navigator->window);
257 if (navigator->ui != NULL)
258 ui_destroy(navigator->ui);
259 free(navigator);
260}
261
262/** Run navigator on the specified display. */
[6aa85c1]263errno_t navigator_run(const char *display_spec)
[5d466a1]264{
265 navigator_t *navigator;
266 errno_t rc;
[748c8bd]267
[5d466a1]268 rc = navigator_create(display_spec, &navigator);
269 if (rc != EOK)
270 return rc;
[748c8bd]271
[5d466a1]272 ui_run(navigator->ui);
[748c8bd]273
[5d466a1]274 navigator_destroy(navigator);
[748c8bd]275 return EOK;
276}
277
[692c7f40]278/** Get the currently active navigator panel.
279 *
280 * @param navigator Navigator
281 * @return Currently active panel
282 */
283panel_t *navigator_get_active_panel(navigator_t *navigator)
284{
285 int i;
286
287 for (i = 0; i < navigator_panels; i++) {
288 if (panel_is_active(navigator->panel[i]))
289 return navigator->panel[i];
290 }
291
292 /* This should not happen */
293 assert(false);
294 return NULL;
295}
296
297/** Switch to another navigator panel.
298 *
299 * Changes the currently active navigator panel to the next panel.
300 *
301 * @param navigator Navigator
302 */
303void navigator_switch_panel(navigator_t *navigator)
304{
[4fcc2de]305 errno_t rc;
306
[692c7f40]307 if (panel_is_active(navigator->panel[0])) {
[4fcc2de]308 rc = panel_activate(navigator->panel[1]);
309 if (rc != EOK)
310 return;
[692c7f40]311 panel_deactivate(navigator->panel[0]);
312 } else {
[4fcc2de]313 rc = panel_activate(navigator->panel[0]);
314 if (rc != EOK)
315 return;
[692c7f40]316 panel_deactivate(navigator->panel[1]);
317 }
318}
319
[f59212cc]320/** File / Open menu entry selected */
321static void navigator_file_open(void *arg)
322{
323 navigator_t *navigator = (navigator_t *)arg;
324 panel_t *panel;
325
326 panel = navigator_get_active_panel(navigator);
[54ddb59]327 ui_file_list_open(panel->flist, ui_file_list_get_cursor(panel->flist));
[f59212cc]328}
329
[accdf882]330/** Open file in text editor.
331 *
[b336bfd8]332 * @param navigator Navigator
[accdf882]333 * @param fname File name
334 *
335 * @return EOK on success or an error code
336 */
337static errno_t navigator_edit_file(navigator_t *navigator, const char *fname)
338{
339 task_id_t id;
340 task_wait_t wait;
341 task_exit_t texit;
342 int retval;
343 errno_t rc;
344
345 /* Free up and clean console for the child task. */
346 rc = ui_suspend(navigator->ui);
347 if (rc != EOK)
348 return rc;
349
350 rc = task_spawnl(&id, &wait, EDITOR_CMD, EDITOR_CMD, fname, NULL);
351 if (rc != EOK)
352 goto error;
353
354 rc = task_wait(&wait, &texit, &retval);
355 if ((rc != EOK) || (texit != TASK_EXIT_NORMAL))
356 goto error;
357
358 /* Resume UI operation */
359 rc = ui_resume(navigator->ui);
360 if (rc != EOK)
361 return rc;
362
363 (void) ui_paint(navigator->ui);
364 return EOK;
365error:
366 (void) ui_resume(navigator->ui);
367 (void) ui_paint(navigator->ui);
368 return rc;
369}
370
[b336bfd8]371/** Execute file entry.
372 *
373 * @param navigator Navigator
374 * @param fname File name
375 *
376 * @return EOK on success or an error code
377 */
378static errno_t navigator_exec_file(navigator_t *navigator, const char *fname)
379{
380 task_id_t id;
381 task_wait_t wait;
382 task_exit_t texit;
383 int retval;
384 errno_t rc;
385
386 /* Free up and clean console for the child task. */
387 rc = ui_suspend(navigator->ui);
388 if (rc != EOK)
389 return rc;
390
391 rc = task_spawnl(&id, &wait, fname, fname, NULL);
392 if (rc != EOK)
393 goto error;
394
395 rc = task_wait(&wait, &texit, &retval);
396 if ((rc != EOK) || (texit != TASK_EXIT_NORMAL))
397 goto error;
398
399 /* Resume UI operation */
400 rc = ui_resume(navigator->ui);
401 if (rc != EOK)
402 return rc;
403
404 (void) ui_paint(navigator->ui);
405 return EOK;
406error:
407 (void) ui_resume(navigator->ui);
408 (void) ui_paint(navigator->ui);
409 return rc;
410}
411
412/** Open panel file entry.
413 *
414 * Perform Open action on a file entry (based on extension).
415 *
416 * @param navigator Navigator
417 * @param fname File name
418 *
419 * @return EOK on success or an error code
420 */
421static errno_t navigator_open_file(navigator_t *navigator, const char *fname)
422{
423 const char *ext;
424
425 ext = str_rchr(fname, '.');
426 if (ext != NULL) {
427 if (str_casecmp(ext, ".txt") == 0)
428 return navigator_edit_file(navigator, fname);
429 }
430
431 return navigator_exec_file(navigator, fname);
432}
433
[accdf882]434/** File / Edit menu entry selected */
435static void navigator_file_edit(void *arg)
436{
437 navigator_t *navigator = (navigator_t *)arg;
438 ui_file_list_entry_t *entry;
439 ui_file_list_entry_attr_t attr;
440 panel_t *panel;
441
442 panel = navigator_get_active_panel(navigator);
443 entry = ui_file_list_get_cursor(panel->flist);
444 ui_file_list_entry_get_attr(entry, &attr);
445
446 (void)navigator_edit_file(navigator, attr.name);
447}
448
[f59212cc]449/** File / Exit menu entry selected */
450static void navigator_file_exit(void *arg)
451{
452 navigator_t *navigator = (navigator_t *)arg;
453
454 ui_quit(navigator->ui);
455}
456
[39ab17c]457/** Panel callback requesting panel activation.
458 *
459 * @param arg Argument (navigator_t *)
460 * @param panel Panel
461 */
462void navigator_panel_activate_req(void *arg, panel_t *panel)
463{
464 navigator_t *navigator = (navigator_t *)arg;
465
466 if (!panel_is_active(panel))
467 navigator_switch_panel(navigator);
468}
469
[b336bfd8]470/** Panel callback requesting file open.
471 *
472 * @param arg Argument (navigator_t *)
473 * @param panel Panel
474 * @param fname File name
475 */
476void navigator_panel_file_open(void *arg, panel_t *panel, const char *fname)
477{
478 navigator_t *navigator = (navigator_t *)arg;
479
480 (void)panel;
481 navigator_open_file(navigator, fname);
482}
483
[748c8bd]484/** @}
485 */
Note: See TracBrowser for help on using the repository browser.