source: mainline/uspace/app/edit/edit.c@ 0f24c57

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

Implement basis of selections in editor.

  • Property mode set to 100644
File size: 21.6 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 <bool.h>
48
49#include "sheet.h"
50
51enum redraw_flags {
52 REDRAW_TEXT = (1 << 0),
53 REDRAW_ROW = (1 << 1),
54 REDRAW_STATUS = (1 << 2),
55 REDRAW_CARET = (1 << 3)
56};
57
58/** Pane
59 *
60 * A rectangular area of the screen used to edit a document. Different
61 * panes can be possibly used to edit the same document.
62 */
63typedef struct {
64 /* Pane dimensions */
65 int rows, columns;
66
67 /* Position of the visible area */
68 int sh_row, sh_column;
69
70 /** Bitmask of components that need redrawing */
71 enum redraw_flags rflags;
72
73 /** Current position of the caret */
74 tag_t caret_pos;
75
76 /** Start of selection */
77 tag_t sel_start;
78
79 /**
80 * Ideal column where the caret should try to get. This is used
81 * for maintaining the same column during vertical movement.
82 */
83 int ideal_column;
84} pane_t;
85
86/** Document
87 *
88 * Associates a sheet with a file where it can be saved to.
89 */
90typedef struct {
91 char *file_name;
92 sheet_t sh;
93} doc_t;
94
95static int con;
96static doc_t doc;
97static bool done;
98static pane_t pane;
99
100static int scr_rows, scr_columns;
101
102#define ROW_BUF_SIZE 4096
103#define BUF_SIZE 64
104#define TAB_WIDTH 8
105#define ED_INFTY 65536
106
107/** Maximum filename length that can be entered. */
108#define INFNAME_MAX_LEN 128
109
110static void key_handle_unmod(console_event_t const *ev);
111static void key_handle_ctrl(console_event_t const *ev);
112static void key_handle_shift(console_event_t const *ev);
113static void key_handle_movement(unsigned int key, bool shift);
114
115static int file_save(char const *fname);
116static void file_save_as(void);
117static int file_insert(char *fname);
118static int file_save_range(char const *fname, spt_t const *spos,
119 spt_t const *epos);
120static char *filename_prompt(char const *prompt, char const *init_value);
121
122static void pane_text_display(void);
123static void pane_row_display(void);
124static void pane_row_range_display(int r0, int r1);
125static void pane_status_display(void);
126static void pane_caret_display(void);
127
128static void insert_char(wchar_t c);
129static void delete_char_before(void);
130static void delete_char_after(void);
131static void caret_update(void);
132static void caret_move(int drow, int dcolumn, enum dir_spec align_dir);
133
134static bool selection_active(void);
135static void selection_delete(void);
136
137static void pt_get_sof(spt_t *pt);
138static void pt_get_eof(spt_t *pt);
139static int tag_cmp(tag_t const *a, tag_t const *b);
140static int spt_cmp(spt_t const *a, spt_t const *b);
141static int coord_cmp(coord_t const *a, coord_t const *b);
142
143static void status_display(char const *str);
144
145
146int main(int argc, char *argv[])
147{
148 console_event_t ev;
149 coord_t coord;
150 bool new_file;
151
152 spt_t pt;
153
154 con = fphone(stdout);
155 console_clear(con);
156
157 console_get_size(con, &scr_columns, &scr_rows);
158
159 pane.rows = scr_rows - 1;
160 pane.columns = scr_columns;
161 pane.sh_row = 1;
162 pane.sh_column = 1;
163
164 /* Start with an empty sheet. */
165 sheet_init(&doc.sh);
166
167 /* Place caret at the beginning of file. */
168 coord.row = coord.column = 1;
169 sheet_get_cell_pt(&doc.sh, &coord, dir_before, &pt);
170 sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
171 pane.ideal_column = coord.column;
172
173 if (argc == 2) {
174 doc.file_name = str_dup(argv[1]);
175 } else if (argc > 1) {
176 printf("Invalid arguments.\n");
177 return -2;
178 } else {
179 doc.file_name = NULL;
180 }
181
182 new_file = false;
183
184 if (doc.file_name == NULL || file_insert(doc.file_name) != EOK)
185 new_file = true;
186
187 /* Move to beginning of file. */
188 caret_move(-ED_INFTY, -ED_INFTY, dir_before);
189
190 /* Place selection start tag. */
191 tag_get_pt(&pane.caret_pos, &pt);
192 sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
193
194 /* Initial display */
195 console_clear(con);
196 pane_text_display();
197 pane_status_display();
198 if (new_file && doc.file_name != NULL)
199 status_display("File not found. Starting empty file.");
200 pane_caret_display();
201
202
203 done = false;
204
205 while (!done) {
206 console_get_event(con, &ev);
207 pane.rflags = 0;
208
209 if (ev.type == KEY_PRESS) {
210 /* Handle key press. */
211 if (((ev.mods & KM_ALT) == 0) &&
212 ((ev.mods & KM_SHIFT) == 0) &&
213 (ev.mods & KM_CTRL) != 0) {
214 key_handle_ctrl(&ev);
215 } else if (((ev.mods & KM_ALT) == 0) &&
216 ((ev.mods & KM_CTRL) == 0) &&
217 (ev.mods & KM_SHIFT) != 0) {
218 key_handle_shift(&ev);
219 } else if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
220 key_handle_unmod(&ev);
221 }
222 }
223
224 /* Redraw as necessary. */
225
226 if (pane.rflags & REDRAW_TEXT)
227 pane_text_display();
228 if (pane.rflags & REDRAW_ROW)
229 pane_row_display();
230 if (pane.rflags & REDRAW_STATUS)
231 pane_status_display();
232 if (pane.rflags & REDRAW_CARET)
233 pane_caret_display();
234 }
235
236 console_clear(con);
237
238 return 0;
239}
240
241/** Handle key without modifier. */
242static void key_handle_unmod(console_event_t const *ev)
243{
244 switch (ev->key) {
245 case KC_ENTER:
246 selection_delete();
247 insert_char('\n');
248 caret_update();
249 break;
250 case KC_LEFT:
251 case KC_RIGHT:
252 case KC_UP:
253 case KC_DOWN:
254 case KC_HOME:
255 case KC_END:
256 case KC_PAGE_UP:
257 case KC_PAGE_DOWN:
258 key_handle_movement(ev->key, false);
259 break;
260 case KC_BACKSPACE:
261 if (selection_active())
262 selection_delete();
263 else
264 delete_char_before();
265 caret_update();
266 break;
267 case KC_DELETE:
268 if (selection_active())
269 selection_delete();
270 else
271 delete_char_after();
272 caret_update();
273 break;
274 default:
275 if (ev->c >= 32 || ev->c == '\t') {
276 selection_delete();
277 insert_char(ev->c);
278 caret_update();
279 }
280 break;
281 }
282}
283
284/** Handle Shift-key combination. */
285static void key_handle_shift(console_event_t const *ev)
286{
287 switch (ev->key) {
288 case KC_LEFT:
289 case KC_RIGHT:
290 case KC_UP:
291 case KC_DOWN:
292 case KC_HOME:
293 case KC_END:
294 case KC_PAGE_UP:
295 case KC_PAGE_DOWN:
296 key_handle_movement(ev->key, true);
297 break;
298 default:
299 if (ev->c >= 32 || ev->c == '\t') {
300 selection_delete();
301 insert_char(ev->c);
302 caret_update();
303 }
304 break;
305 }
306}
307
308/** Handle Ctrl-key combination. */
309static void key_handle_ctrl(console_event_t const *ev)
310{
311 switch (ev->key) {
312 case KC_Q:
313 done = true;
314 break;
315 case KC_S:
316 if (doc.file_name != NULL)
317 file_save(doc.file_name);
318 else
319 file_save_as();
320 break;
321 case KC_E:
322 file_save_as();
323 break;
324 default:
325 break;
326 }
327}
328
329static void key_handle_movement(unsigned int key, bool select)
330{
331 spt_t pt;
332 spt_t caret_pt;
333 coord_t c_old, c_new;
334 bool had_sel;
335
336 /* Check if we had selection before. */
337 tag_get_pt(&pane.caret_pos, &caret_pt);
338 tag_get_pt(&pane.sel_start, &pt);
339 had_sel = !spt_equal(&caret_pt, &pt);
340
341 switch (key) {
342 case KC_LEFT:
343 caret_move(0, -1, dir_before);
344 break;
345 case KC_RIGHT:
346 caret_move(0, 0, dir_after);
347 break;
348 case KC_UP:
349 caret_move(-1, 0, dir_before);
350 break;
351 case KC_DOWN:
352 caret_move(+1, 0, dir_before);
353 break;
354 case KC_HOME:
355 caret_move(0, -ED_INFTY, dir_before);
356 break;
357 case KC_END:
358 caret_move(0, +ED_INFTY, dir_before);
359 break;
360 case KC_PAGE_UP:
361 caret_move(-pane.rows, 0, dir_before);
362 break;
363 case KC_PAGE_DOWN:
364 caret_move(+pane.rows, 0, dir_before);
365 break;
366 default:
367 break;
368 }
369
370 if (select == false) {
371 /* Move sel_start to the same point as caret. */
372 sheet_remove_tag(&doc.sh, &pane.sel_start);
373 tag_get_pt(&pane.caret_pos, &pt);
374 sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
375 }
376
377 if (select) {
378 tag_get_pt(&pane.caret_pos, &pt);
379 spt_get_coord(&caret_pt, &c_old);
380 spt_get_coord(&pt, &c_new);
381
382 if (c_old.row == c_new.row)
383 pane.rflags |= REDRAW_ROW;
384 else
385 pane.rflags |= REDRAW_TEXT;
386
387 } else if (had_sel == true) {
388 /* Redraw because text was unselected. */
389 pane.rflags |= REDRAW_TEXT;
390 }
391}
392
393/** Save the document. */
394static int file_save(char const *fname)
395{
396 spt_t sp, ep;
397 int rc;
398
399 status_display("Saving...");
400 pt_get_sof(&sp);
401 pt_get_eof(&ep);
402
403 rc = file_save_range(fname, &sp, &ep);
404
405 switch (rc) {
406 case EINVAL:
407 status_display("Error opening file!");
408 break;
409 case EIO:
410 status_display("Error writing data!");
411 break;
412 default:
413 status_display("File saved.");
414 break;
415 }
416
417 return rc;
418}
419
420/** Change document name and save. */
421static void file_save_as(void)
422{
423 char *old_fname, *fname;
424 int rc;
425
426 old_fname = (doc.file_name != NULL) ? doc.file_name : "";
427 fname = filename_prompt("Save As", old_fname);
428 if (fname == NULL) {
429 status_display("Save cancelled.");
430 return;
431 }
432
433 rc = file_save(fname);
434 if (rc != EOK)
435 return;
436
437 if (doc.file_name != NULL)
438 free(doc.file_name);
439 doc.file_name = fname;
440}
441
442/** Ask for a file name. */
443static char *filename_prompt(char const *prompt, char const *init_value)
444{
445 console_event_t ev;
446 char *str;
447 wchar_t buffer[INFNAME_MAX_LEN + 1];
448 int max_len;
449 int nc;
450 bool done;
451
452 asprintf(&str, "%s: %s", prompt, init_value);
453 status_display(str);
454 console_goto(con, 1 + str_length(str), scr_rows - 1);
455 free(str);
456
457 console_set_color(con, COLOR_WHITE, COLOR_BLACK, 0);
458
459 max_len = min(INFNAME_MAX_LEN, scr_columns - 4 - str_length(prompt));
460 str_to_wstr(buffer, max_len + 1, init_value);
461 nc = wstr_length(buffer);
462 done = false;
463
464 while (!done) {
465 console_get_event(con, &ev);
466
467 if (ev.type == KEY_PRESS) {
468 /* Handle key press. */
469 if (((ev.mods & KM_ALT) == 0) &&
470 (ev.mods & KM_CTRL) != 0) {
471 ;
472 } else if ((ev.mods & (KM_CTRL | KM_ALT)) == 0) {
473 switch (ev.key) {
474 case KC_ESCAPE:
475 return NULL;
476 case KC_BACKSPACE:
477 if (nc > 0) {
478 putchar('\b');
479 fflush(stdout);
480 --nc;
481 }
482 break;
483 case KC_ENTER:
484 done = true;
485 break;
486 default:
487 if (ev.c >= 32 && nc < max_len) {
488 putchar(ev.c);
489 fflush(stdout);
490 buffer[nc++] = ev.c;
491 }
492 break;
493 }
494 }
495 }
496 }
497
498 buffer[nc] = '\0';
499 str = wstr_to_astr(buffer);
500
501 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
502
503 return str;
504}
505
506/** Insert file at caret position.
507 *
508 * Reads in the contents of a file and inserts them at the current position
509 * of the caret.
510 */
511static int file_insert(char *fname)
512{
513 FILE *f;
514 wchar_t c;
515 char buf[BUF_SIZE];
516 int bcnt;
517 int n_read;
518 size_t off;
519
520 f = fopen(fname, "rt");
521 if (f == NULL)
522 return EINVAL;
523
524 bcnt = 0;
525
526 while (true) {
527 if (bcnt < STR_BOUNDS(1)) {
528 n_read = fread(buf + bcnt, 1, BUF_SIZE - bcnt, f);
529 bcnt += n_read;
530 }
531
532 off = 0;
533 c = str_decode(buf, &off, bcnt);
534 if (c == '\0')
535 break;
536
537 bcnt -= off;
538 memcpy(buf, buf + off, bcnt);
539
540 insert_char(c);
541 }
542
543 fclose(f);
544
545 return EOK;
546}
547
548/** Save a range of text into a file. */
549static int file_save_range(char const *fname, spt_t const *spos,
550 spt_t const *epos)
551{
552 FILE *f;
553 char buf[BUF_SIZE];
554 spt_t sp, bep;
555 size_t bytes, n_written;
556
557 f = fopen(fname, "wt");
558 if (f == NULL)
559 return EINVAL;
560
561 sp = *spos;
562
563 do {
564 sheet_copy_out(&doc.sh, &sp, epos, buf, BUF_SIZE, &bep);
565 bytes = str_size(buf);
566
567 n_written = fwrite(buf, 1, bytes, f);
568 if (n_written != bytes) {
569 return EIO;
570 }
571
572 sp = bep;
573 } while (!spt_equal(&bep, epos));
574
575 if (fclose(f) != EOK)
576 return EIO;
577
578 return EOK;
579}
580
581static void pane_text_display(void)
582{
583 int sh_rows, rows;
584 int i, j;
585
586 sheet_get_num_rows(&doc.sh, &sh_rows);
587 rows = min(sh_rows - pane.sh_row + 1, pane.rows);
588
589 /* Draw rows from the sheet. */
590
591 console_goto(con, 0, 0);
592 pane_row_range_display(0, rows);
593
594 /* Clear the remaining rows if file is short. */
595
596 for (i = rows; i < pane.rows; ++i) {
597 console_goto(con, 0, i);
598 for (j = 0; j < scr_columns; ++j)
599 putchar(' ');
600 fflush(stdout);
601 }
602
603 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
604 pane.rflags &= ~REDRAW_ROW;
605}
606
607/** Display just the row where the caret is. */
608static void pane_row_display(void)
609{
610 spt_t caret_pt;
611 coord_t coord;
612 int ridx;
613
614 tag_get_pt(&pane.caret_pos, &caret_pt);
615 spt_get_coord(&caret_pt, &coord);
616
617 ridx = coord.row - pane.sh_row;
618 pane_row_range_display(ridx, ridx + 1);
619 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
620}
621
622static void pane_row_range_display(int r0, int r1)
623{
624 int i, j, fill;
625 spt_t rb, re, dep, pt;
626 coord_t rbc, rec;
627 char row_buf[ROW_BUF_SIZE];
628 wchar_t c;
629 size_t pos, size;
630 unsigned s_column;
631 coord_t csel_start, csel_end, ctmp;
632
633 /* Determine selection start and end. */
634
635 tag_get_pt(&pane.sel_start, &pt);
636 spt_get_coord(&pt, &csel_start);
637
638 tag_get_pt(&pane.caret_pos, &pt);
639 spt_get_coord(&pt, &csel_end);
640
641 if (coord_cmp(&csel_start, &csel_end) > 0) {
642 ctmp = csel_start;
643 csel_start = csel_end;
644 csel_end = ctmp;
645 }
646
647 /* Draw rows from the sheet. */
648
649 console_goto(con, 0, 0);
650 for (i = r0; i < r1; ++i) {
651 /* Starting point for row display */
652 rbc.row = pane.sh_row + i;
653 rbc.column = pane.sh_column;
654 sheet_get_cell_pt(&doc.sh, &rbc, dir_before, &rb);
655
656 /* Ending point for row display */
657 rec.row = pane.sh_row + i;
658 rec.column = pane.sh_column + pane.columns;
659 sheet_get_cell_pt(&doc.sh, &rec, dir_before, &re);
660
661 /* Copy the text of the row to the buffer. */
662 sheet_copy_out(&doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
663
664 /* Display text from the buffer. */
665
666 if (coord_cmp(&csel_start, &rbc) <= 0 &&
667 coord_cmp(&rbc, &csel_end) < 0) {
668 fflush(stdout);
669 console_set_color(con, COLOR_BLACK, COLOR_RED, 0);
670 fflush(stdout);
671 }
672
673 console_goto(con, 0, i);
674 size = str_size(row_buf);
675 pos = 0;
676 s_column = 1;
677 while (pos < size) {
678 if (csel_start.row == rbc.row && csel_start.column == s_column) {
679 fflush(stdout);
680 console_set_color(con, COLOR_BLACK, COLOR_RED, 0);
681 fflush(stdout);
682 }
683
684 if (csel_end.row == rbc.row && csel_end.column == s_column) {
685 fflush(stdout);
686 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
687 fflush(stdout);
688 }
689
690 c = str_decode(row_buf, &pos, size);
691 if (c != '\t') {
692 printf("%lc", c);
693 s_column += 1;
694 } else {
695 fill = 1 + ALIGN_UP(s_column, TAB_WIDTH)
696 - s_column;
697
698 for (j = 0; j < fill; ++j)
699 putchar(' ');
700 s_column += fill;
701 }
702 }
703
704 if (csel_end.row == rbc.row && csel_end.column == s_column) {
705 fflush(stdout);
706 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
707 fflush(stdout);
708 }
709
710 /* Fill until the end of display area. */
711
712 if (str_length(row_buf) < (unsigned) scr_columns)
713 fill = scr_columns - str_length(row_buf);
714 else
715 fill = 0;
716
717 for (j = 0; j < fill; ++j)
718 putchar(' ');
719 fflush(stdout);
720 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
721 }
722
723 pane.rflags |= REDRAW_CARET;
724}
725
726/** Display pane status in the status line. */
727static void pane_status_display(void)
728{
729 spt_t caret_pt;
730 coord_t coord;
731 char *fname;
732 int n;
733
734 tag_get_pt(&pane.caret_pos, &caret_pt);
735 spt_get_coord(&caret_pt, &coord);
736
737 fname = (doc.file_name != NULL) ? doc.file_name : "<unnamed>";
738
739 console_goto(con, 0, scr_rows - 1);
740 console_set_color(con, COLOR_WHITE, COLOR_BLACK, 0);
741 n = printf(" %d, %d: File '%s'. Ctrl-Q Quit Ctrl-S Save "
742 "Ctrl-E Save As", coord.row, coord.column, fname);
743 printf("%*s", scr_columns - 1 - n, "");
744 fflush(stdout);
745 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
746
747 pane.rflags |= REDRAW_CARET;
748}
749
750/** Set cursor to reflect position of the caret. */
751static void pane_caret_display(void)
752{
753 spt_t caret_pt;
754 coord_t coord;
755
756 tag_get_pt(&pane.caret_pos, &caret_pt);
757
758 spt_get_coord(&caret_pt, &coord);
759 console_goto(con, coord.column - pane.sh_column,
760 coord.row - pane.sh_row);
761}
762
763/** Insert a character at caret position. */
764static void insert_char(wchar_t c)
765{
766 spt_t pt;
767 char cbuf[STR_BOUNDS(1) + 1];
768 size_t offs;
769
770 tag_get_pt(&pane.caret_pos, &pt);
771
772 offs = 0;
773 chr_encode(c, cbuf, &offs, STR_BOUNDS(1) + 1);
774 cbuf[offs] = '\0';
775
776 (void) sheet_insert(&doc.sh, &pt, dir_before, cbuf);
777
778 pane.rflags |= REDRAW_ROW;
779 if (c == '\n')
780 pane.rflags |= REDRAW_TEXT;
781}
782
783/** Delete the character before the caret. */
784static void delete_char_before(void)
785{
786 spt_t sp, ep;
787 coord_t coord;
788
789 tag_get_pt(&pane.caret_pos, &ep);
790 spt_get_coord(&ep, &coord);
791
792 coord.column -= 1;
793 sheet_get_cell_pt(&doc.sh, &coord, dir_before, &sp);
794
795 (void) sheet_delete(&doc.sh, &sp, &ep);
796
797 pane.rflags |= REDRAW_ROW;
798 if (coord.column < 1)
799 pane.rflags |= REDRAW_TEXT;
800}
801
802/** Delete the character after the caret. */
803static void delete_char_after(void)
804{
805 spt_t sp, ep;
806 coord_t sc, ec;
807
808 tag_get_pt(&pane.caret_pos, &sp);
809 spt_get_coord(&sp, &sc);
810
811 sheet_get_cell_pt(&doc.sh, &sc, dir_after, &ep);
812 spt_get_coord(&ep, &ec);
813
814 (void) sheet_delete(&doc.sh, &sp, &ep);
815
816 pane.rflags |= REDRAW_ROW;
817 if (ec.row != sc.row)
818 pane.rflags |= REDRAW_TEXT;
819}
820
821/** Scroll pane after caret has moved.
822 *
823 * After modifying the position of the caret, this is called to scroll
824 * the pane to ensure that the caret is in the visible area.
825 */
826static void caret_update(void)
827{
828 spt_t pt;
829 coord_t coord;
830
831 tag_get_pt(&pane.caret_pos, &pt);
832 spt_get_coord(&pt, &coord);
833
834 /* Scroll pane vertically. */
835
836 if (coord.row < pane.sh_row) {
837 pane.sh_row = coord.row;
838 pane.rflags |= REDRAW_TEXT;
839 }
840
841 if (coord.row > pane.sh_row + pane.rows - 1) {
842 pane.sh_row = coord.row - pane.rows + 1;
843 pane.rflags |= REDRAW_TEXT;
844 }
845
846 /* Scroll pane horizontally. */
847
848 if (coord.column < pane.sh_column) {
849 pane.sh_column = coord.column;
850 pane.rflags |= REDRAW_TEXT;
851 }
852
853 if (coord.column > pane.sh_column + pane.columns - 1) {
854 pane.sh_column = coord.column - pane.columns + 1;
855 pane.rflags |= REDRAW_TEXT;
856 }
857
858 pane.rflags |= (REDRAW_CARET | REDRAW_STATUS);
859}
860
861/** Change the caret position.
862 *
863 * Moves caret relatively to the current position. Looking at the first
864 * character cell after the caret and moving by @a drow and @a dcolumn, we get
865 * to a new character cell, and thus a new character. Then we either go to the
866 * point before the the character or after it, depending on @a align_dir.
867 */
868static void caret_move(int drow, int dcolumn, enum dir_spec align_dir)
869{
870 spt_t pt;
871 coord_t coord;
872 int num_rows;
873 bool pure_vertical;
874
875 tag_get_pt(&pane.caret_pos, &pt);
876 spt_get_coord(&pt, &coord);
877 coord.row += drow; coord.column += dcolumn;
878
879 /* Clamp coordinates. */
880 if (drow < 0 && coord.row < 1) coord.row = 1;
881 if (dcolumn < 0 && coord.column < 1) coord.column = 1;
882 if (drow > 0) {
883 sheet_get_num_rows(&doc.sh, &num_rows);
884 if (coord.row > num_rows) coord.row = num_rows;
885 }
886
887 /* For purely vertical movement try attaining @c ideal_column. */
888 pure_vertical = (dcolumn == 0 && align_dir == dir_before);
889 if (pure_vertical)
890 coord.column = pane.ideal_column;
891
892 /*
893 * Select the point before or after the character at the designated
894 * coordinates. The character can be wider than one cell (e.g. tab).
895 */
896 sheet_get_cell_pt(&doc.sh, &coord, align_dir, &pt);
897 sheet_remove_tag(&doc.sh, &pane.caret_pos);
898 sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
899
900 /* For non-vertical movement set the new value for @c ideal_column. */
901 if (!pure_vertical) {
902 spt_get_coord(&pt, &coord);
903 pane.ideal_column = coord.column;
904 }
905
906 caret_update();
907}
908
909/** Check for non-empty selection. */
910static bool selection_active(void)
911{
912 return (tag_cmp(&pane.caret_pos, &pane.sel_start) != 0);
913}
914
915/** Delete selected text. */
916static void selection_delete(void)
917{
918 spt_t pa, pb;
919 coord_t ca, cb;
920 int rel;
921
922 tag_get_pt(&pane.sel_start, &pa);
923 tag_get_pt(&pane.caret_pos, &pb);
924 spt_get_coord(&pa, &ca);
925 spt_get_coord(&pb, &cb);
926 rel = coord_cmp(&ca, &cb);
927
928 if (rel == 0)
929 return;
930
931 if (rel < 0)
932 sheet_delete(&doc.sh, &pa, &pb);
933 else
934 sheet_delete(&doc.sh, &pb, &pa);
935
936 if (ca.row == cb.row)
937 pane.rflags |= REDRAW_ROW;
938 else
939 pane.rflags |= REDRAW_TEXT;
940}
941
942/** Get start-of-file s-point. */
943static void pt_get_sof(spt_t *pt)
944{
945 coord_t coord;
946
947 coord.row = coord.column = 1;
948 sheet_get_cell_pt(&doc.sh, &coord, dir_before, pt);
949}
950
951/** Get end-of-file s-point. */
952static void pt_get_eof(spt_t *pt)
953{
954 coord_t coord;
955 int num_rows;
956
957 sheet_get_num_rows(&doc.sh, &num_rows);
958 coord.row = num_rows + 1;
959 coord.column = 1;
960
961 sheet_get_cell_pt(&doc.sh, &coord, dir_after, pt);
962}
963
964/** Compare tags. */
965static int tag_cmp(tag_t const *a, tag_t const *b)
966{
967 spt_t pa, pb;
968
969 tag_get_pt(a, &pa);
970 tag_get_pt(b, &pb);
971
972 return spt_cmp(&pa, &pb);
973}
974
975/** Compare s-points. */
976static int spt_cmp(spt_t const *a, spt_t const *b)
977{
978 coord_t ca, cb;
979
980 spt_get_coord(a, &ca);
981 spt_get_coord(b, &cb);
982
983 return coord_cmp(&ca, &cb);
984}
985
986/** Compare coordinats. */
987static int coord_cmp(coord_t const *a, coord_t const *b)
988{
989 if (a->row - b->row != 0)
990 return a->row - b->row;
991
992 return a->column - b->column;
993}
994
995/** Display text in the status line. */
996static void status_display(char const *str)
997{
998 console_goto(con, 0, scr_rows - 1);
999 console_set_color(con, COLOR_WHITE, COLOR_BLACK, 0);
1000 printf(" %*s ", -(scr_columns - 3), str);
1001 fflush(stdout);
1002 console_set_color(con, COLOR_BLACK, COLOR_WHITE, 0);
1003
1004 pane.rflags |= REDRAW_CARET;
1005}
1006
1007/** @}
1008 */
Note: See TracBrowser for help on using the repository browser.