source: mainline/uspace/app/nav/nav.c@ 32ae27bb

Last change on this file since 32ae27bb was 32ae27bb, checked in by Jiri Svoboda <jiri@…>, 2 weeks ago

Suggest unique new file name.

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