source: mainline/uspace/app/edit/edit.c@ ec50d65e

Last change on this file since ec50d65e was ec50d65e, checked in by Jiri Svoboda <jiri@…>, 18 months ago

Editor needs to hide cursor when menu bar is activated

  • Property mode set to 100644
File size: 55.8 KB
Line 
1/*
2 * Copyright (c) 2024 Jiri Svoboda
3 * Copyright (c) 2012 Martin Sucha
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup edit
31 * @brief Text editor.
32 * @{
33 */
34/**
35 * @file
36 */
37
38#include <align.h>
39#include <clipboard.h>
40#include <errno.h>
41#include <gfx/color.h>
42#include <gfx/cursor.h>
43#include <gfx/font.h>
44#include <gfx/render.h>
45#include <gfx/text.h>
46#include <io/kbd_event.h>
47#include <io/keycode.h>
48#include <io/pos_event.h>
49#include <io/style.h>
50#include <macros.h>
51#include <stdio.h>
52#include <stdlib.h>
53#include <stddef.h>
54#include <stdbool.h>
55#include <types/common.h>
56#include <ui/control.h>
57#include <ui/filedialog.h>
58#include <ui/fixed.h>
59#include <ui/label.h>
60#include <ui/menu.h>
61#include <ui/menubar.h>
62#include <ui/menudd.h>
63#include <ui/menuentry.h>
64#include <ui/promptdialog.h>
65#include <ui/resource.h>
66#include <ui/ui.h>
67#include <ui/window.h>
68#include <vfs/vfs.h>
69
70#include "sheet.h"
71#include "search.h"
72
73enum redraw_flags {
74 REDRAW_TEXT = (1 << 0),
75 REDRAW_ROW = (1 << 1),
76 REDRAW_STATUS = (1 << 2),
77 REDRAW_CARET = (1 << 3)
78};
79
80/** Pane
81 *
82 * A rectangular area of the screen used to edit a document. Different
83 * panes can be possibly used to edit the same document. This is a custom
84 * UI control.
85 */
86typedef struct {
87 /** Base control object */
88 struct ui_control *control;
89
90 /** Containing window */
91 ui_window_t *window;
92
93 /** UI resource */
94 struct ui_resource *res;
95
96 /** Pane rectangle */
97 gfx_rect_t rect;
98
99 /** Pane color */
100 gfx_color_t *color;
101
102 /** Selection color */
103 gfx_color_t *sel_color;
104
105 /* Pane dimensions */
106 int rows, columns;
107
108 /* Position of the visible area */
109 int sh_row, sh_column;
110
111 /** Bitmask of components that need redrawing */
112 enum redraw_flags rflags;
113
114 /** Current position of the caret */
115 tag_t caret_pos;
116
117 /** Start of selection */
118 tag_t sel_start;
119
120 /** Active keyboard modifiers */
121 keymod_t keymod;
122
123 /**
124 * Ideal column where the caret should try to get. This is used
125 * for maintaining the same column during vertical movement.
126 */
127 int ideal_column;
128
129 bool search_reverse;
130 char *previous_search;
131 bool previous_search_reverse;
132} pane_t;
133
134/** Text editor */
135typedef struct {
136 /** User interface */
137 ui_t *ui;
138 /** Editor window */
139 ui_window_t *window;
140 /** UI resource */
141 ui_resource_t *ui_res;
142 /** Menu bar */
143 ui_menu_bar_t *menubar;
144 /** Status bar */
145 ui_label_t *status;
146} edit_t;
147
148/** Document
149 *
150 * Associates a sheet with a file where it can be saved to.
151 */
152typedef struct {
153 char *file_name;
154 sheet_t *sh;
155} doc_t;
156
157static edit_t edit;
158static doc_t doc;
159static pane_t pane;
160
161#define ROW_BUF_SIZE 4096
162#define BUF_SIZE 64
163#define TAB_WIDTH 8
164
165/** Maximum filename length that can be entered. */
166#define INFNAME_MAX_LEN 128
167
168static void cursor_setvis(bool visible);
169
170static void key_handle_press(kbd_event_t *ev);
171static void key_handle_unmod(kbd_event_t const *ev);
172static void key_handle_ctrl(kbd_event_t const *ev);
173static void key_handle_shift(kbd_event_t const *ev);
174static void key_handle_shift_ctrl(kbd_event_t const *ev);
175static void key_handle_movement(unsigned int key, bool shift);
176
177static void pos_handle(pos_event_t *ev);
178
179static errno_t file_save(char const *fname);
180static void file_save_as(void);
181static errno_t file_insert(char *fname);
182static errno_t file_save_range(char const *fname, spt_t const *spos,
183 spt_t const *epos);
184static char *range_get_str(spt_t const *spos, spt_t const *epos);
185
186static errno_t pane_init(ui_window_t *, pane_t *);
187static void pane_fini(pane_t *);
188static ui_control_t *pane_ctl(pane_t *);
189static errno_t pane_update(pane_t *);
190static errno_t pane_text_display(pane_t *);
191static void pane_row_display(void);
192static errno_t pane_row_range_display(pane_t *, int r0, int r1);
193static void pane_status_display(pane_t *);
194static void pane_caret_display(pane_t *);
195
196static void insert_char(char32_t c);
197static void delete_char_before(void);
198static void delete_char_after(void);
199static void caret_update(void);
200static void caret_move_relative(int drow, int dcolumn, enum dir_spec align_dir, bool select);
201static void caret_move_absolute(int row, int column, enum dir_spec align_dir, bool select);
202static void caret_move(spt_t spt, bool select, bool update_ideal_column);
203static void caret_move_word_left(bool select);
204static void caret_move_word_right(bool select);
205static void caret_go_to_line_ask(void);
206
207static bool selection_active(void);
208static void selection_sel_all(void);
209static void selection_sel_range(spt_t pa, spt_t pb);
210static void selection_get_points(spt_t *pa, spt_t *pb);
211static void selection_delete(void);
212static void selection_copy(void);
213static void edit_cut(void);
214static void edit_paste(void);
215static void insert_clipboard_data(void);
216
217static void search(char *pattern, bool reverse);
218static void search_prompt(bool reverse);
219static void search_repeat(void);
220
221static void pt_get_sof(spt_t *pt);
222static void pt_get_eof(spt_t *pt);
223static void pt_get_sol(spt_t *cpt, spt_t *spt);
224static void pt_get_eol(spt_t *cpt, spt_t *ept);
225static bool pt_is_word_beginning(spt_t *pt);
226static bool pt_is_delimiter(spt_t *pt);
227static bool pt_is_punctuation(spt_t *pt);
228static spt_t pt_find_word_left(spt_t spt);
229static spt_t pt_find_word_left(spt_t spt);
230
231static int tag_cmp(tag_t const *a, tag_t const *b);
232static int spt_cmp(spt_t const *a, spt_t const *b);
233static int coord_cmp(coord_t const *a, coord_t const *b);
234
235static void status_display(char const *str);
236static errno_t edit_ui_create(edit_t *);
237static void edit_ui_destroy(edit_t *);
238
239static void edit_wnd_close(ui_window_t *, void *);
240static void edit_wnd_focus(ui_window_t *, void *, unsigned);
241static void edit_wnd_kbd_event(ui_window_t *, void *, kbd_event_t *);
242static void edit_wnd_unfocus(ui_window_t *, void *, unsigned);
243
244static ui_window_cb_t edit_window_cb = {
245 .close = edit_wnd_close,
246 .focus = edit_wnd_focus,
247 .kbd = edit_wnd_kbd_event,
248 .unfocus = edit_wnd_unfocus
249};
250
251static void edit_menubar_activate(ui_menu_bar_t *, void *);
252static void edit_menubar_deactivate(ui_menu_bar_t *, void *);
253
254static ui_menu_bar_cb_t edit_menubar_cb = {
255 .activate = edit_menubar_activate,
256 .deactivate = edit_menubar_deactivate
257};
258
259static void edit_file_save(ui_menu_entry_t *, void *);
260static void edit_file_save_as(ui_menu_entry_t *, void *);
261static void edit_file_exit(ui_menu_entry_t *, void *);
262static void edit_edit_cut(ui_menu_entry_t *, void *);
263static void edit_edit_copy(ui_menu_entry_t *, void *);
264static void edit_edit_paste(ui_menu_entry_t *, void *);
265static void edit_edit_delete(ui_menu_entry_t *, void *);
266static void edit_edit_select_all(ui_menu_entry_t *, void *);
267static void edit_search_find(ui_menu_entry_t *, void *);
268static void edit_search_reverse_find(ui_menu_entry_t *, void *);
269static void edit_search_find_next(ui_menu_entry_t *, void *);
270static void edit_search_go_to_line(ui_menu_entry_t *, void *);
271
272static void pane_ctl_destroy(void *);
273static errno_t pane_ctl_paint(void *);
274static ui_evclaim_t pane_ctl_pos_event(void *, pos_event_t *);
275
276/** Pabe control ops */
277ui_control_ops_t pane_ctl_ops = {
278 .destroy = pane_ctl_destroy,
279 .paint = pane_ctl_paint,
280 .pos_event = pane_ctl_pos_event
281};
282
283static void save_as_dialog_bok(ui_file_dialog_t *, void *, const char *);
284static void save_as_dialog_bcancel(ui_file_dialog_t *, void *);
285static void save_as_dialog_close(ui_file_dialog_t *, void *);
286
287static ui_file_dialog_cb_t save_as_dialog_cb = {
288 .bok = save_as_dialog_bok,
289 .bcancel = save_as_dialog_bcancel,
290 .close = save_as_dialog_close
291};
292
293static void go_to_line_dialog_bok(ui_prompt_dialog_t *, void *, const char *);
294static void go_to_line_dialog_bcancel(ui_prompt_dialog_t *, void *);
295static void go_to_line_dialog_close(ui_prompt_dialog_t *, void *);
296
297static ui_prompt_dialog_cb_t go_to_line_dialog_cb = {
298 .bok = go_to_line_dialog_bok,
299 .bcancel = go_to_line_dialog_bcancel,
300 .close = go_to_line_dialog_close
301};
302
303static void search_dialog_bok(ui_prompt_dialog_t *, void *, const char *);
304static void search_dialog_bcancel(ui_prompt_dialog_t *, void *);
305static void search_dialog_close(ui_prompt_dialog_t *, void *);
306
307static ui_prompt_dialog_cb_t search_dialog_cb = {
308 .bok = search_dialog_bok,
309 .bcancel = search_dialog_bcancel,
310 .close = search_dialog_close
311};
312
313int main(int argc, char *argv[])
314{
315 bool new_file;
316 errno_t rc;
317
318 pane.sh_row = 1;
319 pane.sh_column = 1;
320
321 /* Start with an empty sheet. */
322 rc = sheet_create(&doc.sh);
323 if (rc != EOK) {
324 printf("Out of memory.\n");
325 return -1;
326 }
327
328 /* Place caret at the beginning of file. */
329 spt_t sof;
330 pt_get_sof(&sof);
331 sheet_place_tag(doc.sh, &sof, &pane.caret_pos);
332 pane.ideal_column = 1;
333
334 if (argc == 2) {
335 doc.file_name = str_dup(argv[1]);
336 } else if (argc > 1) {
337 printf("Invalid arguments.\n");
338 return -2;
339 } else {
340 doc.file_name = NULL;
341 }
342
343 new_file = false;
344
345 if (doc.file_name == NULL || file_insert(doc.file_name) != EOK)
346 new_file = true;
347
348 /* Place selection start tag. */
349 sheet_place_tag(doc.sh, &sof, &pane.sel_start);
350
351 /* Move to beginning of file. */
352 pt_get_sof(&sof);
353
354 /* Create UI */
355 rc = edit_ui_create(&edit);
356 if (rc != EOK)
357 return 1;
358
359 caret_move(sof, true, true);
360
361 /* Initial display */
362 rc = ui_window_paint(edit.window);
363 if (rc != EOK) {
364 printf("Error painting window.\n");
365 return rc;
366 }
367
368 pane_status_display(&pane);
369 if (new_file && doc.file_name != NULL)
370 status_display("File not found. Starting empty file.");
371 pane_caret_display(&pane);
372 cursor_setvis(true);
373
374 ui_run(edit.ui);
375
376 edit_ui_destroy(&edit);
377 return 0;
378}
379
380/** Create text editor UI.
381 *
382 * @param edit Editor
383 * @return EOK on success or an error code
384 */
385static errno_t edit_ui_create(edit_t *edit)
386{
387 errno_t rc;
388 ui_wnd_params_t params;
389 ui_fixed_t *fixed = NULL;
390 ui_menu_t *mfile = NULL;
391 ui_menu_t *medit = NULL;
392 ui_menu_entry_t *msave = NULL;
393 ui_menu_entry_t *msaveas = NULL;
394 ui_menu_entry_t *mfsep = NULL;
395 ui_menu_entry_t *mexit = NULL;
396 ui_menu_entry_t *mcut = NULL;
397 ui_menu_entry_t *mcopy = NULL;
398 ui_menu_entry_t *mpaste = NULL;
399 ui_menu_entry_t *mdelete = NULL;
400 ui_menu_entry_t *mesep = NULL;
401 ui_menu_entry_t *mselall = NULL;
402 ui_menu_t *msearch = NULL;
403 ui_menu_entry_t *mfind = NULL;
404 ui_menu_entry_t *mfindr = NULL;
405 ui_menu_entry_t *mfindn = NULL;
406 ui_menu_entry_t *mssep = NULL;
407 ui_menu_entry_t *mgoto = NULL;
408 gfx_rect_t arect;
409 gfx_rect_t rect;
410
411 rc = ui_create(UI_CONSOLE_DEFAULT, &edit->ui);
412 if (rc != EOK) {
413 printf("Error creating UI on display %s.\n",
414 UI_CONSOLE_DEFAULT);
415 goto error;
416 }
417
418 ui_wnd_params_init(&params);
419 params.caption = "Text Editor";
420 params.style &= ~ui_wds_decorated;
421 params.placement = ui_wnd_place_full_screen;
422
423 rc = ui_window_create(edit->ui, &params, &edit->window);
424 if (rc != EOK) {
425 printf("Error creating window.\n");
426 goto error;
427 }
428
429 ui_window_set_cb(edit->window, &edit_window_cb, (void *) edit);
430
431 edit->ui_res = ui_window_get_res(edit->window);
432
433 rc = ui_fixed_create(&fixed);
434 if (rc != EOK) {
435 printf("Error creating fixed layout.\n");
436 return rc;
437 }
438
439 rc = ui_menu_bar_create(edit->ui, edit->window, &edit->menubar);
440 if (rc != EOK) {
441 printf("Error creating menu bar.\n");
442 return rc;
443 }
444
445 ui_menu_bar_set_cb(edit->menubar, &edit_menubar_cb, (void *) edit);
446
447 rc = ui_menu_dd_create(edit->menubar, "~F~ile", NULL, &mfile);
448 if (rc != EOK) {
449 printf("Error creating menu.\n");
450 return rc;
451 }
452
453 rc = ui_menu_entry_create(mfile, "~S~ave", "Ctrl-S", &msave);
454 if (rc != EOK) {
455 printf("Error creating menu.\n");
456 return rc;
457 }
458
459 ui_menu_entry_set_cb(msave, edit_file_save, (void *) edit);
460
461 rc = ui_menu_entry_create(mfile, "Save ~A~s", "Ctrl-E", &msaveas);
462 if (rc != EOK) {
463 printf("Error creating menu.\n");
464 return rc;
465 }
466
467 ui_menu_entry_set_cb(msaveas, edit_file_save_as, (void *) edit);
468
469 rc = ui_menu_entry_sep_create(mfile, &mfsep);
470 if (rc != EOK) {
471 printf("Error creating menu.\n");
472 return rc;
473 }
474
475 rc = ui_menu_entry_create(mfile, "E~x~it", "Ctrl-Q", &mexit);
476 if (rc != EOK) {
477 printf("Error creating menu.\n");
478 return rc;
479 }
480
481 ui_menu_entry_set_cb(mexit, edit_file_exit, (void *) edit);
482
483 rc = ui_menu_dd_create(edit->menubar, "~E~dit", NULL, &medit);
484 if (rc != EOK) {
485 printf("Error creating menu.\n");
486 return rc;
487 }
488
489 rc = ui_menu_entry_create(medit, "Cu~t~", "Ctrl-X", &mcut);
490 if (rc != EOK) {
491 printf("Error creating menu.\n");
492 return rc;
493 }
494
495 ui_menu_entry_set_cb(mcut, edit_edit_cut, (void *) edit);
496
497 rc = ui_menu_entry_create(medit, "~C~opy", "Ctrl-C", &mcopy);
498 if (rc != EOK) {
499 printf("Error creating menu.\n");
500 return rc;
501 }
502
503 ui_menu_entry_set_cb(mcopy, edit_edit_copy, (void *) edit);
504
505 rc = ui_menu_entry_create(medit, "~P~aste", "Ctrl-V", &mpaste);
506 if (rc != EOK) {
507 printf("Error creating menu.\n");
508 return rc;
509 }
510
511 ui_menu_entry_set_cb(mpaste, edit_edit_paste, (void *) edit);
512
513 rc = ui_menu_entry_create(medit, "~D~elete", "Del", &mdelete);
514 if (rc != EOK) {
515 printf("Error creating menu.\n");
516 return rc;
517 }
518
519 ui_menu_entry_set_cb(mdelete, edit_edit_delete, (void *) edit);
520
521 rc = ui_menu_entry_sep_create(medit, &mesep);
522 if (rc != EOK) {
523 printf("Error creating menu.\n");
524 return rc;
525 }
526
527 rc = ui_menu_entry_create(medit, "Select ~A~ll", "Ctrl-A", &mselall);
528 if (rc != EOK) {
529 printf("Error creating menu.\n");
530 return rc;
531 }
532
533 ui_menu_entry_set_cb(mselall, edit_edit_select_all, (void *) edit);
534
535 rc = ui_menu_dd_create(edit->menubar, "~S~earch", NULL, &msearch);
536 if (rc != EOK) {
537 printf("Error creating menu.\n");
538 return rc;
539 }
540
541 rc = ui_menu_entry_create(msearch, "~F~ind", "Ctrl-F", &mfind);
542 if (rc != EOK) {
543 printf("Error creating menu.\n");
544 return rc;
545 }
546
547 ui_menu_entry_set_cb(mfind, edit_search_find, (void *) edit);
548
549 rc = ui_menu_entry_create(msearch, "~R~everse Find", "Ctrl-Shift-F", &mfindr);
550 if (rc != EOK) {
551 printf("Error creating menu.\n");
552 return rc;
553 }
554
555 ui_menu_entry_set_cb(mfindr, edit_search_reverse_find, (void *) edit);
556
557 rc = ui_menu_entry_create(msearch, "Find ~N~ext", "Ctrl-N", &mfindn);
558 if (rc != EOK) {
559 printf("Error creating menu.\n");
560 return rc;
561 }
562
563 ui_menu_entry_set_cb(mfindn, edit_search_find_next, (void *) edit);
564
565 rc = ui_menu_entry_sep_create(msearch, &mssep);
566 if (rc != EOK) {
567 printf("Error creating menu.\n");
568 return rc;
569 }
570
571 rc = ui_menu_entry_create(msearch, "Go To ~L~ine", "Ctrl-L", &mgoto);
572 if (rc != EOK) {
573 printf("Error creating menu.\n");
574 return rc;
575 }
576
577 ui_menu_entry_set_cb(mgoto, edit_search_go_to_line, (void *) edit);
578
579 ui_window_get_app_rect(edit->window, &arect);
580
581 rect.p0 = arect.p0;
582 rect.p1.x = arect.p1.x;
583 rect.p1.y = arect.p0.y + 1;
584 ui_menu_bar_set_rect(edit->menubar, &rect);
585
586 rc = ui_fixed_add(fixed, ui_menu_bar_ctl(edit->menubar));
587 if (rc != EOK) {
588 printf("Error adding control to layout.\n");
589 return rc;
590 }
591
592 rc = pane_init(edit->window, &pane);
593 if (rc != EOK) {
594 printf("Error initializing pane.\n");
595 return rc;
596 }
597
598 rc = ui_fixed_add(fixed, pane_ctl(&pane));
599 if (rc != EOK) {
600 printf("Error adding control to layout.\n");
601 return rc;
602 }
603
604 rc = ui_label_create(edit->ui_res, "", &edit->status);
605 if (rc != EOK) {
606 printf("Error creating menu bar.\n");
607 return rc;
608 }
609
610 rect.p0.x = arect.p0.x;
611 rect.p0.y = arect.p1.y - 1;
612 rect.p1 = arect.p1;
613 ui_label_set_rect(edit->status, &rect);
614
615 rc = ui_fixed_add(fixed, ui_label_ctl(edit->status));
616 if (rc != EOK) {
617 printf("Error adding control to layout.\n");
618 return rc;
619 }
620
621 ui_window_add(edit->window, ui_fixed_ctl(fixed));
622 return EOK;
623error:
624 if (edit->window != NULL)
625 ui_window_destroy(edit->window);
626 if (edit->ui != NULL)
627 ui_destroy(edit->ui);
628 return rc;
629}
630
631/** Destroy text editor UI.
632 *
633 * @param edit Editor
634 */
635static void edit_ui_destroy(edit_t *edit)
636{
637 ui_window_destroy(edit->window);
638 ui_destroy(edit->ui);
639}
640
641/* Handle key press. */
642static void key_handle_press(kbd_event_t *ev)
643{
644 if (((ev->mods & KM_ALT) == 0) &&
645 ((ev->mods & KM_SHIFT) == 0) &&
646 (ev->mods & KM_CTRL) != 0) {
647 key_handle_ctrl(ev);
648 } else if (((ev->mods & KM_ALT) == 0) &&
649 ((ev->mods & KM_CTRL) == 0) &&
650 (ev->mods & KM_SHIFT) != 0) {
651 key_handle_shift(ev);
652 } else if (((ev->mods & KM_ALT) == 0) &&
653 ((ev->mods & KM_CTRL) != 0) &&
654 (ev->mods & KM_SHIFT) != 0) {
655 key_handle_shift_ctrl(ev);
656 } else if ((ev->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
657 key_handle_unmod(ev);
658 }
659}
660
661static void cursor_setvis(bool visible)
662{
663 gfx_context_t *gc = ui_window_get_gc(edit.window);
664
665 (void) gfx_cursor_set_visible(gc, visible);
666}
667
668/** Handle key without modifier. */
669static void key_handle_unmod(kbd_event_t const *ev)
670{
671 switch (ev->key) {
672 case KC_ENTER:
673 selection_delete();
674 insert_char('\n');
675 caret_update();
676 break;
677 case KC_LEFT:
678 case KC_RIGHT:
679 case KC_UP:
680 case KC_DOWN:
681 case KC_HOME:
682 case KC_END:
683 case KC_PAGE_UP:
684 case KC_PAGE_DOWN:
685 key_handle_movement(ev->key, false);
686 break;
687 case KC_BACKSPACE:
688 if (selection_active())
689 selection_delete();
690 else
691 delete_char_before();
692 caret_update();
693 break;
694 case KC_DELETE:
695 if (selection_active())
696 selection_delete();
697 else
698 delete_char_after();
699 caret_update();
700 break;
701 default:
702 if (ev->c >= 32 || ev->c == '\t') {
703 selection_delete();
704 insert_char(ev->c);
705 caret_update();
706 }
707 break;
708 }
709}
710
711/** Handle Shift-key combination. */
712static void key_handle_shift(kbd_event_t const *ev)
713{
714 switch (ev->key) {
715 case KC_LEFT:
716 case KC_RIGHT:
717 case KC_UP:
718 case KC_DOWN:
719 case KC_HOME:
720 case KC_END:
721 case KC_PAGE_UP:
722 case KC_PAGE_DOWN:
723 key_handle_movement(ev->key, true);
724 break;
725 default:
726 if (ev->c >= 32 || ev->c == '\t') {
727 selection_delete();
728 insert_char(ev->c);
729 caret_update();
730 }
731 break;
732 }
733}
734
735/** Handle Ctrl-key combination. */
736static void key_handle_ctrl(kbd_event_t const *ev)
737{
738 spt_t pt;
739 switch (ev->key) {
740 case KC_Q:
741 ui_quit(edit.ui);
742 break;
743 case KC_S:
744 if (doc.file_name != NULL)
745 file_save(doc.file_name);
746 else
747 file_save_as();
748 break;
749 case KC_E:
750 file_save_as();
751 break;
752 case KC_C:
753 selection_copy();
754 break;
755 case KC_V:
756 edit_paste();
757 break;
758 case KC_X:
759 edit_cut();
760 break;
761 case KC_A:
762 selection_sel_all();
763 break;
764 case KC_RIGHT:
765 caret_move_word_right(false);
766 break;
767 case KC_LEFT:
768 caret_move_word_left(false);
769 break;
770 case KC_L:
771 caret_go_to_line_ask();
772 break;
773 case KC_F:
774 search_prompt(false);
775 break;
776 case KC_N:
777 search_repeat();
778 break;
779 case KC_HOME:
780 pt_get_sof(&pt);
781 caret_move(pt, false, true);
782 break;
783 case KC_END:
784 pt_get_eof(&pt);
785 caret_move(pt, false, true);
786 break;
787 default:
788 break;
789 }
790}
791
792static void key_handle_shift_ctrl(kbd_event_t const *ev)
793{
794 spt_t pt;
795 switch (ev->key) {
796 case KC_LEFT:
797 caret_move_word_left(true);
798 break;
799 case KC_RIGHT:
800 caret_move_word_right(true);
801 break;
802 case KC_F:
803 search_prompt(true);
804 break;
805 case KC_HOME:
806 pt_get_sof(&pt);
807 caret_move(pt, true, true);
808 break;
809 case KC_END:
810 pt_get_eof(&pt);
811 caret_move(pt, true, true);
812 break;
813 default:
814 break;
815 }
816}
817
818static void pos_handle(pos_event_t *ev)
819{
820 coord_t bc;
821 spt_t pt;
822 bool select;
823
824 if (ev->type == POS_PRESS && ev->vpos < (unsigned)pane.rows) {
825 bc.row = pane.sh_row + ev->vpos - pane.rect.p0.y;
826 bc.column = pane.sh_column + ev->hpos - pane.rect.p0.x;
827 sheet_get_cell_pt(doc.sh, &bc, dir_before, &pt);
828
829 select = (pane.keymod & KM_SHIFT) != 0;
830
831 caret_move(pt, select, true);
832 pane_update(&pane);
833 }
834}
835
836/** Move caret while preserving or resetting selection. */
837static void caret_move(spt_t new_caret_pt, bool select, bool update_ideal_column)
838{
839 spt_t old_caret_pt, old_sel_pt;
840 coord_t c_old, c_new;
841 bool had_sel;
842
843 /* Check if we had selection before. */
844 tag_get_pt(&pane.caret_pos, &old_caret_pt);
845 tag_get_pt(&pane.sel_start, &old_sel_pt);
846 had_sel = !spt_equal(&old_caret_pt, &old_sel_pt);
847
848 /* Place tag of the caret */
849 sheet_remove_tag(doc.sh, &pane.caret_pos);
850 sheet_place_tag(doc.sh, &new_caret_pt, &pane.caret_pos);
851
852 if (select == false) {
853 /* Move sel_start to the same point as caret. */
854 sheet_remove_tag(doc.sh, &pane.sel_start);
855 sheet_place_tag(doc.sh, &new_caret_pt, &pane.sel_start);
856 }
857
858 spt_get_coord(&new_caret_pt, &c_new);
859 if (select) {
860 spt_get_coord(&old_caret_pt, &c_old);
861
862 if (c_old.row == c_new.row)
863 pane.rflags |= REDRAW_ROW;
864 else
865 pane.rflags |= REDRAW_TEXT;
866
867 } else if (had_sel == true) {
868 /* Redraw because text was unselected. */
869 pane.rflags |= REDRAW_TEXT;
870 }
871
872 if (update_ideal_column)
873 pane.ideal_column = c_new.column;
874
875 caret_update();
876}
877
878static void key_handle_movement(unsigned int key, bool select)
879{
880 spt_t pt;
881 switch (key) {
882 case KC_LEFT:
883 caret_move_relative(0, -1, dir_before, select);
884 break;
885 case KC_RIGHT:
886 caret_move_relative(0, 0, dir_after, select);
887 break;
888 case KC_UP:
889 caret_move_relative(-1, 0, dir_before, select);
890 break;
891 case KC_DOWN:
892 caret_move_relative(+1, 0, dir_before, select);
893 break;
894 case KC_HOME:
895 tag_get_pt(&pane.caret_pos, &pt);
896 pt_get_sol(&pt, &pt);
897 caret_move(pt, select, true);
898 break;
899 case KC_END:
900 tag_get_pt(&pane.caret_pos, &pt);
901 pt_get_eol(&pt, &pt);
902 caret_move(pt, select, true);
903 break;
904 case KC_PAGE_UP:
905 caret_move_relative(-pane.rows, 0, dir_before, select);
906 break;
907 case KC_PAGE_DOWN:
908 caret_move_relative(+pane.rows, 0, dir_before, select);
909 break;
910 default:
911 break;
912 }
913}
914
915/** Save the document. */
916static errno_t file_save(char const *fname)
917{
918 spt_t sp, ep;
919 errno_t rc;
920
921 status_display("Saving...");
922 pt_get_sof(&sp);
923 pt_get_eof(&ep);
924
925 rc = file_save_range(fname, &sp, &ep);
926
927 switch (rc) {
928 case EINVAL:
929 status_display("Error opening file!");
930 break;
931 case EIO:
932 status_display("Error writing data!");
933 break;
934 default:
935 status_display("File saved.");
936 break;
937 }
938
939 return rc;
940}
941
942/** Open Save As dialog. */
943static void file_save_as(void)
944{
945 const char *old_fname = (doc.file_name != NULL) ? doc.file_name : "";
946 ui_file_dialog_params_t fdparams;
947 ui_file_dialog_t *dialog;
948 errno_t rc;
949
950 ui_file_dialog_params_init(&fdparams);
951 fdparams.caption = "Save As";
952 fdparams.ifname = old_fname;
953
954 rc = ui_file_dialog_create(edit.ui, &fdparams, &dialog);
955 if (rc != EOK) {
956 printf("Error creating message dialog.\n");
957 return;
958 }
959
960 ui_file_dialog_set_cb(dialog, &save_as_dialog_cb, &edit);
961}
962
963/** Insert file at caret position.
964 *
965 * Reads in the contents of a file and inserts them at the current position
966 * of the caret.
967 */
968static errno_t file_insert(char *fname)
969{
970 FILE *f;
971 char32_t c;
972 char buf[BUF_SIZE];
973 int bcnt;
974 int n_read;
975 size_t off;
976
977 f = fopen(fname, "rt");
978 if (f == NULL)
979 return EINVAL;
980
981 bcnt = 0;
982
983 while (true) {
984 if (bcnt < STR_BOUNDS(1)) {
985 n_read = fread(buf + bcnt, 1, BUF_SIZE - bcnt, f);
986 bcnt += n_read;
987 }
988
989 off = 0;
990 c = str_decode(buf, &off, bcnt);
991 if (c == '\0')
992 break;
993
994 bcnt -= off;
995 memmove(buf, buf + off, bcnt);
996
997 insert_char(c);
998 }
999
1000 fclose(f);
1001
1002 return EOK;
1003}
1004
1005/** Save a range of text into a file. */
1006static errno_t file_save_range(char const *fname, spt_t const *spos,
1007 spt_t const *epos)
1008{
1009 FILE *f;
1010 char buf[BUF_SIZE];
1011 spt_t sp, bep;
1012 size_t bytes, n_written;
1013
1014 f = fopen(fname, "wt");
1015 if (f == NULL)
1016 return EINVAL;
1017
1018 sp = *spos;
1019
1020 do {
1021 sheet_copy_out(doc.sh, &sp, epos, buf, BUF_SIZE, &bep);
1022 bytes = str_size(buf);
1023
1024 n_written = fwrite(buf, 1, bytes, f);
1025 if (n_written != bytes) {
1026 return EIO;
1027 }
1028
1029 sp = bep;
1030 } while (!spt_equal(&bep, epos));
1031
1032 if (fclose(f) < 0)
1033 return EIO;
1034
1035 return EOK;
1036}
1037
1038/** Return contents of range as a new string. */
1039static char *range_get_str(spt_t const *spos, spt_t const *epos)
1040{
1041 char *buf;
1042 spt_t sp, bep;
1043 size_t bytes;
1044 size_t buf_size, bpos;
1045
1046 buf_size = 1;
1047
1048 buf = malloc(buf_size);
1049 if (buf == NULL)
1050 return NULL;
1051
1052 bpos = 0;
1053 sp = *spos;
1054
1055 while (true) {
1056 sheet_copy_out(doc.sh, &sp, epos, &buf[bpos], buf_size - bpos,
1057 &bep);
1058 bytes = str_size(&buf[bpos]);
1059 bpos += bytes;
1060 sp = bep;
1061
1062 if (spt_equal(&bep, epos))
1063 break;
1064
1065 buf_size *= 2;
1066 char *tmp = realloc(buf, buf_size);
1067 if (tmp == NULL) {
1068 free(buf);
1069 return NULL;
1070 }
1071 buf = tmp;
1072 }
1073
1074 return buf;
1075}
1076
1077/** Initialize pane.
1078 *
1079 * TODO: Replace with pane_create() that allocates the pane.
1080 *
1081 * @param window Editor window
1082 * @param pane Pane
1083 * @return EOK on success or an error code
1084 */
1085static errno_t pane_init(ui_window_t *window, pane_t *pane)
1086{
1087 errno_t rc;
1088 gfx_rect_t arect;
1089
1090 pane->control = NULL;
1091 pane->color = NULL;
1092 pane->sel_color = NULL;
1093
1094 rc = ui_control_new(&pane_ctl_ops, (void *) pane, &pane->control);
1095 if (rc != EOK)
1096 goto error;
1097
1098 rc = gfx_color_new_ega(0x07, &pane->color);
1099 if (rc != EOK)
1100 goto error;
1101
1102 rc = gfx_color_new_ega(0x1e, &pane->sel_color);
1103 if (rc != EOK)
1104 goto error;
1105
1106 pane->res = ui_window_get_res(window);
1107 pane->window = window;
1108
1109 ui_window_get_app_rect(window, &arect);
1110 pane->rect.p0.x = arect.p0.x;
1111 pane->rect.p0.y = arect.p0.y + 1;
1112 pane->rect.p1.x = arect.p1.x;
1113 pane->rect.p1.y = arect.p1.y - 1;
1114
1115 pane->columns = pane->rect.p1.x - pane->rect.p0.x;
1116 pane->rows = pane->rect.p1.y - pane->rect.p0.y;
1117
1118 return EOK;
1119error:
1120 if (pane->control != NULL) {
1121 ui_control_delete(pane->control);
1122 pane->control = NULL;
1123 }
1124
1125 if (pane->color != NULL) {
1126 gfx_color_delete(pane->color);
1127 pane->color = NULL;
1128 }
1129
1130 return rc;
1131}
1132
1133/** Finalize pane.
1134 *
1135 * TODO: Replace with pane_destroy() that deallocates the pane.
1136 *
1137 * @param pane Pane
1138 */
1139static void pane_fini(pane_t *pane)
1140{
1141 gfx_color_delete(pane->color);
1142 pane->color = NULL;
1143 gfx_color_delete(pane->sel_color);
1144 pane->sel_color = NULL;
1145 ui_control_delete(pane->control);
1146 pane->control = NULL;
1147}
1148
1149/** Return base control object for a pane.
1150 *
1151 * @param pane Pane
1152 * @return Base UI cntrol
1153 */
1154static ui_control_t *pane_ctl(pane_t *pane)
1155{
1156 return pane->control;
1157}
1158
1159/** Repaint parts of pane that need updating.
1160 *
1161 * @param pane Pane
1162 * @return EOK on succes or an error code
1163 */
1164static errno_t pane_update(pane_t *pane)
1165{
1166 errno_t rc;
1167
1168 if (pane->rflags & REDRAW_TEXT) {
1169 rc = pane_text_display(pane);
1170 if (rc != EOK)
1171 return rc;
1172 }
1173
1174 if (pane->rflags & REDRAW_ROW)
1175 pane_row_display();
1176
1177 if (pane->rflags & REDRAW_STATUS)
1178 pane_status_display(pane);
1179
1180 if (pane->rflags & REDRAW_CARET)
1181 pane_caret_display(pane);
1182
1183 pane->rflags &= ~(REDRAW_TEXT | REDRAW_ROW | REDRAW_STATUS |
1184 REDRAW_CARET);
1185 return EOK;
1186}
1187
1188/** Display pane text.
1189 *
1190 * @param pane Pane
1191 * @return EOK on success or an error code
1192 */
1193static errno_t pane_text_display(pane_t *pane)
1194{
1195 gfx_rect_t rect;
1196 gfx_context_t *gc;
1197 errno_t rc;
1198 int sh_rows, rows;
1199
1200 sheet_get_num_rows(doc.sh, &sh_rows);
1201 rows = min(sh_rows - pane->sh_row + 1, pane->rows);
1202
1203 /* Draw rows from the sheet. */
1204
1205 rc = pane_row_range_display(pane, 0, rows);
1206 if (rc != EOK)
1207 return rc;
1208
1209 /* Clear the remaining rows if file is short. */
1210
1211 gc = ui_window_get_gc(pane->window);
1212
1213 rc = gfx_set_color(gc, pane->color);
1214 if (rc != EOK)
1215 goto error;
1216
1217 rect.p0.x = pane->rect.p0.x;
1218 rect.p0.y = pane->rect.p0.y + rows;
1219 rect.p1.x = pane->rect.p1.x;
1220 rect.p1.y = pane->rect.p1.y;
1221
1222 rc = gfx_fill_rect(gc, &rect);
1223 if (rc != EOK)
1224 goto error;
1225
1226 pane->rflags &= ~REDRAW_ROW;
1227 return EOK;
1228error:
1229 return rc;
1230}
1231
1232/** Display just the row where the caret is. */
1233static void pane_row_display(void)
1234{
1235 spt_t caret_pt;
1236 coord_t coord;
1237 int ridx;
1238
1239 tag_get_pt(&pane.caret_pos, &caret_pt);
1240 spt_get_coord(&caret_pt, &coord);
1241
1242 ridx = coord.row - pane.sh_row;
1243 (void) pane_row_range_display(&pane, ridx, ridx + 1);
1244 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
1245}
1246
1247/** Display a range of rows of text.
1248 *
1249 * @param r0 Start row (inclusive)
1250 * @param r1 End row (exclusive)
1251 * @return EOk on success or an error code
1252 */
1253static errno_t pane_row_range_display(pane_t *pane, int r0, int r1)
1254{
1255 int i, fill;
1256 spt_t rb, re, dep, pt;
1257 coord_t rbc, rec;
1258 char row_buf[ROW_BUF_SIZE];
1259 char cbuf[STR_BOUNDS(1) + 1];
1260 char32_t c;
1261 size_t pos, size;
1262 size_t cpos;
1263 int s_column;
1264 coord_t csel_start, csel_end, ctmp;
1265 gfx_font_t *font;
1266 gfx_context_t *gc;
1267 gfx_text_fmt_t fmt;
1268 gfx_coord2_t tpos;
1269 gfx_rect_t rect;
1270 errno_t rc;
1271
1272 font = ui_resource_get_font(edit.ui_res);
1273 gc = ui_window_get_gc(edit.window);
1274
1275 gfx_text_fmt_init(&fmt);
1276 fmt.font = font;
1277 fmt.color = pane->color;
1278
1279 /* Determine selection start and end. */
1280
1281 tag_get_pt(&pane->sel_start, &pt);
1282 spt_get_coord(&pt, &csel_start);
1283
1284 tag_get_pt(&pane->caret_pos, &pt);
1285 spt_get_coord(&pt, &csel_end);
1286
1287 if (coord_cmp(&csel_start, &csel_end) > 0) {
1288 ctmp = csel_start;
1289 csel_start = csel_end;
1290 csel_end = ctmp;
1291 }
1292
1293 /* Draw rows from the sheet. */
1294
1295 for (i = r0; i < r1; ++i) {
1296 tpos.x = pane->rect.p0.x;
1297 tpos.y = pane->rect.p0.y + i;
1298
1299 /* Starting point for row display */
1300 rbc.row = pane->sh_row + i;
1301 rbc.column = pane->sh_column;
1302 sheet_get_cell_pt(doc.sh, &rbc, dir_before, &rb);
1303
1304 /* Ending point for row display */
1305 rec.row = pane->sh_row + i;
1306 rec.column = pane->sh_column + pane->columns;
1307 sheet_get_cell_pt(doc.sh, &rec, dir_before, &re);
1308
1309 /* Copy the text of the row to the buffer. */
1310 sheet_copy_out(doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
1311
1312 /* Display text from the buffer. */
1313
1314 if (coord_cmp(&csel_start, &rbc) <= 0 &&
1315 coord_cmp(&rbc, &csel_end) < 0) {
1316 fmt.color = pane->sel_color;
1317 }
1318
1319 size = str_size(row_buf);
1320 pos = 0;
1321 s_column = pane->sh_column;
1322 while (pos < size) {
1323 if ((csel_start.row == rbc.row) && (csel_start.column == s_column))
1324 fmt.color = pane->sel_color;
1325
1326 if ((csel_end.row == rbc.row) && (csel_end.column == s_column))
1327 fmt.color = pane->color;
1328
1329 c = str_decode(row_buf, &pos, size);
1330 if (c != '\t') {
1331 cpos = 0;
1332 rc = chr_encode(c, cbuf, &cpos, sizeof(cbuf));
1333 if (rc != EOK)
1334 return rc;
1335
1336 rc = gfx_puttext(&tpos, &fmt, cbuf);
1337 if (rc != EOK)
1338 return rc;
1339
1340 s_column += 1;
1341 tpos.x++;
1342 } else {
1343 fill = 1 + ALIGN_UP(s_column, TAB_WIDTH) -
1344 s_column;
1345
1346 rc = gfx_set_color(gc, fmt.color);
1347 if (rc != EOK)
1348 return rc;
1349
1350 rect.p0.x = tpos.x;
1351 rect.p0.y = tpos.y;
1352 rect.p1.x = tpos.x + fill;
1353 rect.p1.y = tpos.y + 1;
1354
1355 rc = gfx_fill_rect(gc, &rect);
1356 if (rc != EOK)
1357 return rc;
1358
1359 s_column += fill;
1360 tpos.x += fill;
1361 }
1362 }
1363
1364 if ((csel_end.row == rbc.row) && (csel_end.column == s_column))
1365 fmt.color = pane->color;
1366
1367 /* Fill until the end of display area. */
1368
1369 rc = gfx_set_color(gc, fmt.color);
1370 if (rc != EOK)
1371 return rc;
1372
1373 rect.p0.x = tpos.x;
1374 rect.p0.y = tpos.y;
1375 rect.p1.x = pane->rect.p1.x;
1376 rect.p1.y = tpos.y + 1;
1377
1378 rc = gfx_fill_rect(gc, &rect);
1379 if (rc != EOK)
1380 return rc;
1381 }
1382
1383 return EOK;
1384}
1385
1386/** Display pane status in the status line.
1387 *
1388 * @param pane Pane
1389 */
1390static void pane_status_display(pane_t *pane)
1391{
1392 spt_t caret_pt;
1393 coord_t coord;
1394 int last_row;
1395 char *fname;
1396 char *p;
1397 char *text;
1398 size_t n;
1399 size_t nextra;
1400 size_t fnw;
1401
1402 tag_get_pt(&pane->caret_pos, &caret_pt);
1403 spt_get_coord(&caret_pt, &coord);
1404
1405 sheet_get_num_rows(doc.sh, &last_row);
1406
1407 if (doc.file_name != NULL) {
1408 /* Remove directory component */
1409 p = str_rchr(doc.file_name, '/');
1410 if (p != NULL)
1411 fname = str_dup(p + 1);
1412 else
1413 fname = str_dup(doc.file_name);
1414 } else {
1415 fname = str_dup("<unnamed>");
1416 }
1417
1418 if (fname == NULL)
1419 return;
1420
1421 /*
1422 * Make sure the status fits on the screen. This loop should
1423 * be executed at most twice.
1424 */
1425 while (true) {
1426 int rc = asprintf(&text, "%d, %d (%d): File '%s'. Ctrl-Q Quit "
1427 "F10 Menu", coord.row, coord.column, last_row, fname);
1428 if (rc < 0) {
1429 n = 0;
1430 goto finish;
1431 }
1432
1433 /* If it already fits, we're done */
1434 n = str_width(text);
1435 if ((int)n <= pane->columns - 2)
1436 break;
1437
1438 /* Compute number of excess characters */
1439 nextra = n - (pane->columns - 2);
1440 /** With of the file name part */
1441 fnw = str_width(fname);
1442
1443 /*
1444 * If reducing file name to two characters '..' won't help,
1445 * just give up and print a blank status.
1446 */
1447 if (nextra > fnw - 2) {
1448 text[0] = '\0';
1449 goto finish;
1450 }
1451
1452 /* Compute position where we overwrite with '..\0' */
1453 if (fnw >= nextra + 2) {
1454 p = fname + str_lsize(fname, fnw - nextra - 2);
1455 } else {
1456 p = fname;
1457 }
1458
1459 /* Shorten the string */
1460 p[0] = p[1] = '.';
1461 p[2] = '\0';
1462
1463 /* Need to format the string once more. */
1464 free(text);
1465 }
1466
1467finish:
1468 (void) ui_label_set_text(edit.status, text);
1469 (void) ui_label_paint(edit.status);
1470 free(text);
1471 free(fname);
1472}
1473
1474/** Set cursor to reflect position of the caret.
1475 *
1476 * @param pane Pane
1477 */
1478static void pane_caret_display(pane_t *pane)
1479{
1480 spt_t caret_pt;
1481 coord_t coord;
1482 gfx_coord2_t pos;
1483 gfx_context_t *gc;
1484
1485 tag_get_pt(&pane->caret_pos, &caret_pt);
1486
1487 spt_get_coord(&caret_pt, &coord);
1488
1489 gc = ui_window_get_gc(edit.window);
1490 pos.x = pane->rect.p0.x + coord.column - pane->sh_column;
1491 pos.y = pane->rect.p0.y + coord.row - pane->sh_row;
1492
1493 (void) gfx_cursor_set_pos(gc, &pos);
1494}
1495
1496/** Destroy pane control.
1497 *
1498 * @param arg Argument (pane_t *)
1499 */
1500static void pane_ctl_destroy(void *arg)
1501{
1502 pane_t *pane = (pane_t *)arg;
1503
1504 pane_fini(pane);
1505}
1506
1507/** Paint pane control.
1508 *
1509 * @param arg Argument (pane_t *)
1510 */
1511static errno_t pane_ctl_paint(void *arg)
1512{
1513 pane_t *pane = (pane_t *)arg;
1514 gfx_context_t *gc;
1515 errno_t rc;
1516
1517 gc = ui_window_get_gc(pane->window);
1518
1519 rc = pane_text_display(pane);
1520 if (rc != EOK)
1521 goto error;
1522
1523 rc = gfx_update(gc);
1524 if (rc != EOK)
1525 goto error;
1526
1527error:
1528 return rc;
1529}
1530
1531/** Handle pane control position event.
1532 *
1533 * @param arg Argument (pane_t *)
1534 * @param event Position event
1535 */
1536static ui_evclaim_t pane_ctl_pos_event(void *arg, pos_event_t *event)
1537{
1538 gfx_coord2_t pos;
1539
1540 pos.x = event->hpos;
1541 pos.y = event->vpos;
1542
1543 if (!gfx_pix_inside_rect(&pos, &pane.rect))
1544 return ui_unclaimed;
1545
1546 pos_handle(event);
1547 (void) gfx_update(ui_window_get_gc(edit.window));
1548 return ui_claimed;
1549}
1550
1551/** Insert a character at caret position. */
1552static void insert_char(char32_t c)
1553{
1554 spt_t pt;
1555 char cbuf[STR_BOUNDS(1) + 1];
1556 size_t offs;
1557
1558 tag_get_pt(&pane.caret_pos, &pt);
1559
1560 offs = 0;
1561 chr_encode(c, cbuf, &offs, STR_BOUNDS(1) + 1);
1562 cbuf[offs] = '\0';
1563
1564 (void) sheet_insert(doc.sh, &pt, dir_before, cbuf);
1565
1566 pane.rflags |= REDRAW_ROW;
1567 if (c == '\n')
1568 pane.rflags |= REDRAW_TEXT;
1569}
1570
1571/** Delete the character before the caret. */
1572static void delete_char_before(void)
1573{
1574 spt_t sp, ep;
1575 coord_t coord;
1576
1577 tag_get_pt(&pane.caret_pos, &ep);
1578 spt_get_coord(&ep, &coord);
1579
1580 coord.column -= 1;
1581 sheet_get_cell_pt(doc.sh, &coord, dir_before, &sp);
1582
1583 (void) sheet_delete(doc.sh, &sp, &ep);
1584
1585 pane.rflags |= REDRAW_ROW;
1586 if (coord.column < 1)
1587 pane.rflags |= REDRAW_TEXT;
1588}
1589
1590/** Delete the character after the caret. */
1591static void delete_char_after(void)
1592{
1593 spt_t sp, ep;
1594 coord_t sc, ec;
1595
1596 tag_get_pt(&pane.caret_pos, &sp);
1597 spt_get_coord(&sp, &sc);
1598
1599 sheet_get_cell_pt(doc.sh, &sc, dir_after, &ep);
1600 spt_get_coord(&ep, &ec);
1601
1602 (void) sheet_delete(doc.sh, &sp, &ep);
1603
1604 pane.rflags |= REDRAW_ROW;
1605 if (ec.row != sc.row)
1606 pane.rflags |= REDRAW_TEXT;
1607}
1608
1609/** Scroll pane after caret has moved.
1610 *
1611 * After modifying the position of the caret, this is called to scroll
1612 * the pane to ensure that the caret is in the visible area.
1613 */
1614static void caret_update(void)
1615{
1616 spt_t pt;
1617 coord_t coord;
1618
1619 tag_get_pt(&pane.caret_pos, &pt);
1620 spt_get_coord(&pt, &coord);
1621
1622 /* Scroll pane vertically. */
1623
1624 if (coord.row < pane.sh_row) {
1625 pane.sh_row = coord.row;
1626 pane.rflags |= REDRAW_TEXT;
1627 }
1628
1629 if (coord.row > pane.sh_row + pane.rows - 1) {
1630 pane.sh_row = coord.row - pane.rows + 1;
1631 pane.rflags |= REDRAW_TEXT;
1632 }
1633
1634 /* Scroll pane horizontally. */
1635
1636 if (coord.column < pane.sh_column) {
1637 pane.sh_column = coord.column;
1638 pane.rflags |= REDRAW_TEXT;
1639 }
1640
1641 if (coord.column > pane.sh_column + pane.columns - 1) {
1642 pane.sh_column = coord.column - pane.columns + 1;
1643 pane.rflags |= REDRAW_TEXT;
1644 }
1645
1646 pane.rflags |= (REDRAW_CARET | REDRAW_STATUS);
1647}
1648
1649/** Relatively move caret position.
1650 *
1651 * Moves caret relatively to the current position. Looking at the first
1652 * character cell after the caret and moving by @a drow and @a dcolumn, we get
1653 * to a new character cell, and thus a new character. Then we either go to the
1654 * point before the the character or after it, depending on @a align_dir.
1655 *
1656 * @param select true if the selection tag should stay where it is
1657 */
1658static void caret_move_relative(int drow, int dcolumn, enum dir_spec align_dir,
1659 bool select)
1660{
1661 spt_t pt;
1662 coord_t coord;
1663 int num_rows;
1664 bool pure_vertical;
1665
1666 tag_get_pt(&pane.caret_pos, &pt);
1667 spt_get_coord(&pt, &coord);
1668 coord.row += drow;
1669 coord.column += dcolumn;
1670
1671 /* Clamp coordinates. */
1672 if (drow < 0 && coord.row < 1)
1673 coord.row = 1;
1674 if (dcolumn < 0 && coord.column < 1) {
1675 if (coord.row < 2)
1676 coord.column = 1;
1677 else {
1678 coord.row--;
1679 sheet_get_row_width(doc.sh, coord.row, &coord.column);
1680 }
1681 }
1682 if (drow > 0) {
1683 sheet_get_num_rows(doc.sh, &num_rows);
1684 if (coord.row > num_rows)
1685 coord.row = num_rows;
1686 }
1687
1688 /* For purely vertical movement try attaining @c ideal_column. */
1689 pure_vertical = (dcolumn == 0 && align_dir == dir_before);
1690 if (pure_vertical)
1691 coord.column = pane.ideal_column;
1692
1693 /*
1694 * Select the point before or after the character at the designated
1695 * coordinates. The character can be wider than one cell (e.g. tab).
1696 */
1697 sheet_get_cell_pt(doc.sh, &coord, align_dir, &pt);
1698
1699 /* For non-vertical movement set the new value for @c ideal_column. */
1700 caret_move(pt, select, !pure_vertical);
1701}
1702
1703/** Absolutely move caret position.
1704 *
1705 * Moves caret to a specified position. We get to a new character cell, and
1706 * thus a new character. Then we either go to the point before the the character
1707 * or after it, depending on @a align_dir.
1708 *
1709 * @param select true if the selection tag should stay where it is
1710 */
1711static void caret_move_absolute(int row, int column, enum dir_spec align_dir,
1712 bool select)
1713{
1714 coord_t coord;
1715 coord.row = row;
1716 coord.column = column;
1717
1718 spt_t pt;
1719 sheet_get_cell_pt(doc.sh, &coord, align_dir, &pt);
1720
1721 caret_move(pt, select, true);
1722}
1723
1724/** Find beginning of a word to the left of spt */
1725static spt_t pt_find_word_left(spt_t spt)
1726{
1727 do {
1728 spt_prev_char(spt, &spt);
1729 } while (!pt_is_word_beginning(&spt));
1730 return spt;
1731}
1732
1733/** Find beginning of a word to the right of spt */
1734static spt_t pt_find_word_right(spt_t spt)
1735{
1736 do {
1737 spt_next_char(spt, &spt);
1738 } while (!pt_is_word_beginning(&spt));
1739 return spt;
1740}
1741
1742static void caret_move_word_left(bool select)
1743{
1744 spt_t pt;
1745 tag_get_pt(&pane.caret_pos, &pt);
1746 spt_t word_left = pt_find_word_left(pt);
1747 caret_move(word_left, select, true);
1748}
1749
1750static void caret_move_word_right(bool select)
1751{
1752 spt_t pt;
1753 tag_get_pt(&pane.caret_pos, &pt);
1754 spt_t word_right = pt_find_word_right(pt);
1755 caret_move(word_right, select, true);
1756}
1757
1758/** Ask for line and go to it. */
1759static void caret_go_to_line_ask(void)
1760{
1761 ui_prompt_dialog_params_t pdparams;
1762 ui_prompt_dialog_t *dialog;
1763 errno_t rc;
1764
1765 ui_prompt_dialog_params_init(&pdparams);
1766 pdparams.caption = "Go To Line";
1767 pdparams.prompt = "Line Number";
1768
1769 rc = ui_prompt_dialog_create(edit.ui, &pdparams, &dialog);
1770 if (rc != EOK) {
1771 printf("Error creating prompt dialog.\n");
1772 return;
1773 }
1774
1775 ui_prompt_dialog_set_cb(dialog, &go_to_line_dialog_cb, &edit);
1776}
1777
1778/* Search operations */
1779static errno_t search_spt_producer(void *data, char32_t *ret)
1780{
1781 assert(data != NULL);
1782 assert(ret != NULL);
1783 spt_t *spt = data;
1784 *ret = spt_next_char(*spt, spt);
1785 return EOK;
1786}
1787
1788static errno_t search_spt_reverse_producer(void *data, char32_t *ret)
1789{
1790 assert(data != NULL);
1791 assert(ret != NULL);
1792 spt_t *spt = data;
1793 *ret = spt_prev_char(*spt, spt);
1794 return EOK;
1795}
1796
1797static errno_t search_spt_mark(void *data, void **mark)
1798{
1799 assert(data != NULL);
1800 assert(mark != NULL);
1801 spt_t *spt = data;
1802 spt_t *new = calloc(1, sizeof(spt_t));
1803 *mark = new;
1804 if (new == NULL)
1805 return ENOMEM;
1806 *new = *spt;
1807 return EOK;
1808}
1809
1810static void search_spt_mark_free(void *data)
1811{
1812 free(data);
1813}
1814
1815static search_ops_t search_spt_ops = {
1816 .equals = char_exact_equals,
1817 .producer = search_spt_producer,
1818 .mark = search_spt_mark,
1819 .mark_free = search_spt_mark_free,
1820};
1821
1822static search_ops_t search_spt_reverse_ops = {
1823 .equals = char_exact_equals,
1824 .producer = search_spt_reverse_producer,
1825 .mark = search_spt_mark,
1826 .mark_free = search_spt_mark_free,
1827};
1828
1829/** Ask for line and go to it. */
1830static void search_prompt(bool reverse)
1831{
1832 ui_prompt_dialog_params_t pdparams;
1833 ui_prompt_dialog_t *dialog;
1834 errno_t rc;
1835
1836 ui_prompt_dialog_params_init(&pdparams);
1837 pdparams.caption = reverse ? "Reverse Search" : "Search";
1838 pdparams.prompt = "Search text";
1839 pdparams.itext = "";
1840
1841 if (pane.previous_search)
1842 pdparams.itext = pane.previous_search;
1843
1844 rc = ui_prompt_dialog_create(edit.ui, &pdparams, &dialog);
1845 if (rc != EOK) {
1846 printf("Error creating prompt dialog.\n");
1847 return;
1848 }
1849
1850 ui_prompt_dialog_set_cb(dialog, &search_dialog_cb, &edit);
1851 pane.search_reverse = reverse;
1852}
1853
1854static void search_repeat(void)
1855{
1856 if (pane.previous_search == NULL) {
1857 status_display("No previous search to repeat.");
1858 return;
1859 }
1860
1861 search(pane.previous_search, pane.previous_search_reverse);
1862}
1863
1864static void search(char *pattern, bool reverse)
1865{
1866 status_display("Searching...");
1867
1868 spt_t sp, producer_pos;
1869 tag_get_pt(&pane.caret_pos, &sp);
1870
1871 /* Start searching on the position before/after caret */
1872 if (!reverse) {
1873 spt_next_char(sp, &sp);
1874 } else {
1875 spt_prev_char(sp, &sp);
1876 }
1877 producer_pos = sp;
1878
1879 search_ops_t ops = search_spt_ops;
1880 if (reverse)
1881 ops = search_spt_reverse_ops;
1882
1883 search_t *search = search_init(pattern, &producer_pos, ops, reverse);
1884 if (search == NULL) {
1885 status_display("Failed initializing search.");
1886 return;
1887 }
1888
1889 match_t match;
1890 errno_t rc = search_next_match(search, &match);
1891 if (rc != EOK) {
1892 status_display("Failed searching.");
1893 search_fini(search);
1894 }
1895
1896 if (match.end) {
1897 status_display("Match found.");
1898 assert(match.end != NULL);
1899 spt_t *end = match.end;
1900 caret_move(*end, false, true);
1901 while (match.length > 0) {
1902 match.length--;
1903 if (reverse) {
1904 spt_next_char(*end, end);
1905 } else {
1906 spt_prev_char(*end, end);
1907 }
1908 }
1909 caret_move(*end, true, true);
1910 free(end);
1911 } else {
1912 status_display("Not found.");
1913 }
1914
1915 search_fini(search);
1916}
1917
1918/** Check for non-empty selection. */
1919static bool selection_active(void)
1920{
1921 return (tag_cmp(&pane.caret_pos, &pane.sel_start) != 0);
1922}
1923
1924static void selection_get_points(spt_t *pa, spt_t *pb)
1925{
1926 spt_t pt;
1927
1928 tag_get_pt(&pane.sel_start, pa);
1929 tag_get_pt(&pane.caret_pos, pb);
1930
1931 if (spt_cmp(pa, pb) > 0) {
1932 pt = *pa;
1933 *pa = *pb;
1934 *pb = pt;
1935 }
1936}
1937
1938/** Delete selected text. */
1939static void selection_delete(void)
1940{
1941 spt_t pa, pb;
1942 coord_t ca, cb;
1943 int rel;
1944
1945 tag_get_pt(&pane.sel_start, &pa);
1946 tag_get_pt(&pane.caret_pos, &pb);
1947 spt_get_coord(&pa, &ca);
1948 spt_get_coord(&pb, &cb);
1949 rel = coord_cmp(&ca, &cb);
1950
1951 if (rel == 0)
1952 return;
1953
1954 if (rel < 0)
1955 sheet_delete(doc.sh, &pa, &pb);
1956 else
1957 sheet_delete(doc.sh, &pb, &pa);
1958
1959 if (ca.row == cb.row)
1960 pane.rflags |= REDRAW_ROW;
1961 else
1962 pane.rflags |= REDRAW_TEXT;
1963}
1964
1965/** Select all text in the editor */
1966static void selection_sel_all(void)
1967{
1968 spt_t spt, ept;
1969
1970 pt_get_sof(&spt);
1971 pt_get_eof(&ept);
1972
1973 selection_sel_range(spt, ept);
1974}
1975
1976/** Select select all text in a given range with the given direction */
1977static void selection_sel_range(spt_t pa, spt_t pb)
1978{
1979 sheet_remove_tag(doc.sh, &pane.sel_start);
1980 sheet_place_tag(doc.sh, &pa, &pane.sel_start);
1981 sheet_remove_tag(doc.sh, &pane.caret_pos);
1982 sheet_place_tag(doc.sh, &pb, &pane.caret_pos);
1983
1984 pane.rflags |= REDRAW_TEXT;
1985 caret_update();
1986}
1987
1988static void selection_copy(void)
1989{
1990 spt_t pa, pb;
1991 char *str;
1992
1993 selection_get_points(&pa, &pb);
1994 str = range_get_str(&pa, &pb);
1995 if (str == NULL || clipboard_put_str(str) != EOK) {
1996 status_display("Copying to clipboard failed!");
1997 }
1998 free(str);
1999}
2000
2001static void edit_paste(void)
2002{
2003 selection_delete();
2004 insert_clipboard_data();
2005 pane.rflags |= (REDRAW_TEXT | REDRAW_CARET);
2006 pane_update(&pane);
2007}
2008
2009static void edit_cut(void)
2010{
2011 selection_copy();
2012 selection_delete();
2013 pane.rflags |= (REDRAW_TEXT | REDRAW_CARET);
2014 pane_update(&pane);
2015}
2016
2017static void insert_clipboard_data(void)
2018{
2019 char *str;
2020 size_t off;
2021 char32_t c;
2022 errno_t rc;
2023
2024 rc = clipboard_get_str(&str);
2025 if (rc != EOK || str == NULL)
2026 return;
2027
2028 off = 0;
2029
2030 while (true) {
2031 c = str_decode(str, &off, STR_NO_LIMIT);
2032 if (c == '\0')
2033 break;
2034
2035 insert_char(c);
2036 }
2037
2038 free(str);
2039}
2040
2041/** Get start-of-file s-point. */
2042static void pt_get_sof(spt_t *pt)
2043{
2044 coord_t coord;
2045
2046 coord.row = coord.column = 1;
2047 sheet_get_cell_pt(doc.sh, &coord, dir_before, pt);
2048}
2049
2050/** Get end-of-file s-point. */
2051static void pt_get_eof(spt_t *pt)
2052{
2053 coord_t coord;
2054 int num_rows;
2055
2056 sheet_get_num_rows(doc.sh, &num_rows);
2057 coord.row = num_rows + 1;
2058 coord.column = 1;
2059
2060 sheet_get_cell_pt(doc.sh, &coord, dir_after, pt);
2061}
2062
2063/** Get start-of-line s-point for given s-point cpt */
2064static void pt_get_sol(spt_t *cpt, spt_t *spt)
2065{
2066 coord_t coord;
2067
2068 spt_get_coord(cpt, &coord);
2069 coord.column = 1;
2070
2071 sheet_get_cell_pt(doc.sh, &coord, dir_before, spt);
2072}
2073
2074/** Get end-of-line s-point for given s-point cpt */
2075static void pt_get_eol(spt_t *cpt, spt_t *ept)
2076{
2077 coord_t coord;
2078 int row_width;
2079
2080 spt_get_coord(cpt, &coord);
2081 sheet_get_row_width(doc.sh, coord.row, &row_width);
2082 coord.column = row_width - 1;
2083
2084 sheet_get_cell_pt(doc.sh, &coord, dir_after, ept);
2085}
2086
2087/** Check whether the spt is at a beginning of a word */
2088static bool pt_is_word_beginning(spt_t *pt)
2089{
2090 spt_t lp, sfp, efp, slp, elp;
2091 coord_t coord;
2092
2093 pt_get_sof(&sfp);
2094 pt_get_eof(&efp);
2095 pt_get_sol(pt, &slp);
2096 pt_get_eol(pt, &elp);
2097
2098 /* the spt is at the beginning or end of the file or line */
2099 if ((spt_cmp(&sfp, pt) == 0) || (spt_cmp(&efp, pt) == 0) ||
2100 (spt_cmp(&slp, pt) == 0) || (spt_cmp(&elp, pt) == 0))
2101 return true;
2102
2103 /* the spt is a delimiter */
2104 if (pt_is_delimiter(pt))
2105 return false;
2106
2107 spt_get_coord(pt, &coord);
2108
2109 coord.column -= 1;
2110 sheet_get_cell_pt(doc.sh, &coord, dir_before, &lp);
2111
2112 return pt_is_delimiter(&lp) ||
2113 (pt_is_punctuation(pt) && !pt_is_punctuation(&lp)) ||
2114 (pt_is_punctuation(&lp) && !pt_is_punctuation(pt));
2115}
2116
2117static char32_t get_first_wchar(const char *str)
2118{
2119 size_t offset = 0;
2120 return str_decode(str, &offset, str_size(str));
2121}
2122
2123static bool pt_is_delimiter(spt_t *pt)
2124{
2125 spt_t rp;
2126 coord_t coord;
2127 char *ch = NULL;
2128
2129 spt_get_coord(pt, &coord);
2130
2131 coord.column += 1;
2132 sheet_get_cell_pt(doc.sh, &coord, dir_after, &rp);
2133
2134 ch = range_get_str(pt, &rp);
2135 if (ch == NULL)
2136 return false;
2137
2138 char32_t first_char = get_first_wchar(ch);
2139 switch (first_char) {
2140 case ' ':
2141 case '\t':
2142 case '\n':
2143 return true;
2144 default:
2145 return false;
2146 }
2147}
2148
2149static bool pt_is_punctuation(spt_t *pt)
2150{
2151 spt_t rp;
2152 coord_t coord;
2153 char *ch = NULL;
2154
2155 spt_get_coord(pt, &coord);
2156
2157 coord.column += 1;
2158 sheet_get_cell_pt(doc.sh, &coord, dir_after, &rp);
2159
2160 ch = range_get_str(pt, &rp);
2161 if (ch == NULL)
2162 return false;
2163
2164 char32_t first_char = get_first_wchar(ch);
2165 switch (first_char) {
2166 case ',':
2167 case '.':
2168 case ';':
2169 case ':':
2170 case '/':
2171 case '?':
2172 case '\\':
2173 case '|':
2174 case '_':
2175 case '+':
2176 case '-':
2177 case '*':
2178 case '=':
2179 case '<':
2180 case '>':
2181 return true;
2182 default:
2183 return false;
2184 }
2185}
2186
2187/** Compare tags. */
2188static int tag_cmp(tag_t const *a, tag_t const *b)
2189{
2190 spt_t pa, pb;
2191
2192 tag_get_pt(a, &pa);
2193 tag_get_pt(b, &pb);
2194
2195 return spt_cmp(&pa, &pb);
2196}
2197
2198/** Compare s-points. */
2199static int spt_cmp(spt_t const *a, spt_t const *b)
2200{
2201 coord_t ca, cb;
2202
2203 spt_get_coord(a, &ca);
2204 spt_get_coord(b, &cb);
2205
2206 return coord_cmp(&ca, &cb);
2207}
2208
2209/** Compare coordinats. */
2210static int coord_cmp(coord_t const *a, coord_t const *b)
2211{
2212 if (a->row - b->row != 0)
2213 return a->row - b->row;
2214
2215 return a->column - b->column;
2216}
2217
2218/** Display text in the status line. */
2219static void status_display(char const *str)
2220{
2221 (void) ui_label_set_text(edit.status, str);
2222 (void) ui_label_paint(edit.status);
2223}
2224
2225/** Window close request
2226 *
2227 * @param window Window
2228 * @param arg Argument (edit_t *)
2229 */
2230static void edit_wnd_close(ui_window_t *window, void *arg)
2231{
2232 edit_t *edit = (edit_t *) arg;
2233
2234 ui_quit(edit->ui);
2235}
2236
2237/** Window focus event
2238 *
2239 * @param window Window
2240 * @param arg Argument (edit_t *)
2241 * @param focus Focus number
2242 */
2243static void edit_wnd_focus(ui_window_t *window, void *arg, unsigned focus)
2244{
2245 edit_t *edit = (edit_t *)arg;
2246
2247 (void)edit;
2248 pane_caret_display(&pane);
2249 cursor_setvis(true);
2250}
2251
2252/** Window keyboard event
2253 *
2254 * @param window Window
2255 * @param arg Argument (edit_t *)
2256 * @param event Keyboard event
2257 */
2258static void edit_wnd_kbd_event(ui_window_t *window, void *arg,
2259 kbd_event_t *event)
2260{
2261 pane.keymod = event->mods;
2262
2263 if (ui_window_def_kbd(window, event) == ui_claimed)
2264 return;
2265
2266 if (event->type == KEY_PRESS) {
2267 key_handle_press(event);
2268 (void) pane_update(&pane);
2269 (void) gfx_update(ui_window_get_gc(window));
2270 }
2271}
2272
2273/** Window unfocus event
2274 *
2275 * @param window Window
2276 * @param arg Argument (edit_t *)
2277 * @param focus Focus number
2278 */
2279static void edit_wnd_unfocus(ui_window_t *window, void *arg, unsigned focus)
2280{
2281 edit_t *edit = (edit_t *) arg;
2282
2283 (void)edit;
2284 cursor_setvis(false);
2285}
2286
2287/** Menu bar activate event
2288 *
2289 * @param mbar Menu bar
2290 * @param arg Argument (edit_t *)
2291 */
2292static void edit_menubar_activate(ui_menu_bar_t *mbar, void *arg)
2293{
2294 edit_t *edit = (edit_t *)arg;
2295
2296 (void)edit;
2297 cursor_setvis(false);
2298}
2299
2300/** Menu bar deactivate event
2301 *
2302 * @param mbar Menu bar
2303 * @param arg Argument (edit_t *)
2304 */
2305static void edit_menubar_deactivate(ui_menu_bar_t *mbar, void *arg)
2306{
2307 edit_t *edit = (edit_t *)arg;
2308
2309 (void)edit;
2310 pane_caret_display(&pane);
2311 cursor_setvis(true);
2312}
2313
2314/** File / Save menu entry selected.
2315 *
2316 * @param mentry Menu entry
2317 * @param arg Argument (edit_t *)
2318 */
2319static void edit_file_save(ui_menu_entry_t *mentry, void *arg)
2320{
2321 edit_t *edit = (edit_t *) arg;
2322
2323 (void)edit;
2324
2325 if (doc.file_name != NULL)
2326 file_save(doc.file_name);
2327 else
2328 file_save_as();
2329}
2330
2331/** File / Save As menu entry selected.
2332 *
2333 * @param mentry Menu entry
2334 * @param arg Argument (edit_t *)
2335 */
2336static void edit_file_save_as(ui_menu_entry_t *mentry, void *arg)
2337{
2338 edit_t *edit = (edit_t *) arg;
2339
2340 (void)edit;
2341 file_save_as();
2342}
2343
2344/** File / Exit menu entry selected.
2345 *
2346 * @param mentry Menu entry
2347 * @param arg Argument (edit_t *)
2348 */
2349static void edit_file_exit(ui_menu_entry_t *mentry, void *arg)
2350{
2351 edit_t *edit = (edit_t *) arg;
2352
2353 ui_quit(edit->ui);
2354}
2355
2356/** Edit / Cut menu entry selected.
2357 *
2358 * @param mentry Menu entry
2359 * @param arg Argument (edit_t *)
2360 */
2361static void edit_edit_cut(ui_menu_entry_t *mentry, void *arg)
2362{
2363 (void) arg;
2364 edit_cut();
2365 (void) gfx_update(ui_window_get_gc(edit.window));
2366}
2367
2368/** Edit / Copy menu entry selected.
2369 *
2370 * @param mentry Menu entry
2371 * @param arg Argument (edit_t *)
2372 */
2373static void edit_edit_copy(ui_menu_entry_t *mentry, void *arg)
2374{
2375 (void) arg;
2376 selection_copy();
2377}
2378
2379/** Edit / Paste menu entry selected.
2380 *
2381 * @param mentry Menu entry
2382 * @param arg Argument (edit_t *)
2383 */
2384static void edit_edit_paste(ui_menu_entry_t *mentry, void *arg)
2385{
2386 (void) arg;
2387 edit_paste();
2388 (void) gfx_update(ui_window_get_gc(edit.window));
2389}
2390
2391/** Edit / Delete menu entry selected.
2392 *
2393 * @param mentry Menu entry
2394 * @param arg Argument (edit_t *)
2395 */
2396static void edit_edit_delete(ui_menu_entry_t *mentry, void *arg)
2397{
2398 (void) arg;
2399
2400 if (selection_active())
2401 selection_delete();
2402
2403 pane.rflags |= REDRAW_CARET;
2404 (void) pane_update(&pane);
2405 (void) gfx_update(ui_window_get_gc(edit.window));
2406}
2407
2408/** Edit / Select All menu entry selected.
2409 *
2410 * @param mentry Menu entry
2411 * @param arg Argument (edit_t *)
2412 */
2413static void edit_edit_select_all(ui_menu_entry_t *mentry, void *arg)
2414{
2415 (void) arg;
2416
2417 selection_sel_all();
2418 pane.rflags |= (REDRAW_CARET | REDRAW_TEXT | REDRAW_STATUS);
2419 pane_update(&pane);
2420 (void) gfx_update(ui_window_get_gc(edit.window));
2421}
2422
2423/** Search / Find menu entry selected.
2424 *
2425 * @param mentry Menu entry
2426 * @param arg Argument (edit_t *)
2427 */
2428static void edit_search_find(ui_menu_entry_t *mentry, void *arg)
2429{
2430 (void) arg;
2431 search_prompt(false);
2432}
2433
2434/** Search / Reverse Find menu entry selected.
2435 *
2436 * @param mentry Menu entry
2437 * @param arg Argument (edit_t *)
2438 */
2439static void edit_search_reverse_find(ui_menu_entry_t *mentry, void *arg)
2440{
2441 (void) arg;
2442 search_prompt(true);
2443}
2444
2445/** Search / Find Next menu entry selected.
2446 *
2447 * @param mentry Menu entry
2448 * @param arg Argument (edit_t *)
2449 */
2450static void edit_search_find_next(ui_menu_entry_t *mentry, void *arg)
2451{
2452 (void) arg;
2453 search_repeat();
2454 (void) pane_update(&pane);
2455 (void) gfx_update(ui_window_get_gc(edit.window));
2456}
2457
2458/** Search / Go To Line menu entry selected.
2459 *
2460 * @param mentry Menu entry
2461 * @param arg Argument (edit_t *)
2462 */
2463static void edit_search_go_to_line(ui_menu_entry_t *mentry, void *arg)
2464{
2465 (void) arg;
2466 caret_go_to_line_ask();
2467}
2468
2469/** Save As dialog OK button press.
2470 *
2471 * @param dialog Save As dialog
2472 * @param arg Argument (ui_demo_t *)
2473 * @param fname File name
2474 */
2475static void save_as_dialog_bok(ui_file_dialog_t *dialog, void *arg,
2476 const char *fname)
2477{
2478 edit_t *edit = (edit_t *)arg;
2479 char *cname;
2480 errno_t rc;
2481
2482 (void)edit;
2483 ui_file_dialog_destroy(dialog);
2484
2485 cname = str_dup(fname);
2486 if (cname == NULL) {
2487 printf("Out of memory.\n");
2488 return;
2489 }
2490
2491 rc = file_save(fname);
2492 if (rc != EOK)
2493 return;
2494
2495 if (doc.file_name != NULL)
2496 free(doc.file_name);
2497 doc.file_name = cname;
2498
2499}
2500
2501/** Save As dialog cancel button press.
2502 *
2503 * @param dialog File dialog
2504 * @param arg Argument (ui_demo_t *)
2505 */
2506static void save_as_dialog_bcancel(ui_file_dialog_t *dialog, void *arg)
2507{
2508 edit_t *edit = (edit_t *)arg;
2509
2510 (void)edit;
2511 ui_file_dialog_destroy(dialog);
2512}
2513
2514/** Save As dialog close request.
2515 *
2516 * @param dialog File dialog
2517 * @param arg Argument (ui_demo_t *)
2518 */
2519static void save_as_dialog_close(ui_file_dialog_t *dialog, void *arg)
2520{
2521 edit_t *edit = (edit_t *)arg;
2522
2523 (void)edit;
2524 ui_file_dialog_destroy(dialog);
2525}
2526
2527/** Go To Line dialog OK button press.
2528 *
2529 * @param dialog Go To Line dialog
2530 * @param arg Argument (ui_demo_t *)
2531 * @param text Submitted text
2532 */
2533static void go_to_line_dialog_bok(ui_prompt_dialog_t *dialog, void *arg,
2534 const char *text)
2535{
2536 edit_t *edit = (edit_t *) arg;
2537 char *endptr;
2538 int line;
2539
2540 ui_prompt_dialog_destroy(dialog);
2541 line = strtol(text, &endptr, 10);
2542 if (*endptr != '\0') {
2543 status_display("Invalid number entered.");
2544 return;
2545 }
2546
2547 caret_move_absolute(line, pane.ideal_column, dir_before, false);
2548 (void)edit;
2549 (void) pane_update(&pane);
2550}
2551
2552/** Go To Line dialog cancel button press.
2553 *
2554 * @param dialog File dialog
2555 * @param arg Argument (ui_demo_t *)
2556 */
2557static void go_to_line_dialog_bcancel(ui_prompt_dialog_t *dialog, void *arg)
2558{
2559 edit_t *edit = (edit_t *) arg;
2560
2561 (void)edit;
2562 ui_prompt_dialog_destroy(dialog);
2563}
2564
2565/** Go To Line dialog close request.
2566 *
2567 * @param dialog File dialog
2568 * @param arg Argument (ui_demo_t *)
2569 */
2570static void go_to_line_dialog_close(ui_prompt_dialog_t *dialog, void *arg)
2571{
2572 edit_t *edit = (edit_t *) arg;
2573
2574 (void)edit;
2575 ui_prompt_dialog_destroy(dialog);
2576}
2577
2578/** Search dialog OK button press.
2579 *
2580 * @param dialog Search dialog
2581 * @param arg Argument (ui_demo_t *)
2582 * @param text Submitted text
2583 */
2584static void search_dialog_bok(ui_prompt_dialog_t *dialog, void *arg,
2585 const char *text)
2586{
2587 edit_t *edit = (edit_t *) arg;
2588 char *pattern;
2589 bool reverse;
2590
2591 (void)edit;
2592 ui_prompt_dialog_destroy(dialog);
2593
2594 /* Abort if search phrase is empty */
2595 if (text[0] == '\0')
2596 return;
2597
2598 pattern = str_dup(text);
2599 reverse = pane.search_reverse;
2600
2601 if (pane.previous_search)
2602 free(pane.previous_search);
2603 pane.previous_search = pattern;
2604 pane.previous_search_reverse = reverse;
2605
2606 search(pattern, reverse);
2607
2608 (void) pane_update(&pane);
2609}
2610
2611/** Search dialog cancel button press.
2612 *
2613 * @param dialog File dialog
2614 * @param arg Argument (ui_demo_t *)
2615 */
2616static void search_dialog_bcancel(ui_prompt_dialog_t *dialog, void *arg)
2617{
2618 edit_t *edit = (edit_t *) arg;
2619
2620 (void)edit;
2621 ui_prompt_dialog_destroy(dialog);
2622}
2623
2624/** Search dialog close request.
2625 *
2626 * @param dialog File dialog
2627 * @param arg Argument (ui_demo_t *)
2628 */
2629static void search_dialog_close(ui_prompt_dialog_t *dialog, void *arg)
2630{
2631 edit_t *edit = (edit_t *) arg;
2632
2633 (void)edit;
2634 ui_prompt_dialog_destroy(dialog);
2635}
2636
2637/** @}
2638 */
Note: See TracBrowser for help on using the repository browser.