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
Line 
1/*
2 * Copyright (c) 2025 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.
33 *
34 * HelenOS file manager.
35 */
36
37#include <gfx/coord.h>
38#include <stdio.h>
39#include <stdlib.h>
40#include <str.h>
41#include <task.h>
42#include <ui/fixed.h>
43#include <ui/filelist.h>
44#include <ui/resource.h>
45#include <ui/ui.h>
46#include <ui/window.h>
47#include "menu.h"
48#include "nav.h"
49#include "panel.h"
50
51#define EDITOR_CMD "/app/edit"
52
53static void wnd_close(ui_window_t *, void *);
54static void wnd_kbd(ui_window_t *, void *, kbd_event_t *);
55
56static ui_window_cb_t window_cb = {
57 .close = wnd_close,
58 .kbd = wnd_kbd
59};
60
61static void navigator_file_open(void *);
62static void navigator_file_edit(void *);
63static void navigator_file_exit(void *);
64
65static nav_menu_cb_t navigator_menu_cb = {
66 .file_open = navigator_file_open,
67 .file_edit = navigator_file_edit,
68 .file_exit = navigator_file_exit
69};
70
71static void navigator_panel_activate_req(void *, panel_t *);
72static void navigator_panel_file_open(void *, panel_t *, const char *);
73
74static panel_cb_t navigator_panel_cb = {
75 .activate_req = navigator_panel_activate_req,
76 .file_open = navigator_panel_file_open
77};
78
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
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) {
105 switch (event->key) {
106 case KC_E:
107 navigator_file_edit((void *)navigator);
108 break;
109 case KC_Q:
110 ui_quit(navigator->ui);
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 }
126 }
127
128 ui_window_def_kbd(window, event);
129}
130
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 */
137errno_t navigator_create(const char *display_spec,
138 navigator_t **rnavigator)
139{
140 navigator_t *navigator;
141 ui_wnd_params_t params;
142 gfx_rect_t rect;
143 gfx_rect_t arect;
144 gfx_coord_t pw;
145 unsigned i;
146 errno_t rc;
147
148 navigator = calloc(1, sizeof(navigator_t));
149 if (navigator == NULL)
150 return ENOMEM;
151
152 rc = ui_create(display_spec, &navigator->ui);
153 if (rc != EOK) {
154 printf("Error creating UI on display %s.\n", display_spec);
155 goto error;
156 }
157
158 ui_wnd_params_init(&params);
159 params.caption = "Navigator";
160 params.style &= ~ui_wds_decorated;
161 params.placement = ui_wnd_place_full_screen;
162
163 rc = ui_window_create(navigator->ui, &params, &navigator->window);
164 if (rc != EOK) {
165 printf("Error creating window.\n");
166 goto error;
167 }
168
169 ui_window_set_cb(navigator->window, &window_cb, (void *) navigator);
170 ui_window_get_app_rect(navigator->window, &arect);
171
172 rc = ui_fixed_create(&navigator->fixed);
173 if (rc != EOK) {
174 printf("Error creating fixed layout.\n");
175 goto error;
176 }
177
178 ui_window_add(navigator->window, ui_fixed_ctl(navigator->fixed));
179
180 rc = nav_menu_create(navigator->window, &navigator->menu);
181 if (rc != EOK)
182 goto error;
183
184 nav_menu_set_cb(navigator->menu, &navigator_menu_cb,
185 (void *)navigator);
186
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
193 /* Panel width */
194 pw = (arect.p1.x - arect.p0.x) / 2;
195
196 for (i = 0; i < 2; i++) {
197 rc = panel_create(navigator->window, i == 0,
198 &navigator->panel[i]);
199 if (rc != EOK)
200 goto error;
201
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;
206 panel_set_rect(navigator->panel[i], &rect);
207
208 panel_set_cb(navigator->panel[i], &navigator_panel_cb,
209 navigator);
210
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");
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;
222 }
223 }
224
225 rc = ui_window_paint(navigator->window);
226 if (rc != EOK) {
227 printf("Error painting window.\n");
228 goto error;
229 }
230
231 *rnavigator = navigator;
232 return EOK;
233error:
234 navigator_destroy(navigator);
235 return rc;
236}
237
238void navigator_destroy(navigator_t *navigator)
239{
240 unsigned i;
241
242 for (i = 0; i < 2; i++) {
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 }
248 }
249
250 if (navigator->menu != NULL) {
251 ui_fixed_remove(navigator->fixed, nav_menu_ctl(navigator->menu));
252 nav_menu_destroy(navigator->menu);
253 }
254
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. */
263errno_t navigator_run(const char *display_spec)
264{
265 navigator_t *navigator;
266 errno_t rc;
267
268 rc = navigator_create(display_spec, &navigator);
269 if (rc != EOK)
270 return rc;
271
272 ui_run(navigator->ui);
273
274 navigator_destroy(navigator);
275 return EOK;
276}
277
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{
305 errno_t rc;
306
307 if (panel_is_active(navigator->panel[0])) {
308 rc = panel_activate(navigator->panel[1]);
309 if (rc != EOK)
310 return;
311 panel_deactivate(navigator->panel[0]);
312 } else {
313 rc = panel_activate(navigator->panel[0]);
314 if (rc != EOK)
315 return;
316 panel_deactivate(navigator->panel[1]);
317 }
318}
319
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);
327 ui_file_list_open(panel->flist, ui_file_list_get_cursor(panel->flist));
328}
329
330/** Open file in text editor.
331 *
332 * @param navigator Navigator
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
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
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
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
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
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
484/** @}
485 */
Note: See TracBrowser for help on using the repository browser.