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

Last change on this file since dc5c303 was 46bd63c9, checked in by Jiri Svoboda <jiri@…>, 2 years ago

Split drop-down menu into two classes: drop-down and menu

Naming is clearly the hardest problem in computer science.

  • Property mode set to 100644
File size: 55.7 KB
Line 
1/*
2 * Copyright (c) 2023 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_kbd_event(ui_window_t *, void *, kbd_event_t *);
241
242static ui_window_cb_t edit_window_cb = {
243 .close = edit_wnd_close,
244 .kbd = edit_wnd_kbd_event
245};
246
247static void edit_file_save(ui_menu_entry_t *, void *);
248static void edit_file_save_as(ui_menu_entry_t *, void *);
249static void edit_file_exit(ui_menu_entry_t *, void *);
250static void edit_edit_cut(ui_menu_entry_t *, void *);
251static void edit_edit_copy(ui_menu_entry_t *, void *);
252static void edit_edit_paste(ui_menu_entry_t *, void *);
253static void edit_edit_delete(ui_menu_entry_t *, void *);
254static void edit_edit_select_all(ui_menu_entry_t *, void *);
255static void edit_search_find(ui_menu_entry_t *, void *);
256static void edit_search_reverse_find(ui_menu_entry_t *, void *);
257static void edit_search_find_next(ui_menu_entry_t *, void *);
258static void edit_search_go_to_line(ui_menu_entry_t *, void *);
259
260static void pane_ctl_destroy(void *);
261static errno_t pane_ctl_paint(void *);
262static ui_evclaim_t pane_ctl_pos_event(void *, pos_event_t *);
263
264/** Pabe control ops */
265ui_control_ops_t pane_ctl_ops = {
266 .destroy = pane_ctl_destroy,
267 .paint = pane_ctl_paint,
268 .pos_event = pane_ctl_pos_event
269};
270
271static void save_as_dialog_bok(ui_file_dialog_t *, void *, const char *);
272static void save_as_dialog_bcancel(ui_file_dialog_t *, void *);
273static void save_as_dialog_close(ui_file_dialog_t *, void *);
274
275static ui_file_dialog_cb_t save_as_dialog_cb = {
276 .bok = save_as_dialog_bok,
277 .bcancel = save_as_dialog_bcancel,
278 .close = save_as_dialog_close
279};
280
281static void go_to_line_dialog_bok(ui_prompt_dialog_t *, void *, const char *);
282static void go_to_line_dialog_bcancel(ui_prompt_dialog_t *, void *);
283static void go_to_line_dialog_close(ui_prompt_dialog_t *, void *);
284
285static ui_prompt_dialog_cb_t go_to_line_dialog_cb = {
286 .bok = go_to_line_dialog_bok,
287 .bcancel = go_to_line_dialog_bcancel,
288 .close = go_to_line_dialog_close
289};
290
291static void search_dialog_bok(ui_prompt_dialog_t *, void *, const char *);
292static void search_dialog_bcancel(ui_prompt_dialog_t *, void *);
293static void search_dialog_close(ui_prompt_dialog_t *, void *);
294
295static ui_prompt_dialog_cb_t search_dialog_cb = {
296 .bok = search_dialog_bok,
297 .bcancel = search_dialog_bcancel,
298 .close = search_dialog_close
299};
300
301int main(int argc, char *argv[])
302{
303 bool new_file;
304 errno_t rc;
305
306 pane.sh_row = 1;
307 pane.sh_column = 1;
308
309 /* Start with an empty sheet. */
310 rc = sheet_create(&doc.sh);
311 if (rc != EOK) {
312 printf("Out of memory.\n");
313 return -1;
314 }
315
316 /* Place caret at the beginning of file. */
317 spt_t sof;
318 pt_get_sof(&sof);
319 sheet_place_tag(doc.sh, &sof, &pane.caret_pos);
320 pane.ideal_column = 1;
321
322 if (argc == 2) {
323 doc.file_name = str_dup(argv[1]);
324 } else if (argc > 1) {
325 printf("Invalid arguments.\n");
326 return -2;
327 } else {
328 doc.file_name = NULL;
329 }
330
331 new_file = false;
332
333 if (doc.file_name == NULL || file_insert(doc.file_name) != EOK)
334 new_file = true;
335
336 /* Place selection start tag. */
337 sheet_place_tag(doc.sh, &sof, &pane.sel_start);
338
339 /* Move to beginning of file. */
340 pt_get_sof(&sof);
341
342 /* Create UI */
343 rc = edit_ui_create(&edit);
344 if (rc != EOK)
345 return 1;
346
347 caret_move(sof, true, true);
348
349 /* Initial display */
350 rc = ui_window_paint(edit.window);
351 if (rc != EOK) {
352 printf("Error painting window.\n");
353 return rc;
354 }
355
356 pane_status_display(&pane);
357 if (new_file && doc.file_name != NULL)
358 status_display("File not found. Starting empty file.");
359 pane_caret_display(&pane);
360 cursor_setvis(true);
361
362 ui_run(edit.ui);
363
364 edit_ui_destroy(&edit);
365 return 0;
366}
367
368/** Create text editor UI.
369 *
370 * @param edit Editor
371 * @return EOK on success or an error code
372 */
373static errno_t edit_ui_create(edit_t *edit)
374{
375 errno_t rc;
376 ui_wnd_params_t params;
377 ui_fixed_t *fixed = NULL;
378 ui_menu_t *mfile = NULL;
379 ui_menu_t *medit = NULL;
380 ui_menu_entry_t *msave = NULL;
381 ui_menu_entry_t *msaveas = NULL;
382 ui_menu_entry_t *mfsep = NULL;
383 ui_menu_entry_t *mexit = NULL;
384 ui_menu_entry_t *mcut = NULL;
385 ui_menu_entry_t *mcopy = NULL;
386 ui_menu_entry_t *mpaste = NULL;
387 ui_menu_entry_t *mdelete = NULL;
388 ui_menu_entry_t *mesep = NULL;
389 ui_menu_entry_t *mselall = NULL;
390 ui_menu_t *msearch = NULL;
391 ui_menu_entry_t *mfind = NULL;
392 ui_menu_entry_t *mfindr = NULL;
393 ui_menu_entry_t *mfindn = NULL;
394 ui_menu_entry_t *mssep = NULL;
395 ui_menu_entry_t *mgoto = NULL;
396 gfx_rect_t arect;
397 gfx_rect_t rect;
398
399 rc = ui_create(UI_CONSOLE_DEFAULT, &edit->ui);
400 if (rc != EOK) {
401 printf("Error creating UI on display %s.\n",
402 UI_CONSOLE_DEFAULT);
403 goto error;
404 }
405
406 ui_wnd_params_init(&params);
407 params.caption = "Text Editor";
408 params.style &= ~ui_wds_decorated;
409 params.placement = ui_wnd_place_full_screen;
410
411 rc = ui_window_create(edit->ui, &params, &edit->window);
412 if (rc != EOK) {
413 printf("Error creating window.\n");
414 goto error;
415 }
416
417 ui_window_set_cb(edit->window, &edit_window_cb, (void *) edit);
418
419 edit->ui_res = ui_window_get_res(edit->window);
420
421 rc = ui_fixed_create(&fixed);
422 if (rc != EOK) {
423 printf("Error creating fixed layout.\n");
424 return rc;
425 }
426
427 rc = ui_menu_bar_create(edit->ui, edit->window, &edit->menubar);
428 if (rc != EOK) {
429 printf("Error creating menu bar.\n");
430 return rc;
431 }
432
433 rc = ui_menu_dd_create(edit->menubar, "~F~ile", NULL, &mfile);
434 if (rc != EOK) {
435 printf("Error creating menu.\n");
436 return rc;
437 }
438
439 rc = ui_menu_entry_create(mfile, "~S~ave", "Ctrl-S", &msave);
440 if (rc != EOK) {
441 printf("Error creating menu.\n");
442 return rc;
443 }
444
445 ui_menu_entry_set_cb(msave, edit_file_save, (void *) edit);
446
447 rc = ui_menu_entry_create(mfile, "Save ~A~s", "Ctrl-E", &msaveas);
448 if (rc != EOK) {
449 printf("Error creating menu.\n");
450 return rc;
451 }
452
453 ui_menu_entry_set_cb(msaveas, edit_file_save_as, (void *) edit);
454
455 rc = ui_menu_entry_sep_create(mfile, &mfsep);
456 if (rc != EOK) {
457 printf("Error creating menu.\n");
458 return rc;
459 }
460
461 rc = ui_menu_entry_create(mfile, "E~x~it", "Ctrl-Q", &mexit);
462 if (rc != EOK) {
463 printf("Error creating menu.\n");
464 return rc;
465 }
466
467 ui_menu_entry_set_cb(mexit, edit_file_exit, (void *) edit);
468
469 rc = ui_menu_dd_create(edit->menubar, "~E~dit", NULL, &medit);
470 if (rc != EOK) {
471 printf("Error creating menu.\n");
472 return rc;
473 }
474
475 rc = ui_menu_entry_create(medit, "Cu~t~", "Ctrl-X", &mcut);
476 if (rc != EOK) {
477 printf("Error creating menu.\n");
478 return rc;
479 }
480
481 ui_menu_entry_set_cb(mcut, edit_edit_cut, (void *) edit);
482
483 rc = ui_menu_entry_create(medit, "~C~opy", "Ctrl-C", &mcopy);
484 if (rc != EOK) {
485 printf("Error creating menu.\n");
486 return rc;
487 }
488
489 ui_menu_entry_set_cb(mcopy, edit_edit_copy, (void *) edit);
490
491 rc = ui_menu_entry_create(medit, "~P~aste", "Ctrl-V", &mpaste);
492 if (rc != EOK) {
493 printf("Error creating menu.\n");
494 return rc;
495 }
496
497 ui_menu_entry_set_cb(mpaste, edit_edit_paste, (void *) edit);
498
499 rc = ui_menu_entry_create(medit, "~D~elete", "Del", &mdelete);
500 if (rc != EOK) {
501 printf("Error creating menu.\n");
502 return rc;
503 }
504
505 ui_menu_entry_set_cb(mdelete, edit_edit_delete, (void *) edit);
506
507 rc = ui_menu_entry_sep_create(medit, &mesep);
508 if (rc != EOK) {
509 printf("Error creating menu.\n");
510 return rc;
511 }
512
513 rc = ui_menu_entry_create(medit, "Select ~A~ll", "Ctrl-A", &mselall);
514 if (rc != EOK) {
515 printf("Error creating menu.\n");
516 return rc;
517 }
518
519 ui_menu_entry_set_cb(mselall, edit_edit_select_all, (void *) edit);
520
521 rc = ui_menu_dd_create(edit->menubar, "~S~earch", NULL, &msearch);
522 if (rc != EOK) {
523 printf("Error creating menu.\n");
524 return rc;
525 }
526
527 rc = ui_menu_entry_create(msearch, "~F~ind", "Ctrl-F", &mfind);
528 if (rc != EOK) {
529 printf("Error creating menu.\n");
530 return rc;
531 }
532
533 ui_menu_entry_set_cb(mfind, edit_search_find, (void *) edit);
534
535 rc = ui_menu_entry_create(msearch, "~R~everse Find", "Ctrl-Shift-F", &mfindr);
536 if (rc != EOK) {
537 printf("Error creating menu.\n");
538 return rc;
539 }
540
541 ui_menu_entry_set_cb(mfindr, edit_search_reverse_find, (void *) edit);
542
543 rc = ui_menu_entry_create(msearch, "Find ~N~ext", "Ctrl-N", &mfindn);
544 if (rc != EOK) {
545 printf("Error creating menu.\n");
546 return rc;
547 }
548
549 ui_menu_entry_set_cb(mfindn, edit_search_find_next, (void *) edit);
550
551 rc = ui_menu_entry_sep_create(msearch, &mssep);
552 if (rc != EOK) {
553 printf("Error creating menu.\n");
554 return rc;
555 }
556
557 rc = ui_menu_entry_create(msearch, "Go To ~L~ine", "Ctrl-L", &mgoto);
558 if (rc != EOK) {
559 printf("Error creating menu.\n");
560 return rc;
561 }
562
563 ui_menu_entry_set_cb(mgoto, edit_search_go_to_line, (void *) edit);
564
565 ui_window_get_app_rect(edit->window, &arect);
566
567 rect.p0 = arect.p0;
568 rect.p1.x = arect.p1.x;
569 rect.p1.y = arect.p0.y + 1;
570 ui_menu_bar_set_rect(edit->menubar, &rect);
571
572 rc = ui_fixed_add(fixed, ui_menu_bar_ctl(edit->menubar));
573 if (rc != EOK) {
574 printf("Error adding control to layout.\n");
575 return rc;
576 }
577
578 rc = pane_init(edit->window, &pane);
579 if (rc != EOK) {
580 printf("Error initializing pane.\n");
581 return rc;
582 }
583
584 rc = ui_fixed_add(fixed, pane_ctl(&pane));
585 if (rc != EOK) {
586 printf("Error adding control to layout.\n");
587 return rc;
588 }
589
590 rc = ui_label_create(edit->ui_res, "", &edit->status);
591 if (rc != EOK) {
592 printf("Error creating menu bar.\n");
593 return rc;
594 }
595
596 rect.p0.x = arect.p0.x;
597 rect.p0.y = arect.p1.y - 1;
598 rect.p1 = arect.p1;
599 ui_label_set_rect(edit->status, &rect);
600
601 rc = ui_fixed_add(fixed, ui_label_ctl(edit->status));
602 if (rc != EOK) {
603 printf("Error adding control to layout.\n");
604 return rc;
605 }
606
607 ui_window_add(edit->window, ui_fixed_ctl(fixed));
608 return EOK;
609error:
610 if (edit->window != NULL)
611 ui_window_destroy(edit->window);
612 if (edit->ui != NULL)
613 ui_destroy(edit->ui);
614 return rc;
615}
616
617/** Destroy text editor UI.
618 *
619 * @param edit Editor
620 */
621static void edit_ui_destroy(edit_t *edit)
622{
623 ui_window_destroy(edit->window);
624 ui_destroy(edit->ui);
625}
626
627/* Handle key press. */
628static void key_handle_press(kbd_event_t *ev)
629{
630 if (((ev->mods & KM_ALT) == 0) &&
631 ((ev->mods & KM_SHIFT) == 0) &&
632 (ev->mods & KM_CTRL) != 0) {
633 key_handle_ctrl(ev);
634 } else if (((ev->mods & KM_ALT) == 0) &&
635 ((ev->mods & KM_CTRL) == 0) &&
636 (ev->mods & KM_SHIFT) != 0) {
637 key_handle_shift(ev);
638 } else if (((ev->mods & KM_ALT) == 0) &&
639 ((ev->mods & KM_CTRL) != 0) &&
640 (ev->mods & KM_SHIFT) != 0) {
641 key_handle_shift_ctrl(ev);
642 } else if ((ev->mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
643 key_handle_unmod(ev);
644 }
645}
646
647static void cursor_setvis(bool visible)
648{
649 gfx_context_t *gc = ui_window_get_gc(edit.window);
650
651 (void) gfx_cursor_set_visible(gc, visible);
652}
653
654/** Handle key without modifier. */
655static void key_handle_unmod(kbd_event_t const *ev)
656{
657 switch (ev->key) {
658 case KC_ENTER:
659 selection_delete();
660 insert_char('\n');
661 caret_update();
662 break;
663 case KC_LEFT:
664 case KC_RIGHT:
665 case KC_UP:
666 case KC_DOWN:
667 case KC_HOME:
668 case KC_END:
669 case KC_PAGE_UP:
670 case KC_PAGE_DOWN:
671 key_handle_movement(ev->key, false);
672 break;
673 case KC_BACKSPACE:
674 if (selection_active())
675 selection_delete();
676 else
677 delete_char_before();
678 caret_update();
679 break;
680 case KC_DELETE:
681 if (selection_active())
682 selection_delete();
683 else
684 delete_char_after();
685 caret_update();
686 break;
687 default:
688 if (ev->c >= 32 || ev->c == '\t') {
689 selection_delete();
690 insert_char(ev->c);
691 caret_update();
692 }
693 break;
694 }
695}
696
697/** Handle Shift-key combination. */
698static void key_handle_shift(kbd_event_t const *ev)
699{
700 switch (ev->key) {
701 case KC_LEFT:
702 case KC_RIGHT:
703 case KC_UP:
704 case KC_DOWN:
705 case KC_HOME:
706 case KC_END:
707 case KC_PAGE_UP:
708 case KC_PAGE_DOWN:
709 key_handle_movement(ev->key, true);
710 break;
711 default:
712 if (ev->c >= 32 || ev->c == '\t') {
713 selection_delete();
714 insert_char(ev->c);
715 caret_update();
716 }
717 break;
718 }
719}
720
721/** Handle Ctrl-key combination. */
722static void key_handle_ctrl(kbd_event_t const *ev)
723{
724 spt_t pt;
725 switch (ev->key) {
726 case KC_Q:
727 ui_quit(edit.ui);
728 break;
729 case KC_S:
730 if (doc.file_name != NULL)
731 file_save(doc.file_name);
732 else
733 file_save_as();
734 break;
735 case KC_E:
736 file_save_as();
737 break;
738 case KC_C:
739 selection_copy();
740 break;
741 case KC_V:
742 edit_paste();
743 break;
744 case KC_X:
745 edit_cut();
746 break;
747 case KC_A:
748 selection_sel_all();
749 break;
750 case KC_RIGHT:
751 caret_move_word_right(false);
752 break;
753 case KC_LEFT:
754 caret_move_word_left(false);
755 break;
756 case KC_L:
757 caret_go_to_line_ask();
758 break;
759 case KC_F:
760 search_prompt(false);
761 break;
762 case KC_N:
763 search_repeat();
764 break;
765 case KC_HOME:
766 pt_get_sof(&pt);
767 caret_move(pt, false, true);
768 break;
769 case KC_END:
770 pt_get_eof(&pt);
771 caret_move(pt, false, true);
772 break;
773 default:
774 break;
775 }
776}
777
778static void key_handle_shift_ctrl(kbd_event_t const *ev)
779{
780 spt_t pt;
781 switch (ev->key) {
782 case KC_LEFT:
783 caret_move_word_left(true);
784 break;
785 case KC_RIGHT:
786 caret_move_word_right(true);
787 break;
788 case KC_F:
789 search_prompt(true);
790 break;
791 case KC_HOME:
792 pt_get_sof(&pt);
793 caret_move(pt, true, true);
794 break;
795 case KC_END:
796 pt_get_eof(&pt);
797 caret_move(pt, true, true);
798 break;
799 default:
800 break;
801 }
802}
803
804static void pos_handle(pos_event_t *ev)
805{
806 coord_t bc;
807 spt_t pt;
808 bool select;
809
810 if (ev->type == POS_PRESS && ev->vpos < (unsigned)pane.rows) {
811 bc.row = pane.sh_row + ev->vpos - pane.rect.p0.y;
812 bc.column = pane.sh_column + ev->hpos - pane.rect.p0.x;
813 sheet_get_cell_pt(doc.sh, &bc, dir_before, &pt);
814
815 select = (pane.keymod & KM_SHIFT) != 0;
816
817 caret_move(pt, select, true);
818 pane_update(&pane);
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 fdparams.ifname = 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 memmove(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.font = font;
1263 fmt.color = pane->color;
1264
1265 /* Determine selection start and end. */
1266
1267 tag_get_pt(&pane->sel_start, &pt);
1268 spt_get_coord(&pt, &csel_start);
1269
1270 tag_get_pt(&pane->caret_pos, &pt);
1271 spt_get_coord(&pt, &csel_end);
1272
1273 if (coord_cmp(&csel_start, &csel_end) > 0) {
1274 ctmp = csel_start;
1275 csel_start = csel_end;
1276 csel_end = ctmp;
1277 }
1278
1279 /* Draw rows from the sheet. */
1280
1281 for (i = r0; i < r1; ++i) {
1282 tpos.x = pane->rect.p0.x;
1283 tpos.y = pane->rect.p0.y + i;
1284
1285 /* Starting point for row display */
1286 rbc.row = pane->sh_row + i;
1287 rbc.column = pane->sh_column;
1288 sheet_get_cell_pt(doc.sh, &rbc, dir_before, &rb);
1289
1290 /* Ending point for row display */
1291 rec.row = pane->sh_row + i;
1292 rec.column = pane->sh_column + pane->columns;
1293 sheet_get_cell_pt(doc.sh, &rec, dir_before, &re);
1294
1295 /* Copy the text of the row to the buffer. */
1296 sheet_copy_out(doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
1297
1298 /* Display text from the buffer. */
1299
1300 if (coord_cmp(&csel_start, &rbc) <= 0 &&
1301 coord_cmp(&rbc, &csel_end) < 0) {
1302 fmt.color = pane->sel_color;
1303 }
1304
1305 size = str_size(row_buf);
1306 pos = 0;
1307 s_column = pane->sh_column;
1308 while (pos < size) {
1309 if ((csel_start.row == rbc.row) && (csel_start.column == s_column))
1310 fmt.color = pane->sel_color;
1311
1312 if ((csel_end.row == rbc.row) && (csel_end.column == s_column))
1313 fmt.color = pane->color;
1314
1315 c = str_decode(row_buf, &pos, size);
1316 if (c != '\t') {
1317 cpos = 0;
1318 rc = chr_encode(c, cbuf, &cpos, sizeof(cbuf));
1319 if (rc != EOK)
1320 return rc;
1321
1322 rc = gfx_puttext(&tpos, &fmt, cbuf);
1323 if (rc != EOK)
1324 return rc;
1325
1326 s_column += 1;
1327 tpos.x++;
1328 } else {
1329 fill = 1 + ALIGN_UP(s_column, TAB_WIDTH) -
1330 s_column;
1331
1332 rc = gfx_set_color(gc, fmt.color);
1333 if (rc != EOK)
1334 return rc;
1335
1336 rect.p0.x = tpos.x;
1337 rect.p0.y = tpos.y;
1338 rect.p1.x = tpos.x + fill;
1339 rect.p1.y = tpos.y + 1;
1340
1341 rc = gfx_fill_rect(gc, &rect);
1342 if (rc != EOK)
1343 return rc;
1344
1345 s_column += fill;
1346 tpos.x += fill;
1347 }
1348 }
1349
1350 if ((csel_end.row == rbc.row) && (csel_end.column == s_column))
1351 fmt.color = pane->color;
1352
1353 /* Fill until the end of display area. */
1354
1355 rc = gfx_set_color(gc, fmt.color);
1356 if (rc != EOK)
1357 return rc;
1358
1359 rect.p0.x = tpos.x;
1360 rect.p0.y = tpos.y;
1361 rect.p1.x = pane->rect.p1.x;
1362 rect.p1.y = tpos.y + 1;
1363
1364 rc = gfx_fill_rect(gc, &rect);
1365 if (rc != EOK)
1366 return rc;
1367 }
1368
1369 return EOK;
1370}
1371
1372/** Display pane status in the status line.
1373 *
1374 * @param pane Pane
1375 */
1376static void pane_status_display(pane_t *pane)
1377{
1378 spt_t caret_pt;
1379 coord_t coord;
1380 int last_row;
1381 char *fname;
1382 char *p;
1383 char *text;
1384 size_t n;
1385 size_t nextra;
1386 size_t fnw;
1387
1388 tag_get_pt(&pane->caret_pos, &caret_pt);
1389 spt_get_coord(&caret_pt, &coord);
1390
1391 sheet_get_num_rows(doc.sh, &last_row);
1392
1393 if (doc.file_name != NULL) {
1394 /* Remove directory component */
1395 p = str_rchr(doc.file_name, '/');
1396 if (p != NULL)
1397 fname = str_dup(p + 1);
1398 else
1399 fname = str_dup(doc.file_name);
1400 } else {
1401 fname = str_dup("<unnamed>");
1402 }
1403
1404 if (fname == NULL)
1405 return;
1406
1407 /*
1408 * Make sure the status fits on the screen. This loop should
1409 * be executed at most twice.
1410 */
1411 while (true) {
1412 int rc = asprintf(&text, "%d, %d (%d): File '%s'. Ctrl-Q Quit "
1413 "F10 Menu", coord.row, coord.column, last_row, fname);
1414 if (rc < 0) {
1415 n = 0;
1416 goto finish;
1417 }
1418
1419 /* If it already fits, we're done */
1420 n = str_width(text);
1421 if ((int)n <= pane->columns - 2)
1422 break;
1423
1424 /* Compute number of excess characters */
1425 nextra = n - (pane->columns - 2);
1426 /** With of the file name part */
1427 fnw = str_width(fname);
1428
1429 /*
1430 * If reducing file name to two characters '..' won't help,
1431 * just give up and print a blank status.
1432 */
1433 if (nextra > fnw - 2) {
1434 text[0] = '\0';
1435 goto finish;
1436 }
1437
1438 /* Compute position where we overwrite with '..\0' */
1439 if (fnw >= nextra + 2) {
1440 p = fname + str_lsize(fname, fnw - nextra - 2);
1441 } else {
1442 p = fname;
1443 }
1444
1445 /* Shorten the string */
1446 p[0] = p[1] = '.';
1447 p[2] = '\0';
1448
1449 /* Need to format the string once more. */
1450 free(text);
1451 }
1452
1453finish:
1454 (void) ui_label_set_text(edit.status, text);
1455 (void) ui_label_paint(edit.status);
1456 free(text);
1457 free(fname);
1458}
1459
1460/** Set cursor to reflect position of the caret.
1461 *
1462 * @param pane Pane
1463 */
1464static void pane_caret_display(pane_t *pane)
1465{
1466 spt_t caret_pt;
1467 coord_t coord;
1468 gfx_coord2_t pos;
1469 gfx_context_t *gc;
1470
1471 tag_get_pt(&pane->caret_pos, &caret_pt);
1472
1473 spt_get_coord(&caret_pt, &coord);
1474
1475 gc = ui_window_get_gc(edit.window);
1476 pos.x = pane->rect.p0.x + coord.column - pane->sh_column;
1477 pos.y = pane->rect.p0.y + coord.row - pane->sh_row;
1478
1479 (void) gfx_cursor_set_pos(gc, &pos);
1480}
1481
1482/** Destroy pane control.
1483 *
1484 * @param arg Argument (pane_t *)
1485 */
1486static void pane_ctl_destroy(void *arg)
1487{
1488 pane_t *pane = (pane_t *)arg;
1489
1490 pane_fini(pane);
1491}
1492
1493/** Paint pane control.
1494 *
1495 * @param arg Argument (pane_t *)
1496 */
1497static errno_t pane_ctl_paint(void *arg)
1498{
1499 pane_t *pane = (pane_t *)arg;
1500 gfx_context_t *gc;
1501 errno_t rc;
1502
1503 gc = ui_window_get_gc(pane->window);
1504
1505 rc = pane_text_display(pane);
1506 if (rc != EOK)
1507 goto error;
1508
1509 rc = gfx_update(gc);
1510 if (rc != EOK)
1511 goto error;
1512
1513error:
1514 return rc;
1515}
1516
1517/** Handle pane control position event.
1518 *
1519 * @param arg Argument (pane_t *)
1520 * @param event Position event
1521 */
1522static ui_evclaim_t pane_ctl_pos_event(void *arg, pos_event_t *event)
1523{
1524 gfx_coord2_t pos;
1525
1526 pos.x = event->hpos;
1527 pos.y = event->vpos;
1528
1529 if (!gfx_pix_inside_rect(&pos, &pane.rect))
1530 return ui_unclaimed;
1531
1532 pos_handle(event);
1533 (void) gfx_update(ui_window_get_gc(edit.window));
1534 return ui_claimed;
1535}
1536
1537/** Insert a character at caret position. */
1538static void insert_char(char32_t c)
1539{
1540 spt_t pt;
1541 char cbuf[STR_BOUNDS(1) + 1];
1542 size_t offs;
1543
1544 tag_get_pt(&pane.caret_pos, &pt);
1545
1546 offs = 0;
1547 chr_encode(c, cbuf, &offs, STR_BOUNDS(1) + 1);
1548 cbuf[offs] = '\0';
1549
1550 (void) sheet_insert(doc.sh, &pt, dir_before, cbuf);
1551
1552 pane.rflags |= REDRAW_ROW;
1553 if (c == '\n')
1554 pane.rflags |= REDRAW_TEXT;
1555}
1556
1557/** Delete the character before the caret. */
1558static void delete_char_before(void)
1559{
1560 spt_t sp, ep;
1561 coord_t coord;
1562
1563 tag_get_pt(&pane.caret_pos, &ep);
1564 spt_get_coord(&ep, &coord);
1565
1566 coord.column -= 1;
1567 sheet_get_cell_pt(doc.sh, &coord, dir_before, &sp);
1568
1569 (void) sheet_delete(doc.sh, &sp, &ep);
1570
1571 pane.rflags |= REDRAW_ROW;
1572 if (coord.column < 1)
1573 pane.rflags |= REDRAW_TEXT;
1574}
1575
1576/** Delete the character after the caret. */
1577static void delete_char_after(void)
1578{
1579 spt_t sp, ep;
1580 coord_t sc, ec;
1581
1582 tag_get_pt(&pane.caret_pos, &sp);
1583 spt_get_coord(&sp, &sc);
1584
1585 sheet_get_cell_pt(doc.sh, &sc, dir_after, &ep);
1586 spt_get_coord(&ep, &ec);
1587
1588 (void) sheet_delete(doc.sh, &sp, &ep);
1589
1590 pane.rflags |= REDRAW_ROW;
1591 if (ec.row != sc.row)
1592 pane.rflags |= REDRAW_TEXT;
1593}
1594
1595/** Scroll pane after caret has moved.
1596 *
1597 * After modifying the position of the caret, this is called to scroll
1598 * the pane to ensure that the caret is in the visible area.
1599 */
1600static void caret_update(void)
1601{
1602 spt_t pt;
1603 coord_t coord;
1604
1605 tag_get_pt(&pane.caret_pos, &pt);
1606 spt_get_coord(&pt, &coord);
1607
1608 /* Scroll pane vertically. */
1609
1610 if (coord.row < pane.sh_row) {
1611 pane.sh_row = coord.row;
1612 pane.rflags |= REDRAW_TEXT;
1613 }
1614
1615 if (coord.row > pane.sh_row + pane.rows - 1) {
1616 pane.sh_row = coord.row - pane.rows + 1;
1617 pane.rflags |= REDRAW_TEXT;
1618 }
1619
1620 /* Scroll pane horizontally. */
1621
1622 if (coord.column < pane.sh_column) {
1623 pane.sh_column = coord.column;
1624 pane.rflags |= REDRAW_TEXT;
1625 }
1626
1627 if (coord.column > pane.sh_column + pane.columns - 1) {
1628 pane.sh_column = coord.column - pane.columns + 1;
1629 pane.rflags |= REDRAW_TEXT;
1630 }
1631
1632 pane.rflags |= (REDRAW_CARET | REDRAW_STATUS);
1633}
1634
1635/** Relatively move caret position.
1636 *
1637 * Moves caret relatively to the current position. Looking at the first
1638 * character cell after the caret and moving by @a drow and @a dcolumn, we get
1639 * to a new character cell, and thus a new character. Then we either go to the
1640 * point before the the character or after it, depending on @a align_dir.
1641 *
1642 * @param select true if the selection tag should stay where it is
1643 */
1644static void caret_move_relative(int drow, int dcolumn, enum dir_spec align_dir,
1645 bool select)
1646{
1647 spt_t pt;
1648 coord_t coord;
1649 int num_rows;
1650 bool pure_vertical;
1651
1652 tag_get_pt(&pane.caret_pos, &pt);
1653 spt_get_coord(&pt, &coord);
1654 coord.row += drow;
1655 coord.column += dcolumn;
1656
1657 /* Clamp coordinates. */
1658 if (drow < 0 && coord.row < 1)
1659 coord.row = 1;
1660 if (dcolumn < 0 && coord.column < 1) {
1661 if (coord.row < 2)
1662 coord.column = 1;
1663 else {
1664 coord.row--;
1665 sheet_get_row_width(doc.sh, coord.row, &coord.column);
1666 }
1667 }
1668 if (drow > 0) {
1669 sheet_get_num_rows(doc.sh, &num_rows);
1670 if (coord.row > num_rows)
1671 coord.row = num_rows;
1672 }
1673
1674 /* For purely vertical movement try attaining @c ideal_column. */
1675 pure_vertical = (dcolumn == 0 && align_dir == dir_before);
1676 if (pure_vertical)
1677 coord.column = pane.ideal_column;
1678
1679 /*
1680 * Select the point before or after the character at the designated
1681 * coordinates. The character can be wider than one cell (e.g. tab).
1682 */
1683 sheet_get_cell_pt(doc.sh, &coord, align_dir, &pt);
1684
1685 /* For non-vertical movement set the new value for @c ideal_column. */
1686 caret_move(pt, select, !pure_vertical);
1687}
1688
1689/** Absolutely move caret position.
1690 *
1691 * Moves caret to a specified position. We get to a new character cell, and
1692 * thus a new character. Then we either go to the point before the the character
1693 * or after it, depending on @a align_dir.
1694 *
1695 * @param select true if the selection tag should stay where it is
1696 */
1697static void caret_move_absolute(int row, int column, enum dir_spec align_dir,
1698 bool select)
1699{
1700 coord_t coord;
1701 coord.row = row;
1702 coord.column = column;
1703
1704 spt_t pt;
1705 sheet_get_cell_pt(doc.sh, &coord, align_dir, &pt);
1706
1707 caret_move(pt, select, true);
1708}
1709
1710/** Find beginning of a word to the left of spt */
1711static spt_t pt_find_word_left(spt_t spt)
1712{
1713 do {
1714 spt_prev_char(spt, &spt);
1715 } while (!pt_is_word_beginning(&spt));
1716 return spt;
1717}
1718
1719/** Find beginning of a word to the right of spt */
1720static spt_t pt_find_word_right(spt_t spt)
1721{
1722 do {
1723 spt_next_char(spt, &spt);
1724 } while (!pt_is_word_beginning(&spt));
1725 return spt;
1726}
1727
1728static void caret_move_word_left(bool select)
1729{
1730 spt_t pt;
1731 tag_get_pt(&pane.caret_pos, &pt);
1732 spt_t word_left = pt_find_word_left(pt);
1733 caret_move(word_left, select, true);
1734}
1735
1736static void caret_move_word_right(bool select)
1737{
1738 spt_t pt;
1739 tag_get_pt(&pane.caret_pos, &pt);
1740 spt_t word_right = pt_find_word_right(pt);
1741 caret_move(word_right, select, true);
1742}
1743
1744/** Ask for line and go to it. */
1745static void caret_go_to_line_ask(void)
1746{
1747 ui_prompt_dialog_params_t pdparams;
1748 ui_prompt_dialog_t *dialog;
1749 errno_t rc;
1750
1751 ui_prompt_dialog_params_init(&pdparams);
1752 pdparams.caption = "Go To Line";
1753 pdparams.prompt = "Line Number";
1754
1755 rc = ui_prompt_dialog_create(edit.ui, &pdparams, &dialog);
1756 if (rc != EOK) {
1757 printf("Error creating prompt dialog.\n");
1758 return;
1759 }
1760
1761 ui_prompt_dialog_set_cb(dialog, &go_to_line_dialog_cb, &edit);
1762}
1763
1764/* Search operations */
1765static errno_t search_spt_producer(void *data, char32_t *ret)
1766{
1767 assert(data != NULL);
1768 assert(ret != NULL);
1769 spt_t *spt = data;
1770 *ret = spt_next_char(*spt, spt);
1771 return EOK;
1772}
1773
1774static errno_t search_spt_reverse_producer(void *data, char32_t *ret)
1775{
1776 assert(data != NULL);
1777 assert(ret != NULL);
1778 spt_t *spt = data;
1779 *ret = spt_prev_char(*spt, spt);
1780 return EOK;
1781}
1782
1783static errno_t search_spt_mark(void *data, void **mark)
1784{
1785 assert(data != NULL);
1786 assert(mark != NULL);
1787 spt_t *spt = data;
1788 spt_t *new = calloc(1, sizeof(spt_t));
1789 *mark = new;
1790 if (new == NULL)
1791 return ENOMEM;
1792 *new = *spt;
1793 return EOK;
1794}
1795
1796static void search_spt_mark_free(void *data)
1797{
1798 free(data);
1799}
1800
1801static search_ops_t search_spt_ops = {
1802 .equals = char_exact_equals,
1803 .producer = search_spt_producer,
1804 .mark = search_spt_mark,
1805 .mark_free = search_spt_mark_free,
1806};
1807
1808static search_ops_t search_spt_reverse_ops = {
1809 .equals = char_exact_equals,
1810 .producer = search_spt_reverse_producer,
1811 .mark = search_spt_mark,
1812 .mark_free = search_spt_mark_free,
1813};
1814
1815/** Ask for line and go to it. */
1816static void search_prompt(bool reverse)
1817{
1818 ui_prompt_dialog_params_t pdparams;
1819 ui_prompt_dialog_t *dialog;
1820 errno_t rc;
1821
1822 ui_prompt_dialog_params_init(&pdparams);
1823 pdparams.caption = reverse ? "Reverse Search" : "Search";
1824 pdparams.prompt = "Search text";
1825 pdparams.itext = "";
1826
1827 if (pane.previous_search)
1828 pdparams.itext = pane.previous_search;
1829
1830 rc = ui_prompt_dialog_create(edit.ui, &pdparams, &dialog);
1831 if (rc != EOK) {
1832 printf("Error creating prompt dialog.\n");
1833 return;
1834 }
1835
1836 ui_prompt_dialog_set_cb(dialog, &search_dialog_cb, &edit);
1837 pane.search_reverse = reverse;
1838}
1839
1840static void search_repeat(void)
1841{
1842 if (pane.previous_search == NULL) {
1843 status_display("No previous search to repeat.");
1844 return;
1845 }
1846
1847 search(pane.previous_search, pane.previous_search_reverse);
1848}
1849
1850static void search(char *pattern, bool reverse)
1851{
1852 status_display("Searching...");
1853
1854 spt_t sp, producer_pos;
1855 tag_get_pt(&pane.caret_pos, &sp);
1856
1857 /* Start searching on the position before/after caret */
1858 if (!reverse) {
1859 spt_next_char(sp, &sp);
1860 } else {
1861 spt_prev_char(sp, &sp);
1862 }
1863 producer_pos = sp;
1864
1865 search_ops_t ops = search_spt_ops;
1866 if (reverse)
1867 ops = search_spt_reverse_ops;
1868
1869 search_t *search = search_init(pattern, &producer_pos, ops, reverse);
1870 if (search == NULL) {
1871 status_display("Failed initializing search.");
1872 return;
1873 }
1874
1875 match_t match;
1876 errno_t rc = search_next_match(search, &match);
1877 if (rc != EOK) {
1878 status_display("Failed searching.");
1879 search_fini(search);
1880 }
1881
1882 if (match.end) {
1883 status_display("Match found.");
1884 assert(match.end != NULL);
1885 spt_t *end = match.end;
1886 caret_move(*end, false, true);
1887 while (match.length > 0) {
1888 match.length--;
1889 if (reverse) {
1890 spt_next_char(*end, end);
1891 } else {
1892 spt_prev_char(*end, end);
1893 }
1894 }
1895 caret_move(*end, true, true);
1896 free(end);
1897 } else {
1898 status_display("Not found.");
1899 }
1900
1901 search_fini(search);
1902}
1903
1904/** Check for non-empty selection. */
1905static bool selection_active(void)
1906{
1907 return (tag_cmp(&pane.caret_pos, &pane.sel_start) != 0);
1908}
1909
1910static void selection_get_points(spt_t *pa, spt_t *pb)
1911{
1912 spt_t pt;
1913
1914 tag_get_pt(&pane.sel_start, pa);
1915 tag_get_pt(&pane.caret_pos, pb);
1916
1917 if (spt_cmp(pa, pb) > 0) {
1918 pt = *pa;
1919 *pa = *pb;
1920 *pb = pt;
1921 }
1922}
1923
1924/** Delete selected text. */
1925static void selection_delete(void)
1926{
1927 spt_t pa, pb;
1928 coord_t ca, cb;
1929 int rel;
1930
1931 tag_get_pt(&pane.sel_start, &pa);
1932 tag_get_pt(&pane.caret_pos, &pb);
1933 spt_get_coord(&pa, &ca);
1934 spt_get_coord(&pb, &cb);
1935 rel = coord_cmp(&ca, &cb);
1936
1937 if (rel == 0)
1938 return;
1939
1940 if (rel < 0)
1941 sheet_delete(doc.sh, &pa, &pb);
1942 else
1943 sheet_delete(doc.sh, &pb, &pa);
1944
1945 if (ca.row == cb.row)
1946 pane.rflags |= REDRAW_ROW;
1947 else
1948 pane.rflags |= REDRAW_TEXT;
1949}
1950
1951/** Select all text in the editor */
1952static void selection_sel_all(void)
1953{
1954 spt_t spt, ept;
1955
1956 pt_get_sof(&spt);
1957 pt_get_eof(&ept);
1958
1959 selection_sel_range(spt, ept);
1960}
1961
1962/** Select select all text in a given range with the given direction */
1963static void selection_sel_range(spt_t pa, spt_t pb)
1964{
1965 sheet_remove_tag(doc.sh, &pane.sel_start);
1966 sheet_place_tag(doc.sh, &pa, &pane.sel_start);
1967 sheet_remove_tag(doc.sh, &pane.caret_pos);
1968 sheet_place_tag(doc.sh, &pb, &pane.caret_pos);
1969
1970 pane.rflags |= REDRAW_TEXT;
1971 caret_update();
1972}
1973
1974static void selection_copy(void)
1975{
1976 spt_t pa, pb;
1977 char *str;
1978
1979 selection_get_points(&pa, &pb);
1980 str = range_get_str(&pa, &pb);
1981 if (str == NULL || clipboard_put_str(str) != EOK) {
1982 status_display("Copying to clipboard failed!");
1983 }
1984 free(str);
1985}
1986
1987static void edit_paste(void)
1988{
1989 selection_delete();
1990 insert_clipboard_data();
1991 pane.rflags |= (REDRAW_TEXT | REDRAW_CARET);
1992 pane_update(&pane);
1993}
1994
1995static void edit_cut(void)
1996{
1997 selection_copy();
1998 selection_delete();
1999 pane.rflags |= (REDRAW_TEXT | REDRAW_CARET);
2000 pane_update(&pane);
2001}
2002
2003static void insert_clipboard_data(void)
2004{
2005 char *str;
2006 size_t off;
2007 char32_t c;
2008 errno_t rc;
2009
2010 rc = clipboard_get_str(&str);
2011 if (rc != EOK || str == NULL)
2012 return;
2013
2014 off = 0;
2015
2016 while (true) {
2017 c = str_decode(str, &off, STR_NO_LIMIT);
2018 if (c == '\0')
2019 break;
2020
2021 insert_char(c);
2022 }
2023
2024 free(str);
2025}
2026
2027/** Get start-of-file s-point. */
2028static void pt_get_sof(spt_t *pt)
2029{
2030 coord_t coord;
2031
2032 coord.row = coord.column = 1;
2033 sheet_get_cell_pt(doc.sh, &coord, dir_before, pt);
2034}
2035
2036/** Get end-of-file s-point. */
2037static void pt_get_eof(spt_t *pt)
2038{
2039 coord_t coord;
2040 int num_rows;
2041
2042 sheet_get_num_rows(doc.sh, &num_rows);
2043 coord.row = num_rows + 1;
2044 coord.column = 1;
2045
2046 sheet_get_cell_pt(doc.sh, &coord, dir_after, pt);
2047}
2048
2049/** Get start-of-line s-point for given s-point cpt */
2050static void pt_get_sol(spt_t *cpt, spt_t *spt)
2051{
2052 coord_t coord;
2053
2054 spt_get_coord(cpt, &coord);
2055 coord.column = 1;
2056
2057 sheet_get_cell_pt(doc.sh, &coord, dir_before, spt);
2058}
2059
2060/** Get end-of-line s-point for given s-point cpt */
2061static void pt_get_eol(spt_t *cpt, spt_t *ept)
2062{
2063 coord_t coord;
2064 int row_width;
2065
2066 spt_get_coord(cpt, &coord);
2067 sheet_get_row_width(doc.sh, coord.row, &row_width);
2068 coord.column = row_width - 1;
2069
2070 sheet_get_cell_pt(doc.sh, &coord, dir_after, ept);
2071}
2072
2073/** Check whether the spt is at a beginning of a word */
2074static bool pt_is_word_beginning(spt_t *pt)
2075{
2076 spt_t lp, sfp, efp, slp, elp;
2077 coord_t coord;
2078
2079 pt_get_sof(&sfp);
2080 pt_get_eof(&efp);
2081 pt_get_sol(pt, &slp);
2082 pt_get_eol(pt, &elp);
2083
2084 /* the spt is at the beginning or end of the file or line */
2085 if ((spt_cmp(&sfp, pt) == 0) || (spt_cmp(&efp, pt) == 0) ||
2086 (spt_cmp(&slp, pt) == 0) || (spt_cmp(&elp, pt) == 0))
2087 return true;
2088
2089 /* the spt is a delimiter */
2090 if (pt_is_delimiter(pt))
2091 return false;
2092
2093 spt_get_coord(pt, &coord);
2094
2095 coord.column -= 1;
2096 sheet_get_cell_pt(doc.sh, &coord, dir_before, &lp);
2097
2098 return pt_is_delimiter(&lp) ||
2099 (pt_is_punctuation(pt) && !pt_is_punctuation(&lp)) ||
2100 (pt_is_punctuation(&lp) && !pt_is_punctuation(pt));
2101}
2102
2103static char32_t get_first_wchar(const char *str)
2104{
2105 size_t offset = 0;
2106 return str_decode(str, &offset, str_size(str));
2107}
2108
2109static bool pt_is_delimiter(spt_t *pt)
2110{
2111 spt_t rp;
2112 coord_t coord;
2113 char *ch = NULL;
2114
2115 spt_get_coord(pt, &coord);
2116
2117 coord.column += 1;
2118 sheet_get_cell_pt(doc.sh, &coord, dir_after, &rp);
2119
2120 ch = range_get_str(pt, &rp);
2121 if (ch == NULL)
2122 return false;
2123
2124 char32_t first_char = get_first_wchar(ch);
2125 switch (first_char) {
2126 case ' ':
2127 case '\t':
2128 case '\n':
2129 return true;
2130 default:
2131 return false;
2132 }
2133}
2134
2135static bool pt_is_punctuation(spt_t *pt)
2136{
2137 spt_t rp;
2138 coord_t coord;
2139 char *ch = NULL;
2140
2141 spt_get_coord(pt, &coord);
2142
2143 coord.column += 1;
2144 sheet_get_cell_pt(doc.sh, &coord, dir_after, &rp);
2145
2146 ch = range_get_str(pt, &rp);
2147 if (ch == NULL)
2148 return false;
2149
2150 char32_t first_char = get_first_wchar(ch);
2151 switch (first_char) {
2152 case ',':
2153 case '.':
2154 case ';':
2155 case ':':
2156 case '/':
2157 case '?':
2158 case '\\':
2159 case '|':
2160 case '_':
2161 case '+':
2162 case '-':
2163 case '*':
2164 case '=':
2165 case '<':
2166 case '>':
2167 return true;
2168 default:
2169 return false;
2170 }
2171}
2172
2173/** Compare tags. */
2174static int tag_cmp(tag_t const *a, tag_t const *b)
2175{
2176 spt_t pa, pb;
2177
2178 tag_get_pt(a, &pa);
2179 tag_get_pt(b, &pb);
2180
2181 return spt_cmp(&pa, &pb);
2182}
2183
2184/** Compare s-points. */
2185static int spt_cmp(spt_t const *a, spt_t const *b)
2186{
2187 coord_t ca, cb;
2188
2189 spt_get_coord(a, &ca);
2190 spt_get_coord(b, &cb);
2191
2192 return coord_cmp(&ca, &cb);
2193}
2194
2195/** Compare coordinats. */
2196static int coord_cmp(coord_t const *a, coord_t const *b)
2197{
2198 if (a->row - b->row != 0)
2199 return a->row - b->row;
2200
2201 return a->column - b->column;
2202}
2203
2204/** Display text in the status line. */
2205static void status_display(char const *str)
2206{
2207 (void) ui_label_set_text(edit.status, str);
2208 (void) ui_label_paint(edit.status);
2209}
2210
2211/** Window close request
2212 *
2213 * @param window Window
2214 * @param arg Argument (edit_t *)
2215 */
2216static void edit_wnd_close(ui_window_t *window, void *arg)
2217{
2218 edit_t *edit = (edit_t *) arg;
2219
2220 ui_quit(edit->ui);
2221}
2222
2223/** Window keyboard event
2224 *
2225 * @param window Window
2226 * @param arg Argument (edit_t *)
2227 * @param event Keyboard event
2228 */
2229static void edit_wnd_kbd_event(ui_window_t *window, void *arg,
2230 kbd_event_t *event)
2231{
2232 pane.keymod = event->mods;
2233
2234 if (ui_window_def_kbd(window, event) == ui_claimed)
2235 return;
2236
2237 if (event->type == KEY_PRESS) {
2238 key_handle_press(event);
2239 (void) pane_update(&pane);
2240 (void) gfx_update(ui_window_get_gc(window));
2241 }
2242}
2243
2244/** File / Save menu entry selected.
2245 *
2246 * @param mentry Menu entry
2247 * @param arg Argument (edit_t *)
2248 */
2249static void edit_file_save(ui_menu_entry_t *mentry, void *arg)
2250{
2251 edit_t *edit = (edit_t *) arg;
2252
2253 (void)edit;
2254
2255 if (doc.file_name != NULL)
2256 file_save(doc.file_name);
2257 else
2258 file_save_as();
2259}
2260
2261/** File / Save As menu entry selected.
2262 *
2263 * @param mentry Menu entry
2264 * @param arg Argument (edit_t *)
2265 */
2266static void edit_file_save_as(ui_menu_entry_t *mentry, void *arg)
2267{
2268 edit_t *edit = (edit_t *) arg;
2269
2270 (void)edit;
2271 file_save_as();
2272}
2273
2274/** File / Exit menu entry selected.
2275 *
2276 * @param mentry Menu entry
2277 * @param arg Argument (edit_t *)
2278 */
2279static void edit_file_exit(ui_menu_entry_t *mentry, void *arg)
2280{
2281 edit_t *edit = (edit_t *) arg;
2282
2283 ui_quit(edit->ui);
2284}
2285
2286/** Edit / Cut menu entry selected.
2287 *
2288 * @param mentry Menu entry
2289 * @param arg Argument (edit_t *)
2290 */
2291static void edit_edit_cut(ui_menu_entry_t *mentry, void *arg)
2292{
2293 (void) arg;
2294 edit_cut();
2295 (void) gfx_update(ui_window_get_gc(edit.window));
2296}
2297
2298/** Edit / Copy menu entry selected.
2299 *
2300 * @param mentry Menu entry
2301 * @param arg Argument (edit_t *)
2302 */
2303static void edit_edit_copy(ui_menu_entry_t *mentry, void *arg)
2304{
2305 (void) arg;
2306 selection_copy();
2307}
2308
2309/** Edit / Paste menu entry selected.
2310 *
2311 * @param mentry Menu entry
2312 * @param arg Argument (edit_t *)
2313 */
2314static void edit_edit_paste(ui_menu_entry_t *mentry, void *arg)
2315{
2316 (void) arg;
2317 edit_paste();
2318 (void) gfx_update(ui_window_get_gc(edit.window));
2319}
2320
2321/** Edit / Delete menu entry selected.
2322 *
2323 * @param mentry Menu entry
2324 * @param arg Argument (edit_t *)
2325 */
2326static void edit_edit_delete(ui_menu_entry_t *mentry, void *arg)
2327{
2328 (void) arg;
2329
2330 if (selection_active())
2331 selection_delete();
2332
2333 pane.rflags |= REDRAW_CARET;
2334 (void) pane_update(&pane);
2335 (void) gfx_update(ui_window_get_gc(edit.window));
2336}
2337
2338/** Edit / Select All menu entry selected.
2339 *
2340 * @param mentry Menu entry
2341 * @param arg Argument (edit_t *)
2342 */
2343static void edit_edit_select_all(ui_menu_entry_t *mentry, void *arg)
2344{
2345 (void) arg;
2346
2347 selection_sel_all();
2348 pane.rflags |= (REDRAW_CARET | REDRAW_TEXT | REDRAW_STATUS);
2349 pane_update(&pane);
2350 (void) gfx_update(ui_window_get_gc(edit.window));
2351}
2352
2353/** Search / Find menu entry selected.
2354 *
2355 * @param mentry Menu entry
2356 * @param arg Argument (edit_t *)
2357 */
2358static void edit_search_find(ui_menu_entry_t *mentry, void *arg)
2359{
2360 (void) arg;
2361 search_prompt(false);
2362}
2363
2364/** Search / Reverse Find menu entry selected.
2365 *
2366 * @param mentry Menu entry
2367 * @param arg Argument (edit_t *)
2368 */
2369static void edit_search_reverse_find(ui_menu_entry_t *mentry, void *arg)
2370{
2371 (void) arg;
2372 search_prompt(true);
2373}
2374
2375/** Search / Find Next menu entry selected.
2376 *
2377 * @param mentry Menu entry
2378 * @param arg Argument (edit_t *)
2379 */
2380static void edit_search_find_next(ui_menu_entry_t *mentry, void *arg)
2381{
2382 (void) arg;
2383 search_repeat();
2384 (void) pane_update(&pane);
2385 (void) gfx_update(ui_window_get_gc(edit.window));
2386}
2387
2388/** Search / Go To Line menu entry selected.
2389 *
2390 * @param mentry Menu entry
2391 * @param arg Argument (edit_t *)
2392 */
2393static void edit_search_go_to_line(ui_menu_entry_t *mentry, void *arg)
2394{
2395 (void) arg;
2396 caret_go_to_line_ask();
2397}
2398
2399/** Save As dialog OK button press.
2400 *
2401 * @param dialog Save As dialog
2402 * @param arg Argument (ui_demo_t *)
2403 * @param fname File name
2404 */
2405static void save_as_dialog_bok(ui_file_dialog_t *dialog, void *arg,
2406 const char *fname)
2407{
2408 edit_t *edit = (edit_t *)arg;
2409 gfx_context_t *gc = ui_window_get_gc(edit->window);
2410 char *cname;
2411 errno_t rc;
2412
2413 ui_file_dialog_destroy(dialog);
2414 // TODO Smarter cursor management
2415 pane.rflags |= REDRAW_CARET;
2416 (void) pane_update(&pane);
2417 gfx_cursor_set_visible(gc, true);
2418
2419 cname = str_dup(fname);
2420 if (cname == NULL) {
2421 printf("Out of memory.\n");
2422 return;
2423 }
2424
2425 rc = file_save(fname);
2426 if (rc != EOK)
2427 return;
2428
2429 if (doc.file_name != NULL)
2430 free(doc.file_name);
2431 doc.file_name = cname;
2432
2433}
2434
2435/** Save As dialog cancel button press.
2436 *
2437 * @param dialog File dialog
2438 * @param arg Argument (ui_demo_t *)
2439 */
2440static void save_as_dialog_bcancel(ui_file_dialog_t *dialog, void *arg)
2441{
2442 edit_t *edit = (edit_t *)arg;
2443 gfx_context_t *gc = ui_window_get_gc(edit->window);
2444
2445 ui_file_dialog_destroy(dialog);
2446 // TODO Smarter cursor management
2447 pane.rflags |= REDRAW_CARET;
2448 (void) pane_update(&pane);
2449 gfx_cursor_set_visible(gc, true);
2450}
2451
2452/** Save As dialog close request.
2453 *
2454 * @param dialog File dialog
2455 * @param arg Argument (ui_demo_t *)
2456 */
2457static void save_as_dialog_close(ui_file_dialog_t *dialog, void *arg)
2458{
2459 edit_t *edit = (edit_t *)arg;
2460 gfx_context_t *gc = ui_window_get_gc(edit->window);
2461
2462 ui_file_dialog_destroy(dialog);
2463 // TODO Smarter cursor management
2464 pane.rflags |= REDRAW_CARET;
2465 (void) pane_update(&pane);
2466 gfx_cursor_set_visible(gc, true);
2467}
2468
2469/** Go To Line dialog OK button press.
2470 *
2471 * @param dialog Go To Line dialog
2472 * @param arg Argument (ui_demo_t *)
2473 * @param text Submitted text
2474 */
2475static void go_to_line_dialog_bok(ui_prompt_dialog_t *dialog, void *arg,
2476 const char *text)
2477{
2478 edit_t *edit = (edit_t *) arg;
2479 gfx_context_t *gc = ui_window_get_gc(edit->window);
2480 char *endptr;
2481 int line;
2482
2483 ui_prompt_dialog_destroy(dialog);
2484 line = strtol(text, &endptr, 10);
2485 if (*endptr != '\0') {
2486 status_display("Invalid number entered.");
2487 return;
2488 }
2489
2490 caret_move_absolute(line, pane.ideal_column, dir_before, false);
2491 // TODO Smarter cursor management
2492 (void) pane_update(&pane);
2493 gfx_cursor_set_visible(gc, true);
2494 (void) gfx_update(gc);
2495}
2496
2497/** Go To Line dialog cancel button press.
2498 *
2499 * @param dialog File dialog
2500 * @param arg Argument (ui_demo_t *)
2501 */
2502static void go_to_line_dialog_bcancel(ui_prompt_dialog_t *dialog, void *arg)
2503{
2504 edit_t *edit = (edit_t *) arg;
2505 gfx_context_t *gc = ui_window_get_gc(edit->window);
2506
2507 ui_prompt_dialog_destroy(dialog);
2508 // TODO Smarter cursor management
2509 pane.rflags |= REDRAW_CARET;
2510 (void) pane_update(&pane);
2511 gfx_cursor_set_visible(gc, true);
2512}
2513
2514/** Go To Line dialog close request.
2515 *
2516 * @param dialog File dialog
2517 * @param arg Argument (ui_demo_t *)
2518 */
2519static void go_to_line_dialog_close(ui_prompt_dialog_t *dialog, void *arg)
2520{
2521 edit_t *edit = (edit_t *) arg;
2522 gfx_context_t *gc = ui_window_get_gc(edit->window);
2523
2524 ui_prompt_dialog_destroy(dialog);
2525 // TODO Smarter cursor management
2526 pane.rflags |= REDRAW_CARET;
2527 (void) pane_update(&pane);
2528 gfx_cursor_set_visible(gc, true);
2529}
2530
2531/** Search dialog OK button press.
2532 *
2533 * @param dialog Search dialog
2534 * @param arg Argument (ui_demo_t *)
2535 * @param text Submitted text
2536 */
2537static void search_dialog_bok(ui_prompt_dialog_t *dialog, void *arg,
2538 const char *text)
2539{
2540 edit_t *edit = (edit_t *) arg;
2541 gfx_context_t *gc = ui_window_get_gc(edit->window);
2542 char *pattern;
2543 bool reverse;
2544
2545 ui_prompt_dialog_destroy(dialog);
2546
2547 /* Abort if search phrase is empty */
2548 if (text[0] == '\0')
2549 return;
2550
2551 pattern = str_dup(text);
2552 reverse = pane.search_reverse;
2553
2554 if (pane.previous_search)
2555 free(pane.previous_search);
2556 pane.previous_search = pattern;
2557 pane.previous_search_reverse = reverse;
2558
2559 search(pattern, reverse);
2560
2561 // TODO Smarter cursor management
2562 (void) pane_update(&pane);
2563 gfx_cursor_set_visible(gc, true);
2564 (void) gfx_update(gc);
2565}
2566
2567/** Search dialog cancel button press.
2568 *
2569 * @param dialog File dialog
2570 * @param arg Argument (ui_demo_t *)
2571 */
2572static void search_dialog_bcancel(ui_prompt_dialog_t *dialog, void *arg)
2573{
2574 edit_t *edit = (edit_t *) arg;
2575 gfx_context_t *gc = ui_window_get_gc(edit->window);
2576
2577 ui_prompt_dialog_destroy(dialog);
2578 // TODO Smarter cursor management
2579 pane.rflags |= REDRAW_CARET;
2580 (void) pane_update(&pane);
2581 gfx_cursor_set_visible(gc, true);
2582}
2583
2584/** Search dialog close request.
2585 *
2586 * @param dialog File dialog
2587 * @param arg Argument (ui_demo_t *)
2588 */
2589static void search_dialog_close(ui_prompt_dialog_t *dialog, void *arg)
2590{
2591 edit_t *edit = (edit_t *) arg;
2592 gfx_context_t *gc = ui_window_get_gc(edit->window);
2593
2594 ui_prompt_dialog_destroy(dialog);
2595 // TODO Smarter cursor management
2596 pane.rflags |= REDRAW_CARET;
2597 (void) pane_update(&pane);
2598 gfx_cursor_set_visible(gc, true);
2599}
2600
2601/** @}
2602 */
Note: See TracBrowser for help on using the repository browser.