source: mainline/uspace/app/edit/edit.c@ 36e9cd1

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 36e9cd1 was 36e9cd1, checked in by Martin Decky <martin@…>, 15 years ago

silence compiler warnings (no change in actual functionality)

  • Property mode set to 100644
File size: 24.3 KB
Line 
1/*
2 * Copyright (c) 2009 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup edit
30 * @brief Text editor.
31 * @{
32 */
33/**
34 * @file
35 */
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <sys/types.h>
40#include <vfs/vfs.h>
41#include <io/console.h>
42#include <io/color.h>
43#include <io/keycode.h>
44#include <errno.h>
45#include <align.h>
46#include <macros.h>
47#include <clipboard.h>
48#include <bool.h>
49
50#include "sheet.h"
51
52enum redraw_flags {
53 REDRAW_TEXT = (1 << 0),
54 REDRAW_ROW = (1 << 1),
55 REDRAW_STATUS = (1 << 2),
56 REDRAW_CARET = (1 << 3)
57};
58
59/** Pane
60 *
61 * A rectangular area of the screen used to edit a document. Different
62 * panes can be possibly used to edit the same document.
63 */
64typedef struct {
65 /* Pane dimensions */
66 int rows, columns;
67
68 /* Position of the visible area */
69 int sh_row, sh_column;
70
71 /** Bitmask of components that need redrawing */
72 enum redraw_flags rflags;
73
74 /** Current position of the caret */
75 tag_t caret_pos;
76
77 /** Start of selection */
78 tag_t sel_start;
79
80 /**
81 * Ideal column where the caret should try to get. This is used
82 * for maintaining the same column during vertical movement.
83 */
84 int ideal_column;
85} pane_t;
86
87/** Document
88 *
89 * Associates a sheet with a file where it can be saved to.
90 */
91typedef struct {
92 char *file_name;
93 sheet_t sh;
94} doc_t;
95
96static int con;
97static doc_t doc;
98static bool done;
99static pane_t pane;
100static bool cursor_visible;
101
102static int scr_rows, scr_columns;
103
104#define ROW_BUF_SIZE 4096
105#define BUF_SIZE 64
106#define TAB_WIDTH 8
107#define ED_INFTY 65536
108
109/** Maximum filename length that can be entered. */
110#define INFNAME_MAX_LEN 128
111
112static void cursor_show(void);
113static void cursor_hide(void);
114static void cursor_setvis(bool visible);
115
116static void key_handle_unmod(console_event_t const *ev);
117static void key_handle_ctrl(console_event_t const *ev);
118static void key_handle_shift(console_event_t const *ev);
119static void key_handle_movement(unsigned int key, bool shift);
120
121static int file_save(char const *fname);
122static void file_save_as(void);
123static int file_insert(char *fname);
124static int file_save_range(char const *fname, spt_t const *spos,
125 spt_t const *epos);
126static char *filename_prompt(char const *prompt, char const *init_value);
127static char *range_get_str(spt_t const *spos, spt_t const *epos);
128
129static void pane_text_display(void);
130static void pane_row_display(void);
131static void pane_row_range_display(int r0, int r1);
132static void pane_status_display(void);
133static void pane_caret_display(void);
134
135static void insert_char(wchar_t c);
136static void delete_char_before(void);
137static void delete_char_after(void);
138static void caret_update(void);
139static void caret_move(int drow, int dcolumn, enum dir_spec align_dir);
140
141static bool selection_active(void);
142static void selection_sel_all(void);
143static void selection_get_points(spt_t *pa, spt_t *pb);
144static void selection_delete(void);
145static void selection_copy(void);
146static void insert_clipboard_data(void);
147
148static void pt_get_sof(spt_t *pt);
149static void pt_get_eof(spt_t *pt);
150static int tag_cmp(tag_t const *a, tag_t const *b);
151static int spt_cmp(spt_t const *a, spt_t const *b);
152static int coord_cmp(coord_t const *a, coord_t const *b);
153
154static void status_display(char const *str);
155
156
157int main(int argc, char *argv[])
158{
159 console_event_t ev;
160 coord_t coord;
161 bool new_file;
162
163 spt_t pt;
164
165 con = fphone(stdout);
166 console_clear(con);
167
168 console_get_size(con, &scr_columns, &scr_rows);
169
170 pane.rows = scr_rows - 1;
171 pane.columns = scr_columns;
172 pane.sh_row = 1;
173 pane.sh_column = 1;
174
175 /* Start with an empty sheet. */
176 sheet_init(&doc.sh);
177
178 /* Place caret at the beginning of file. */
179 coord.row = coord.column = 1;
180 sheet_get_cell_pt(&doc.sh, &coord, dir_before, &pt);
181 sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
182 pane.ideal_column = coord.column;
183
184 if (argc == 2) {
185 doc.file_name = str_dup(argv[1]);
186 } else if (argc > 1) {
187 printf("Invalid arguments.\n");
188 return -2;
189 } else {
190 doc.file_name = NULL;
191 }
192
193 new_file = false;
194
195 if (doc.file_name == NULL || file_insert(doc.file_name) != EOK)
196 new_file = true;
197
198 /* Move to beginning of file. */
199 caret_move(-ED_INFTY, -ED_INFTY, dir_before);
200
201 /* Place selection start tag. */
202 tag_get_pt(&pane.caret_pos, &pt);
203 sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
204
205 /* Initial display */
206 cursor_visible = true;
207
208 cursor_hide();
209 console_clear(con);
210 pane_text_display();
211 pane_status_display();
212 if (new_file && doc.file_name != NULL)
213 status_display("File not found. Starting empty file.");
214 pane_caret_display();
215 cursor_show();
216
217 done = false;
218
219 while (!done) {
220 console_get_event(con, &ev);
221 pane.rflags = 0;
222
223 if (ev.type == KEY_PRESS) {
224 /* Handle key press. */
225 if (((ev.mods & KM_ALT) == 0) &&
226 ((ev.mods & KM_SHIFT) == 0) &&
227 (ev.mods & KM_CTRL) != 0) {
228 key_handle_ctrl(&ev);
229 } else if (((ev.mods & KM_ALT) == 0) &&
230 ((ev.mods & KM_CTRL) == 0) &&
231 (ev.mods & KM_SHIFT) != 0) {
232 key_handle_shift(&ev);
233 } else if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
234 key_handle_unmod(&ev);
235 }
236 }
237
238 /* Redraw as necessary. */
239
240 cursor_hide();
241
242 if (pane.rflags & REDRAW_TEXT)
243 pane_text_display();
244 if (pane.rflags & REDRAW_ROW)
245 pane_row_display();
246 if (pane.rflags & REDRAW_STATUS)
247 pane_status_display();
248 if (pane.rflags & REDRAW_CARET)
249 pane_caret_display();
250
251 cursor_show();
252 }
253
254 console_clear(con);
255
256 return 0;
257}
258
259static void cursor_show(void)
260{
261 cursor_setvis(true);
262}
263
264static void cursor_hide(void)
265{
266 cursor_setvis(false);
267}
268
269static void cursor_setvis(bool visible)
270{
271 if (cursor_visible != visible) {
272 console_cursor_visibility(con, visible);
273 cursor_visible = visible;
274 }
275}
276
277/** Handle key without modifier. */
278static void key_handle_unmod(console_event_t const *ev)
279{
280 switch (ev->key) {
281 case KC_ENTER:
282 selection_delete();
283 insert_char('\n');
284 caret_update();
285 break;
286 case KC_LEFT:
287 case KC_RIGHT:
288 case KC_UP:
289 case KC_DOWN:
290 case KC_HOME:
291 case KC_END:
292 case KC_PAGE_UP:
293 case KC_PAGE_DOWN:
294 key_handle_movement(ev->key, false);
295 break;
296 case KC_BACKSPACE:
297 if (selection_active())
298 selection_delete();
299 else
300 delete_char_before();
301 caret_update();
302 break;
303 case KC_DELETE:
304 if (selection_active())
305 selection_delete();
306 else
307 delete_char_after();
308 caret_update();
309 break;
310 default:
311 if (ev->c >= 32 || ev->c == '\t') {
312 selection_delete();
313 insert_char(ev->c);
314 caret_update();
315 }
316 break;
317 }
318}
319
320/** Handle Shift-key combination. */
321static void key_handle_shift(console_event_t const *ev)
322{
323 switch (ev->key) {
324 case KC_LEFT:
325 case KC_RIGHT:
326 case KC_UP:
327 case KC_DOWN:
328 case KC_HOME:
329 case KC_END:
330 case KC_PAGE_UP:
331 case KC_PAGE_DOWN:
332 key_handle_movement(ev->key, true);
333 break;
334 default:
335 if (ev->c >= 32 || ev->c == '\t') {
336 selection_delete();
337 insert_char(ev->c);
338 caret_update();
339 }
340 break;
341 }
342}
343
344/** Handle Ctrl-key combination. */
345static void key_handle_ctrl(console_event_t const *ev)
346{
347 switch (ev->key) {
348 case KC_Q:
349 done = true;
350 break;
351 case KC_S:
352 if (doc.file_name != NULL)
353 file_save(doc.file_name);
354 else
355 file_save_as();
356 break;
357 case KC_E:
358 file_save_as();
359 break;
360 case KC_C:
361 selection_copy();
362 break;
363 case KC_V:
364 selection_delete();
365 insert_clipboard_data();
366 pane.rflags |= REDRAW_TEXT;
367 caret_update();
368 break;
369 case KC_X:
370 selection_copy();
371 selection_delete();
372 pane.rflags |= REDRAW_TEXT;
373 caret_update();
374 break;
375 case KC_A:
376 selection_sel_all();
377 break;
378 default:
379 break;
380 }
381}
382
383static void key_handle_movement(unsigned int key, bool select)
384{
385 spt_t pt;
386 spt_t caret_pt;
387 coord_t c_old, c_new;
388 bool had_sel;
389
390 /* Check if we had selection before. */
391 tag_get_pt(&pane.caret_pos, &caret_pt);
392 tag_get_pt(&pane.sel_start, &pt);
393 had_sel = !spt_equal(&caret_pt, &pt);
394
395 switch (key) {
396 case KC_LEFT:
397 caret_move(0, -1, dir_before);
398 break;
399 case KC_RIGHT:
400 caret_move(0, 0, dir_after);
401 break;
402 case KC_UP:
403 caret_move(-1, 0, dir_before);
404 break;
405 case KC_DOWN:
406 caret_move(+1, 0, dir_before);
407 break;
408 case KC_HOME:
409 caret_move(0, -ED_INFTY, dir_before);
410 break;
411 case KC_END:
412 caret_move(0, +ED_INFTY, dir_before);
413 break;
414 case KC_PAGE_UP:
415 caret_move(-pane.rows, 0, dir_before);
416 break;
417 case KC_PAGE_DOWN:
418 caret_move(+pane.rows, 0, dir_before);
419 break;
420 default:
421 break;
422 }
423
424 if (select == false) {
425 /* Move sel_start to the same point as caret. */
426 sheet_remove_tag(&doc.sh, &pane.sel_start);
427 tag_get_pt(&pane.caret_pos, &pt);
428 sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
429 }
430
431 if (select) {
432 tag_get_pt(&pane.caret_pos, &pt);
433 spt_get_coord(&caret_pt, &c_old);
434 spt_get_coord(&pt, &c_new);
435
436 if (c_old.row == c_new.row)
437 pane.rflags |= REDRAW_ROW;
438 else
439 pane.rflags |= REDRAW_TEXT;
440
441 } else if (had_sel == true) {
442 /* Redraw because text was unselected. */
443 pane.rflags |= REDRAW_TEXT;
444 }
445}
446
447/** Save the document. */
448static int file_save(char const *fname)
449{
450 spt_t sp, ep;
451 int rc;
452
453 status_display("Saving...");
454 pt_get_sof(&sp);
455 pt_get_eof(&ep);
456
457 rc = file_save_range(fname, &sp, &ep);
458
459 switch (rc) {
460 case EINVAL:
461 status_display("Error opening file!");
462 break;
463 case EIO:
464 status_display("Error writing data!");
465 break;
466 default:
467 status_display("File saved.");
468 break;
469 }
470
471 return rc;
472}
473
474/** Change document name and save. */
475static void file_save_as(void)
476{
477 char *old_fname, *fname;
478 int rc;
479
480 old_fname = (doc.file_name != NULL) ? doc.file_name : "";
481 fname = filename_prompt("Save As", old_fname);
482 if (fname == NULL) {
483 status_display("Save cancelled.");
484 return;
485 }
486
487 rc = file_save(fname);
488 if (rc != EOK)
489 return;
490
491 if (doc.file_name != NULL)
492 free(doc.file_name);
493 doc.file_name = fname;
494}
495
496/** Ask for a file name. */
497static char *filename_prompt(char const *prompt, char const *init_value)
498{
499 console_event_t ev;
500 char *str;
501 wchar_t buffer[INFNAME_MAX_LEN + 1];
502 int max_len;
503 int nc;
504 bool done;
505
506 asprintf(&str, "%s: %s", prompt, init_value);
507 status_display(str);
508 console_goto(con, 1 + str_length(str), scr_rows - 1);
509 free(str);
510
511 console_set_color(con, COLOR_WHITE, COLOR_BLACK, 0);
512
513 max_len = min(INFNAME_MAX_LEN, scr_columns - 4 - str_length(prompt));
514 str_to_wstr(buffer, max_len + 1, init_value);
515 nc = wstr_length(buffer);
516 done = false;
517
518 while (!done) {
519 console_get_event(con, &ev);
520
521 if (ev.type == KEY_PRESS) {
522 /* Handle key press. */
523 if (((ev.mods & KM_ALT) == 0) &&
524 (ev.mods & KM_CTRL) != 0) {
525 ;
526 } else if ((ev.mods & (KM_CTRL | KM_ALT)) == 0) {
527 switch (ev.key) {
528 case KC_ESCAPE:
529 return NULL;
530 case KC_BACKSPACE:
531 if (nc > 0) {
532 putchar('\b');
533 fflush(stdout);
534 --nc;
535 }
536 break;
537 case KC_ENTER:
538 done = true;
539 break;
540 default:
541 if (ev.c >= 32 && nc < max_len) {
542 putchar(ev.c);
543 fflush(stdout);
544 buffer[nc++] = ev.c;
545 }
546 break;
547 }
548 }
549 }
550 }
551
552 buffer[nc] = '\0';
553 str = wstr_to_astr(buffer);
554
555 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
556
557 return str;
558}
559
560/** Insert file at caret position.
561 *
562 * Reads in the contents of a file and inserts them at the current position
563 * of the caret.
564 */
565static int file_insert(char *fname)
566{
567 FILE *f;
568 wchar_t c;
569 char buf[BUF_SIZE];
570 int bcnt;
571 int n_read;
572 size_t off;
573
574 f = fopen(fname, "rt");
575 if (f == NULL)
576 return EINVAL;
577
578 bcnt = 0;
579
580 while (true) {
581 if (bcnt < STR_BOUNDS(1)) {
582 n_read = fread(buf + bcnt, 1, BUF_SIZE - bcnt, f);
583 bcnt += n_read;
584 }
585
586 off = 0;
587 c = str_decode(buf, &off, bcnt);
588 if (c == '\0')
589 break;
590
591 bcnt -= off;
592 memcpy(buf, buf + off, bcnt);
593
594 insert_char(c);
595 }
596
597 fclose(f);
598
599 return EOK;
600}
601
602/** Save a range of text into a file. */
603static int file_save_range(char const *fname, spt_t const *spos,
604 spt_t const *epos)
605{
606 FILE *f;
607 char buf[BUF_SIZE];
608 spt_t sp, bep;
609 size_t bytes, n_written;
610
611 f = fopen(fname, "wt");
612 if (f == NULL)
613 return EINVAL;
614
615 sp = *spos;
616
617 do {
618 sheet_copy_out(&doc.sh, &sp, epos, buf, BUF_SIZE, &bep);
619 bytes = str_size(buf);
620
621 n_written = fwrite(buf, 1, bytes, f);
622 if (n_written != bytes) {
623 return EIO;
624 }
625
626 sp = bep;
627 } while (!spt_equal(&bep, epos));
628
629 if (fclose(f) != EOK)
630 return EIO;
631
632 return EOK;
633}
634
635/** Return contents of range as a new string. */
636static char *range_get_str(spt_t const *spos, spt_t const *epos)
637{
638 char *buf;
639 spt_t sp, bep;
640 size_t bytes;
641 size_t buf_size, bpos;
642
643 buf_size = 1;
644
645 buf = malloc(buf_size);
646 if (buf == NULL)
647 return NULL;
648
649 bpos = 0;
650 sp = *spos;
651
652 while (true) {
653 sheet_copy_out(&doc.sh, &sp, epos, &buf[bpos], buf_size - bpos,
654 &bep);
655 bytes = str_size(&buf[bpos]);
656 bpos += bytes;
657 sp = bep;
658
659 if (spt_equal(&bep, epos))
660 break;
661
662 buf_size *= 2;
663 buf = realloc(buf, buf_size);
664 if (buf == NULL)
665 return NULL;
666 }
667
668 return buf;
669}
670
671static void pane_text_display(void)
672{
673 int sh_rows, rows;
674 int i, j;
675
676 sheet_get_num_rows(&doc.sh, &sh_rows);
677 rows = min(sh_rows - pane.sh_row + 1, pane.rows);
678
679 /* Draw rows from the sheet. */
680
681 console_goto(con, 0, 0);
682 pane_row_range_display(0, rows);
683
684 /* Clear the remaining rows if file is short. */
685
686 for (i = rows; i < pane.rows; ++i) {
687 console_goto(con, 0, i);
688 for (j = 0; j < scr_columns; ++j)
689 putchar(' ');
690 fflush(stdout);
691 }
692
693 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
694 pane.rflags &= ~REDRAW_ROW;
695}
696
697/** Display just the row where the caret is. */
698static void pane_row_display(void)
699{
700 spt_t caret_pt;
701 coord_t coord;
702 int ridx;
703
704 tag_get_pt(&pane.caret_pos, &caret_pt);
705 spt_get_coord(&caret_pt, &coord);
706
707 ridx = coord.row - pane.sh_row;
708 pane_row_range_display(ridx, ridx + 1);
709 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
710}
711
712static void pane_row_range_display(int r0, int r1)
713{
714 int i, j, fill;
715 spt_t rb, re, dep, pt;
716 coord_t rbc, rec;
717 char row_buf[ROW_BUF_SIZE];
718 wchar_t c;
719 size_t pos, size;
720 int s_column;
721 coord_t csel_start, csel_end, ctmp;
722
723 /* Determine selection start and end. */
724
725 tag_get_pt(&pane.sel_start, &pt);
726 spt_get_coord(&pt, &csel_start);
727
728 tag_get_pt(&pane.caret_pos, &pt);
729 spt_get_coord(&pt, &csel_end);
730
731 if (coord_cmp(&csel_start, &csel_end) > 0) {
732 ctmp = csel_start;
733 csel_start = csel_end;
734 csel_end = ctmp;
735 }
736
737 /* Draw rows from the sheet. */
738
739 console_goto(con, 0, 0);
740 for (i = r0; i < r1; ++i) {
741 /* Starting point for row display */
742 rbc.row = pane.sh_row + i;
743 rbc.column = pane.sh_column;
744 sheet_get_cell_pt(&doc.sh, &rbc, dir_before, &rb);
745
746 /* Ending point for row display */
747 rec.row = pane.sh_row + i;
748 rec.column = pane.sh_column + pane.columns;
749 sheet_get_cell_pt(&doc.sh, &rec, dir_before, &re);
750
751 /* Copy the text of the row to the buffer. */
752 sheet_copy_out(&doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
753
754 /* Display text from the buffer. */
755
756 if (coord_cmp(&csel_start, &rbc) <= 0 &&
757 coord_cmp(&rbc, &csel_end) < 0) {
758 fflush(stdout);
759 console_set_color(con, COLOR_BLACK, COLOR_RED, 0);
760 fflush(stdout);
761 }
762
763 console_goto(con, 0, i);
764 size = str_size(row_buf);
765 pos = 0;
766 s_column = pane.sh_column;
767 while (pos < size) {
768 if ((csel_start.row == rbc.row) && (csel_start.column == s_column)) {
769 fflush(stdout);
770 console_set_color(con, COLOR_BLACK, COLOR_RED, 0);
771 fflush(stdout);
772 }
773
774 if ((csel_end.row == rbc.row) && (csel_end.column == s_column)) {
775 fflush(stdout);
776 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
777 fflush(stdout);
778 }
779
780 c = str_decode(row_buf, &pos, size);
781 if (c != '\t') {
782 printf("%lc", c);
783 s_column += 1;
784 } else {
785 fill = 1 + ALIGN_UP(s_column, TAB_WIDTH)
786 - s_column;
787
788 for (j = 0; j < fill; ++j)
789 putchar(' ');
790 s_column += fill;
791 }
792 }
793
794 if ((csel_end.row == rbc.row) && (csel_end.column == s_column)) {
795 fflush(stdout);
796 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
797 fflush(stdout);
798 }
799
800 /* Fill until the end of display area. */
801
802 if (str_length(row_buf) < (unsigned) scr_columns)
803 fill = scr_columns - str_length(row_buf);
804 else
805 fill = 0;
806
807 for (j = 0; j < fill; ++j)
808 putchar(' ');
809 fflush(stdout);
810 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
811 }
812
813 pane.rflags |= REDRAW_CARET;
814}
815
816/** Display pane status in the status line. */
817static void pane_status_display(void)
818{
819 spt_t caret_pt;
820 coord_t coord;
821 char *fname;
822 int n;
823
824 tag_get_pt(&pane.caret_pos, &caret_pt);
825 spt_get_coord(&caret_pt, &coord);
826
827 fname = (doc.file_name != NULL) ? doc.file_name : "<unnamed>";
828
829 console_goto(con, 0, scr_rows - 1);
830 console_set_color(con, COLOR_WHITE, COLOR_BLACK, 0);
831 n = printf(" %d, %d: File '%s'. Ctrl-Q Quit Ctrl-S Save "
832 "Ctrl-E Save As", coord.row, coord.column, fname);
833 printf("%*s", scr_columns - 1 - n, "");
834 fflush(stdout);
835 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
836
837 pane.rflags |= REDRAW_CARET;
838}
839
840/** Set cursor to reflect position of the caret. */
841static void pane_caret_display(void)
842{
843 spt_t caret_pt;
844 coord_t coord;
845
846 tag_get_pt(&pane.caret_pos, &caret_pt);
847
848 spt_get_coord(&caret_pt, &coord);
849 console_goto(con, coord.column - pane.sh_column,
850 coord.row - pane.sh_row);
851}
852
853/** Insert a character at caret position. */
854static void insert_char(wchar_t c)
855{
856 spt_t pt;
857 char cbuf[STR_BOUNDS(1) + 1];
858 size_t offs;
859
860 tag_get_pt(&pane.caret_pos, &pt);
861
862 offs = 0;
863 chr_encode(c, cbuf, &offs, STR_BOUNDS(1) + 1);
864 cbuf[offs] = '\0';
865
866 (void) sheet_insert(&doc.sh, &pt, dir_before, cbuf);
867
868 pane.rflags |= REDRAW_ROW;
869 if (c == '\n')
870 pane.rflags |= REDRAW_TEXT;
871}
872
873/** Delete the character before the caret. */
874static void delete_char_before(void)
875{
876 spt_t sp, ep;
877 coord_t coord;
878
879 tag_get_pt(&pane.caret_pos, &ep);
880 spt_get_coord(&ep, &coord);
881
882 coord.column -= 1;
883 sheet_get_cell_pt(&doc.sh, &coord, dir_before, &sp);
884
885 (void) sheet_delete(&doc.sh, &sp, &ep);
886
887 pane.rflags |= REDRAW_ROW;
888 if (coord.column < 1)
889 pane.rflags |= REDRAW_TEXT;
890}
891
892/** Delete the character after the caret. */
893static void delete_char_after(void)
894{
895 spt_t sp, ep;
896 coord_t sc, ec;
897
898 tag_get_pt(&pane.caret_pos, &sp);
899 spt_get_coord(&sp, &sc);
900
901 sheet_get_cell_pt(&doc.sh, &sc, dir_after, &ep);
902 spt_get_coord(&ep, &ec);
903
904 (void) sheet_delete(&doc.sh, &sp, &ep);
905
906 pane.rflags |= REDRAW_ROW;
907 if (ec.row != sc.row)
908 pane.rflags |= REDRAW_TEXT;
909}
910
911/** Scroll pane after caret has moved.
912 *
913 * After modifying the position of the caret, this is called to scroll
914 * the pane to ensure that the caret is in the visible area.
915 */
916static void caret_update(void)
917{
918 spt_t pt;
919 coord_t coord;
920
921 tag_get_pt(&pane.caret_pos, &pt);
922 spt_get_coord(&pt, &coord);
923
924 /* Scroll pane vertically. */
925
926 if (coord.row < pane.sh_row) {
927 pane.sh_row = coord.row;
928 pane.rflags |= REDRAW_TEXT;
929 }
930
931 if (coord.row > pane.sh_row + pane.rows - 1) {
932 pane.sh_row = coord.row - pane.rows + 1;
933 pane.rflags |= REDRAW_TEXT;
934 }
935
936 /* Scroll pane horizontally. */
937
938 if (coord.column < pane.sh_column) {
939 pane.sh_column = coord.column;
940 pane.rflags |= REDRAW_TEXT;
941 }
942
943 if (coord.column > pane.sh_column + pane.columns - 1) {
944 pane.sh_column = coord.column - pane.columns + 1;
945 pane.rflags |= REDRAW_TEXT;
946 }
947
948 pane.rflags |= (REDRAW_CARET | REDRAW_STATUS);
949}
950
951/** Change the caret position.
952 *
953 * Moves caret relatively to the current position. Looking at the first
954 * character cell after the caret and moving by @a drow and @a dcolumn, we get
955 * to a new character cell, and thus a new character. Then we either go to the
956 * point before the the character or after it, depending on @a align_dir.
957 */
958static void caret_move(int drow, int dcolumn, enum dir_spec align_dir)
959{
960 spt_t pt;
961 coord_t coord;
962 int num_rows;
963 bool pure_vertical;
964
965 tag_get_pt(&pane.caret_pos, &pt);
966 spt_get_coord(&pt, &coord);
967 coord.row += drow; coord.column += dcolumn;
968
969 /* Clamp coordinates. */
970 if (drow < 0 && coord.row < 1) coord.row = 1;
971 if (dcolumn < 0 && coord.column < 1) coord.column = 1;
972 if (drow > 0) {
973 sheet_get_num_rows(&doc.sh, &num_rows);
974 if (coord.row > num_rows) coord.row = num_rows;
975 }
976
977 /* For purely vertical movement try attaining @c ideal_column. */
978 pure_vertical = (dcolumn == 0 && align_dir == dir_before);
979 if (pure_vertical)
980 coord.column = pane.ideal_column;
981
982 /*
983 * Select the point before or after the character at the designated
984 * coordinates. The character can be wider than one cell (e.g. tab).
985 */
986 sheet_get_cell_pt(&doc.sh, &coord, align_dir, &pt);
987 sheet_remove_tag(&doc.sh, &pane.caret_pos);
988 sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
989
990 /* For non-vertical movement set the new value for @c ideal_column. */
991 if (!pure_vertical) {
992 spt_get_coord(&pt, &coord);
993 pane.ideal_column = coord.column;
994 }
995
996 caret_update();
997}
998
999/** Check for non-empty selection. */
1000static bool selection_active(void)
1001{
1002 return (tag_cmp(&pane.caret_pos, &pane.sel_start) != 0);
1003}
1004
1005static void selection_get_points(spt_t *pa, spt_t *pb)
1006{
1007 spt_t pt;
1008
1009 tag_get_pt(&pane.sel_start, pa);
1010 tag_get_pt(&pane.caret_pos, pb);
1011
1012 if (spt_cmp(pa, pb) > 0) {
1013 pt = *pa;
1014 *pa = *pb;
1015 *pb = pt;
1016 }
1017}
1018
1019/** Delete selected text. */
1020static void selection_delete(void)
1021{
1022 spt_t pa, pb;
1023 coord_t ca, cb;
1024 int rel;
1025
1026 tag_get_pt(&pane.sel_start, &pa);
1027 tag_get_pt(&pane.caret_pos, &pb);
1028 spt_get_coord(&pa, &ca);
1029 spt_get_coord(&pb, &cb);
1030 rel = coord_cmp(&ca, &cb);
1031
1032 if (rel == 0)
1033 return;
1034
1035 if (rel < 0)
1036 sheet_delete(&doc.sh, &pa, &pb);
1037 else
1038 sheet_delete(&doc.sh, &pb, &pa);
1039
1040 if (ca.row == cb.row)
1041 pane.rflags |= REDRAW_ROW;
1042 else
1043 pane.rflags |= REDRAW_TEXT;
1044}
1045
1046static void selection_sel_all(void)
1047{
1048 spt_t spt, ept;
1049
1050 pt_get_sof(&spt);
1051 pt_get_eof(&ept);
1052 sheet_remove_tag(&doc.sh, &pane.sel_start);
1053 sheet_place_tag(&doc.sh, &spt, &pane.sel_start);
1054 sheet_remove_tag(&doc.sh, &pane.caret_pos);
1055 sheet_place_tag(&doc.sh, &ept, &pane.caret_pos);
1056
1057 pane.rflags |= REDRAW_TEXT;
1058 caret_update();
1059}
1060
1061static void selection_copy(void)
1062{
1063 spt_t pa, pb;
1064 char *str;
1065
1066 selection_get_points(&pa, &pb);
1067 str = range_get_str(&pa, &pb);
1068 if (str == NULL || clipboard_put_str(str) != EOK) {
1069 status_display("Copying to clipboard failed!");
1070 }
1071 free(str);
1072}
1073
1074static void insert_clipboard_data(void)
1075{
1076 char *str;
1077 size_t off;
1078 wchar_t c;
1079 int rc;
1080
1081 rc = clipboard_get_str(&str);
1082 if (rc != EOK || str == NULL)
1083 return;
1084
1085 off = 0;
1086
1087 while (true) {
1088 c = str_decode(str, &off, STR_NO_LIMIT);
1089 if (c == '\0')
1090 break;
1091
1092 insert_char(c);
1093 }
1094
1095 free(str);
1096}
1097
1098/** Get start-of-file s-point. */
1099static void pt_get_sof(spt_t *pt)
1100{
1101 coord_t coord;
1102
1103 coord.row = coord.column = 1;
1104 sheet_get_cell_pt(&doc.sh, &coord, dir_before, pt);
1105}
1106
1107/** Get end-of-file s-point. */
1108static void pt_get_eof(spt_t *pt)
1109{
1110 coord_t coord;
1111 int num_rows;
1112
1113 sheet_get_num_rows(&doc.sh, &num_rows);
1114 coord.row = num_rows + 1;
1115 coord.column = 1;
1116
1117 sheet_get_cell_pt(&doc.sh, &coord, dir_after, pt);
1118}
1119
1120/** Compare tags. */
1121static int tag_cmp(tag_t const *a, tag_t const *b)
1122{
1123 spt_t pa, pb;
1124
1125 tag_get_pt(a, &pa);
1126 tag_get_pt(b, &pb);
1127
1128 return spt_cmp(&pa, &pb);
1129}
1130
1131/** Compare s-points. */
1132static int spt_cmp(spt_t const *a, spt_t const *b)
1133{
1134 coord_t ca, cb;
1135
1136 spt_get_coord(a, &ca);
1137 spt_get_coord(b, &cb);
1138
1139 return coord_cmp(&ca, &cb);
1140}
1141
1142/** Compare coordinats. */
1143static int coord_cmp(coord_t const *a, coord_t const *b)
1144{
1145 if (a->row - b->row != 0)
1146 return a->row - b->row;
1147
1148 return a->column - b->column;
1149}
1150
1151/** Display text in the status line. */
1152static void status_display(char const *str)
1153{
1154 console_goto(con, 0, scr_rows - 1);
1155 console_set_color(con, COLOR_WHITE, COLOR_BLACK, 0);
1156 printf(" %*s ", -(scr_columns - 3), str);
1157 fflush(stdout);
1158 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
1159
1160 pane.rflags |= REDRAW_CARET;
1161}
1162
1163/** @}
1164 */
Note: See TracBrowser for help on using the repository browser.