source: mainline/uspace/app/edit/edit.c@ 448c448

serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 448c448 was 448c448, checked in by Jiri Svoboda <jiri@…>, 4 years ago

Add menu entry equivalents to hot keys for editor actions

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