source: mainline/uspace/app/edit/edit.c@ 9f1362d4

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

console output improvements

  • define new generic styles (STYLE_INVERTED for inverted print and STYLE_SELECTION for selections), use them primarily instead of specifying colors or RGBs
  • use console_set_style(fphone(stdout), STYLE_NORMAL) as the correct mean for reseting console settings (instead of specifying conrete hardcoded colors)
  • rename console_goto() to console_set_pos() (consistency with console_get_pos())
  • use semantically correct unsigned types for console sizes and cursor positions (instead of signed types)
  • use unsigned types for sizes and positions in libclui
  • top: nicer screen redrawing (do not use console_clear() which causes flickering, but repaint the screen properly — not entirely finished yet)
  • initialize mouse pointer coordinates (so the mouse cursor does not behave erratic after boot, unfortunatelly this does not solve ticket #223)
  • Property mode set to 100644
File size: 24.2 KB
RevLine 
[3052ff4]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>
[1352fc1]38#include <stdlib.h>
[3052ff4]39#include <sys/types.h>
40#include <vfs/vfs.h>
41#include <io/console.h>
[9f1362d4]42#include <io/style.h>
[3052ff4]43#include <io/keycode.h>
44#include <errno.h>
45#include <align.h>
46#include <macros.h>
[0902edfe]47#include <clipboard.h>
[3052ff4]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;
[743e17b]76
[0f24c57]77 /** Start of selection */
78 tag_t sel_start;
79
[743e17b]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;
[3052ff4]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;
[8190e63]100static bool cursor_visible;
[3052ff4]101
[9f1362d4]102static ipcarg_t scr_rows;
103static ipcarg_t scr_columns;
[3052ff4]104
105#define ROW_BUF_SIZE 4096
106#define BUF_SIZE 64
107#define TAB_WIDTH 8
108#define ED_INFTY 65536
109
[ba26129]110/** Maximum filename length that can be entered. */
111#define INFNAME_MAX_LEN 128
112
[8190e63]113static void cursor_show(void);
114static void cursor_hide(void);
115static void cursor_setvis(bool visible);
116
[3052ff4]117static void key_handle_unmod(console_event_t const *ev);
118static void key_handle_ctrl(console_event_t const *ev);
[0f24c57]119static void key_handle_shift(console_event_t const *ev);
120static void key_handle_movement(unsigned int key, bool shift);
121
[3052ff4]122static int file_save(char const *fname);
[1352fc1]123static void file_save_as(void);
[3052ff4]124static int file_insert(char *fname);
125static int file_save_range(char const *fname, spt_t const *spos,
126 spt_t const *epos);
[1352fc1]127static char *filename_prompt(char const *prompt, char const *init_value);
[0902edfe]128static char *range_get_str(spt_t const *spos, spt_t const *epos);
[0f24c57]129
[3052ff4]130static void pane_text_display(void);
131static void pane_row_display(void);
132static void pane_row_range_display(int r0, int r1);
133static void pane_status_display(void);
134static void pane_caret_display(void);
[0f24c57]135
[3052ff4]136static void insert_char(wchar_t c);
137static void delete_char_before(void);
138static void delete_char_after(void);
139static void caret_update(void);
140static void caret_move(int drow, int dcolumn, enum dir_spec align_dir);
[0f24c57]141
142static bool selection_active(void);
[cedd33b]143static void selection_sel_all(void);
[0902edfe]144static void selection_get_points(spt_t *pa, spt_t *pb);
[0f24c57]145static void selection_delete(void);
[0902edfe]146static void selection_copy(void);
147static void insert_clipboard_data(void);
[0f24c57]148
[3052ff4]149static void pt_get_sof(spt_t *pt);
150static void pt_get_eof(spt_t *pt);
[0f24c57]151static int tag_cmp(tag_t const *a, tag_t const *b);
152static int spt_cmp(spt_t const *a, spt_t const *b);
153static int coord_cmp(coord_t const *a, coord_t const *b);
154
[3052ff4]155static void status_display(char const *str);
156
157
158int main(int argc, char *argv[])
159{
160 console_event_t ev;
161 coord_t coord;
162 bool new_file;
163
164 spt_t pt;
165
166 con = fphone(stdout);
167 console_clear(con);
168
169 console_get_size(con, &scr_columns, &scr_rows);
170
171 pane.rows = scr_rows - 1;
[99e5526]172 pane.columns = scr_columns;
[3052ff4]173 pane.sh_row = 1;
[99e5526]174 pane.sh_column = 1;
[3052ff4]175
176 /* Start with an empty sheet. */
177 sheet_init(&doc.sh);
178
179 /* Place caret at the beginning of file. */
180 coord.row = coord.column = 1;
181 sheet_get_cell_pt(&doc.sh, &coord, dir_before, &pt);
182 sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
[743e17b]183 pane.ideal_column = coord.column;
[3052ff4]184
185 if (argc == 2) {
[1352fc1]186 doc.file_name = str_dup(argv[1]);
[3052ff4]187 } else if (argc > 1) {
188 printf("Invalid arguments.\n");
189 return -2;
190 } else {
[1352fc1]191 doc.file_name = NULL;
[3052ff4]192 }
193
194 new_file = false;
195
[1352fc1]196 if (doc.file_name == NULL || file_insert(doc.file_name) != EOK)
[3052ff4]197 new_file = true;
198
199 /* Move to beginning of file. */
200 caret_move(-ED_INFTY, -ED_INFTY, dir_before);
201
[0f24c57]202 /* Place selection start tag. */
203 tag_get_pt(&pane.caret_pos, &pt);
204 sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
205
[3052ff4]206 /* Initial display */
[8190e63]207 cursor_visible = true;
208
209 cursor_hide();
[3052ff4]210 console_clear(con);
211 pane_text_display();
212 pane_status_display();
[1352fc1]213 if (new_file && doc.file_name != NULL)
214 status_display("File not found. Starting empty file.");
[3052ff4]215 pane_caret_display();
[8190e63]216 cursor_show();
[3052ff4]217
218 done = false;
219
220 while (!done) {
221 console_get_event(con, &ev);
222 pane.rflags = 0;
223
224 if (ev.type == KEY_PRESS) {
225 /* Handle key press. */
226 if (((ev.mods & KM_ALT) == 0) &&
[0f24c57]227 ((ev.mods & KM_SHIFT) == 0) &&
[3052ff4]228 (ev.mods & KM_CTRL) != 0) {
229 key_handle_ctrl(&ev);
[0f24c57]230 } else if (((ev.mods & KM_ALT) == 0) &&
231 ((ev.mods & KM_CTRL) == 0) &&
232 (ev.mods & KM_SHIFT) != 0) {
233 key_handle_shift(&ev);
234 } else if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
[3052ff4]235 key_handle_unmod(&ev);
236 }
237 }
238
239 /* Redraw as necessary. */
240
[8190e63]241 cursor_hide();
242
[3052ff4]243 if (pane.rflags & REDRAW_TEXT)
244 pane_text_display();
245 if (pane.rflags & REDRAW_ROW)
246 pane_row_display();
247 if (pane.rflags & REDRAW_STATUS)
248 pane_status_display();
249 if (pane.rflags & REDRAW_CARET)
250 pane_caret_display();
[8190e63]251
252 cursor_show();
[3052ff4]253 }
254
255 console_clear(con);
256
257 return 0;
258}
259
[8190e63]260static void cursor_show(void)
261{
262 cursor_setvis(true);
263}
264
265static void cursor_hide(void)
266{
267 cursor_setvis(false);
268}
269
270static void cursor_setvis(bool visible)
271{
272 if (cursor_visible != visible) {
273 console_cursor_visibility(con, visible);
274 cursor_visible = visible;
275 }
276}
277
[3052ff4]278/** Handle key without modifier. */
279static void key_handle_unmod(console_event_t const *ev)
280{
281 switch (ev->key) {
282 case KC_ENTER:
[0f24c57]283 selection_delete();
[3052ff4]284 insert_char('\n');
285 caret_update();
286 break;
287 case KC_LEFT:
288 case KC_RIGHT:
289 case KC_UP:
290 case KC_DOWN:
291 case KC_HOME:
292 case KC_END:
293 case KC_PAGE_UP:
294 case KC_PAGE_DOWN:
[0f24c57]295 key_handle_movement(ev->key, false);
[3052ff4]296 break;
297 case KC_BACKSPACE:
[0f24c57]298 if (selection_active())
299 selection_delete();
300 else
301 delete_char_before();
[3052ff4]302 caret_update();
303 break;
304 case KC_DELETE:
[0f24c57]305 if (selection_active())
306 selection_delete();
307 else
308 delete_char_after();
[3052ff4]309 caret_update();
310 break;
311 default:
312 if (ev->c >= 32 || ev->c == '\t') {
[0f24c57]313 selection_delete();
314 insert_char(ev->c);
315 caret_update();
316 }
317 break;
318 }
319}
320
321/** Handle Shift-key combination. */
322static void key_handle_shift(console_event_t const *ev)
323{
324 switch (ev->key) {
325 case KC_LEFT:
326 case KC_RIGHT:
327 case KC_UP:
328 case KC_DOWN:
329 case KC_HOME:
330 case KC_END:
331 case KC_PAGE_UP:
332 case KC_PAGE_DOWN:
333 key_handle_movement(ev->key, true);
334 break;
335 default:
336 if (ev->c >= 32 || ev->c == '\t') {
337 selection_delete();
[3052ff4]338 insert_char(ev->c);
339 caret_update();
340 }
341 break;
342 }
343}
344
345/** Handle Ctrl-key combination. */
346static void key_handle_ctrl(console_event_t const *ev)
347{
348 switch (ev->key) {
349 case KC_Q:
350 done = true;
351 break;
352 case KC_S:
[1352fc1]353 if (doc.file_name != NULL)
354 file_save(doc.file_name);
355 else
356 file_save_as();
357 break;
358 case KC_E:
359 file_save_as();
[3052ff4]360 break;
[0902edfe]361 case KC_C:
362 selection_copy();
363 break;
364 case KC_V:
365 selection_delete();
366 insert_clipboard_data();
367 pane.rflags |= REDRAW_TEXT;
368 caret_update();
369 break;
[cedd33b]370 case KC_X:
371 selection_copy();
372 selection_delete();
373 pane.rflags |= REDRAW_TEXT;
374 caret_update();
375 break;
376 case KC_A:
377 selection_sel_all();
378 break;
[3052ff4]379 default:
380 break;
381 }
382}
383
[0f24c57]384static void key_handle_movement(unsigned int key, bool select)
385{
386 spt_t pt;
387 spt_t caret_pt;
388 coord_t c_old, c_new;
389 bool had_sel;
390
391 /* Check if we had selection before. */
392 tag_get_pt(&pane.caret_pos, &caret_pt);
393 tag_get_pt(&pane.sel_start, &pt);
394 had_sel = !spt_equal(&caret_pt, &pt);
395
396 switch (key) {
397 case KC_LEFT:
398 caret_move(0, -1, dir_before);
399 break;
400 case KC_RIGHT:
401 caret_move(0, 0, dir_after);
402 break;
403 case KC_UP:
404 caret_move(-1, 0, dir_before);
405 break;
406 case KC_DOWN:
407 caret_move(+1, 0, dir_before);
408 break;
409 case KC_HOME:
410 caret_move(0, -ED_INFTY, dir_before);
411 break;
412 case KC_END:
413 caret_move(0, +ED_INFTY, dir_before);
414 break;
415 case KC_PAGE_UP:
416 caret_move(-pane.rows, 0, dir_before);
417 break;
418 case KC_PAGE_DOWN:
419 caret_move(+pane.rows, 0, dir_before);
420 break;
421 default:
422 break;
423 }
424
425 if (select == false) {
426 /* Move sel_start to the same point as caret. */
427 sheet_remove_tag(&doc.sh, &pane.sel_start);
428 tag_get_pt(&pane.caret_pos, &pt);
429 sheet_place_tag(&doc.sh, &pt, &pane.sel_start);
430 }
431
432 if (select) {
433 tag_get_pt(&pane.caret_pos, &pt);
434 spt_get_coord(&caret_pt, &c_old);
435 spt_get_coord(&pt, &c_new);
436
437 if (c_old.row == c_new.row)
438 pane.rflags |= REDRAW_ROW;
439 else
440 pane.rflags |= REDRAW_TEXT;
441
442 } else if (had_sel == true) {
443 /* Redraw because text was unselected. */
444 pane.rflags |= REDRAW_TEXT;
445 }
446}
447
[3052ff4]448/** Save the document. */
449static int file_save(char const *fname)
450{
451 spt_t sp, ep;
452 int rc;
453
454 status_display("Saving...");
455 pt_get_sof(&sp);
456 pt_get_eof(&ep);
457
458 rc = file_save_range(fname, &sp, &ep);
[1352fc1]459
460 switch (rc) {
461 case EINVAL:
462 status_display("Error opening file!");
463 break;
464 case EIO:
465 status_display("Error writing data!");
466 break;
467 default:
468 status_display("File saved.");
469 break;
470 }
[3052ff4]471
472 return rc;
473}
474
[1352fc1]475/** Change document name and save. */
476static void file_save_as(void)
477{
[a000878c]478 const char *old_fname = (doc.file_name != NULL) ? doc.file_name : "";
479 char *fname;
480
[1352fc1]481 fname = filename_prompt("Save As", old_fname);
482 if (fname == NULL) {
483 status_display("Save cancelled.");
484 return;
485 }
486
[a000878c]487 int rc = file_save(fname);
[1352fc1]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;
[ba26129]501 wchar_t buffer[INFNAME_MAX_LEN + 1];
502 int max_len;
[1352fc1]503 int nc;
504 bool done;
505
506 asprintf(&str, "%s: %s", prompt, init_value);
507 status_display(str);
[9f1362d4]508 console_set_pos(con, 1 + str_length(str), scr_rows - 1);
[1352fc1]509 free(str);
510
[9f1362d4]511 console_set_style(con, STYLE_INVERTED);
[1352fc1]512
[ba26129]513 max_len = min(INFNAME_MAX_LEN, scr_columns - 4 - str_length(prompt));
514 str_to_wstr(buffer, max_len + 1, init_value);
[1352fc1]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:
[ba26129]541 if (ev.c >= 32 && nc < max_len) {
[1352fc1]542 putchar(ev.c);
543 fflush(stdout);
544 buffer[nc++] = ev.c;
545 }
546 break;
547 }
548 }
549 }
550 }
551
552 buffer[nc] = '\0';
[b67c7d64]553 str = wstr_to_astr(buffer);
[1352fc1]554
[9f1362d4]555 console_set_style(con, STYLE_NORMAL);
[1352fc1]556
557 return str;
558}
559
[3052ff4]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
[1352fc1]629 if (fclose(f) != EOK)
630 return EIO;
[3052ff4]631
632 return EOK;
633}
634
[0902edfe]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
[3052ff4]671static void pane_text_display(void)
672{
673 int sh_rows, rows;
674
675 sheet_get_num_rows(&doc.sh, &sh_rows);
676 rows = min(sh_rows - pane.sh_row + 1, pane.rows);
677
678 /* Draw rows from the sheet. */
679
[9f1362d4]680 console_set_pos(con, 0, 0);
[3052ff4]681 pane_row_range_display(0, rows);
682
683 /* Clear the remaining rows if file is short. */
[9f1362d4]684
685 int i;
686 ipcarg_t j;
[3052ff4]687 for (i = rows; i < pane.rows; ++i) {
[9f1362d4]688 console_set_pos(con, 0, i);
[3052ff4]689 for (j = 0; j < scr_columns; ++j)
690 putchar(' ');
691 fflush(stdout);
692 }
693
694 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
695 pane.rflags &= ~REDRAW_ROW;
696}
697
698/** Display just the row where the caret is. */
699static void pane_row_display(void)
700{
701 spt_t caret_pt;
702 coord_t coord;
703 int ridx;
704
705 tag_get_pt(&pane.caret_pos, &caret_pt);
706 spt_get_coord(&caret_pt, &coord);
707
708 ridx = coord.row - pane.sh_row;
709 pane_row_range_display(ridx, ridx + 1);
710 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
711}
712
713static void pane_row_range_display(int r0, int r1)
714{
715 int i, j, fill;
[0f24c57]716 spt_t rb, re, dep, pt;
[3052ff4]717 coord_t rbc, rec;
718 char row_buf[ROW_BUF_SIZE];
719 wchar_t c;
720 size_t pos, size;
[36e9cd1]721 int s_column;
[0f24c57]722 coord_t csel_start, csel_end, ctmp;
723
724 /* Determine selection start and end. */
725
726 tag_get_pt(&pane.sel_start, &pt);
727 spt_get_coord(&pt, &csel_start);
728
729 tag_get_pt(&pane.caret_pos, &pt);
730 spt_get_coord(&pt, &csel_end);
731
732 if (coord_cmp(&csel_start, &csel_end) > 0) {
733 ctmp = csel_start;
734 csel_start = csel_end;
735 csel_end = ctmp;
736 }
[3052ff4]737
738 /* Draw rows from the sheet. */
739
[9f1362d4]740 console_set_pos(con, 0, 0);
[3052ff4]741 for (i = r0; i < r1; ++i) {
[99e5526]742 /* Starting point for row display */
743 rbc.row = pane.sh_row + i;
744 rbc.column = pane.sh_column;
[3052ff4]745 sheet_get_cell_pt(&doc.sh, &rbc, dir_before, &rb);
746
[99e5526]747 /* Ending point for row display */
748 rec.row = pane.sh_row + i;
749 rec.column = pane.sh_column + pane.columns;
[3052ff4]750 sheet_get_cell_pt(&doc.sh, &rec, dir_before, &re);
751
752 /* Copy the text of the row to the buffer. */
753 sheet_copy_out(&doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
754
755 /* Display text from the buffer. */
756
[0f24c57]757 if (coord_cmp(&csel_start, &rbc) <= 0 &&
758 coord_cmp(&rbc, &csel_end) < 0) {
759 fflush(stdout);
[9f1362d4]760 console_set_style(con, STYLE_SELECTED);
[0f24c57]761 fflush(stdout);
762 }
763
[9f1362d4]764 console_set_pos(con, 0, i);
[3052ff4]765 size = str_size(row_buf);
766 pos = 0;
[cd82bb1]767 s_column = pane.sh_column;
[3052ff4]768 while (pos < size) {
[36e9cd1]769 if ((csel_start.row == rbc.row) && (csel_start.column == s_column)) {
[0f24c57]770 fflush(stdout);
[9f1362d4]771 console_set_style(con, STYLE_SELECTED);
[0f24c57]772 fflush(stdout);
773 }
774
[36e9cd1]775 if ((csel_end.row == rbc.row) && (csel_end.column == s_column)) {
[0f24c57]776 fflush(stdout);
[9f1362d4]777 console_set_style(con, STYLE_NORMAL);
[0f24c57]778 fflush(stdout);
779 }
780
[3052ff4]781 c = str_decode(row_buf, &pos, size);
782 if (c != '\t') {
783 printf("%lc", c);
784 s_column += 1;
785 } else {
786 fill = 1 + ALIGN_UP(s_column, TAB_WIDTH)
787 - s_column;
788
789 for (j = 0; j < fill; ++j)
790 putchar(' ');
791 s_column += fill;
792 }
793 }
794
[36e9cd1]795 if ((csel_end.row == rbc.row) && (csel_end.column == s_column)) {
[0f24c57]796 fflush(stdout);
[9f1362d4]797 console_set_style(con, STYLE_NORMAL);
[0f24c57]798 fflush(stdout);
799 }
800
[3052ff4]801 /* Fill until the end of display area. */
802
[b077886e]803 if (str_length(row_buf) < (unsigned) scr_columns)
[3052ff4]804 fill = scr_columns - str_length(row_buf);
805 else
806 fill = 0;
807
808 for (j = 0; j < fill; ++j)
809 putchar(' ');
810 fflush(stdout);
[9f1362d4]811 console_set_style(con, STYLE_NORMAL);
[3052ff4]812 }
813
814 pane.rflags |= REDRAW_CARET;
815}
816
817/** Display pane status in the status line. */
818static void pane_status_display(void)
819{
820 spt_t caret_pt;
821 coord_t coord;
822
823 tag_get_pt(&pane.caret_pos, &caret_pt);
824 spt_get_coord(&caret_pt, &coord);
825
[a000878c]826 const char *fname = (doc.file_name != NULL) ? doc.file_name : "<unnamed>";
[1352fc1]827
[9f1362d4]828 console_set_pos(con, 0, scr_rows - 1);
829 console_set_style(con, STYLE_INVERTED);
[a000878c]830 int n = printf(" %d, %d: File '%s'. Ctrl-Q Quit Ctrl-S Save "
[1352fc1]831 "Ctrl-E Save As", coord.row, coord.column, fname);
[3052ff4]832 printf("%*s", scr_columns - 1 - n, "");
833 fflush(stdout);
[9f1362d4]834 console_set_style(con, STYLE_NORMAL);
[3052ff4]835
836 pane.rflags |= REDRAW_CARET;
837}
838
839/** Set cursor to reflect position of the caret. */
840static void pane_caret_display(void)
841{
842 spt_t caret_pt;
843 coord_t coord;
844
845 tag_get_pt(&pane.caret_pos, &caret_pt);
846
847 spt_get_coord(&caret_pt, &coord);
[9f1362d4]848 console_set_pos(con, coord.column - pane.sh_column,
[99e5526]849 coord.row - pane.sh_row);
[3052ff4]850}
851
852/** Insert a character at caret position. */
853static void insert_char(wchar_t c)
854{
855 spt_t pt;
856 char cbuf[STR_BOUNDS(1) + 1];
857 size_t offs;
858
859 tag_get_pt(&pane.caret_pos, &pt);
860
861 offs = 0;
862 chr_encode(c, cbuf, &offs, STR_BOUNDS(1) + 1);
863 cbuf[offs] = '\0';
864
865 (void) sheet_insert(&doc.sh, &pt, dir_before, cbuf);
[884b461]866
867 pane.rflags |= REDRAW_ROW;
868 if (c == '\n')
869 pane.rflags |= REDRAW_TEXT;
[3052ff4]870}
871
872/** Delete the character before the caret. */
873static void delete_char_before(void)
874{
875 spt_t sp, ep;
876 coord_t coord;
877
878 tag_get_pt(&pane.caret_pos, &ep);
879 spt_get_coord(&ep, &coord);
880
881 coord.column -= 1;
882 sheet_get_cell_pt(&doc.sh, &coord, dir_before, &sp);
883
884 (void) sheet_delete(&doc.sh, &sp, &ep);
[884b461]885
886 pane.rflags |= REDRAW_ROW;
887 if (coord.column < 1)
888 pane.rflags |= REDRAW_TEXT;
[3052ff4]889}
890
891/** Delete the character after the caret. */
892static void delete_char_after(void)
893{
894 spt_t sp, ep;
[884b461]895 coord_t sc, ec;
[3052ff4]896
897 tag_get_pt(&pane.caret_pos, &sp);
[884b461]898 spt_get_coord(&sp, &sc);
[3052ff4]899
[884b461]900 sheet_get_cell_pt(&doc.sh, &sc, dir_after, &ep);
901 spt_get_coord(&ep, &ec);
[3052ff4]902
903 (void) sheet_delete(&doc.sh, &sp, &ep);
[884b461]904
905 pane.rflags |= REDRAW_ROW;
906 if (ec.row != sc.row)
907 pane.rflags |= REDRAW_TEXT;
[3052ff4]908}
909
910/** Scroll pane after caret has moved.
911 *
912 * After modifying the position of the caret, this is called to scroll
913 * the pane to ensure that the caret is in the visible area.
914 */
915static void caret_update(void)
916{
917 spt_t pt;
918 coord_t coord;
919
920 tag_get_pt(&pane.caret_pos, &pt);
921 spt_get_coord(&pt, &coord);
922
[99e5526]923 /* Scroll pane vertically. */
[3052ff4]924
925 if (coord.row < pane.sh_row) {
926 pane.sh_row = coord.row;
927 pane.rflags |= REDRAW_TEXT;
928 }
[99e5526]929
[3052ff4]930 if (coord.row > pane.sh_row + pane.rows - 1) {
931 pane.sh_row = coord.row - pane.rows + 1;
932 pane.rflags |= REDRAW_TEXT;
933 }
934
[99e5526]935 /* Scroll pane horizontally. */
936
937 if (coord.column < pane.sh_column) {
938 pane.sh_column = coord.column;
939 pane.rflags |= REDRAW_TEXT;
940 }
[3052ff4]941
[99e5526]942 if (coord.column > pane.sh_column + pane.columns - 1) {
943 pane.sh_column = coord.column - pane.columns + 1;
944 pane.rflags |= REDRAW_TEXT;
945 }
946
947 pane.rflags |= (REDRAW_CARET | REDRAW_STATUS);
[3052ff4]948}
949
950/** Change the caret position.
951 *
952 * Moves caret relatively to the current position. Looking at the first
953 * character cell after the caret and moving by @a drow and @a dcolumn, we get
954 * to a new character cell, and thus a new character. Then we either go to the
955 * point before the the character or after it, depending on @a align_dir.
956 */
957static void caret_move(int drow, int dcolumn, enum dir_spec align_dir)
958{
959 spt_t pt;
960 coord_t coord;
961 int num_rows;
[743e17b]962 bool pure_vertical;
[3052ff4]963
964 tag_get_pt(&pane.caret_pos, &pt);
965 spt_get_coord(&pt, &coord);
966 coord.row += drow; coord.column += dcolumn;
967
968 /* Clamp coordinates. */
969 if (drow < 0 && coord.row < 1) coord.row = 1;
970 if (dcolumn < 0 && coord.column < 1) coord.column = 1;
971 if (drow > 0) {
972 sheet_get_num_rows(&doc.sh, &num_rows);
973 if (coord.row > num_rows) coord.row = num_rows;
974 }
975
[743e17b]976 /* For purely vertical movement try attaining @c ideal_column. */
977 pure_vertical = (dcolumn == 0 && align_dir == dir_before);
978 if (pure_vertical)
979 coord.column = pane.ideal_column;
980
[3052ff4]981 /*
982 * Select the point before or after the character at the designated
983 * coordinates. The character can be wider than one cell (e.g. tab).
984 */
985 sheet_get_cell_pt(&doc.sh, &coord, align_dir, &pt);
986 sheet_remove_tag(&doc.sh, &pane.caret_pos);
987 sheet_place_tag(&doc.sh, &pt, &pane.caret_pos);
988
[743e17b]989 /* For non-vertical movement set the new value for @c ideal_column. */
990 if (!pure_vertical) {
991 spt_get_coord(&pt, &coord);
992 pane.ideal_column = coord.column;
993 }
994
[3052ff4]995 caret_update();
996}
997
[0f24c57]998/** Check for non-empty selection. */
999static bool selection_active(void)
1000{
1001 return (tag_cmp(&pane.caret_pos, &pane.sel_start) != 0);
1002}
1003
[0902edfe]1004static void selection_get_points(spt_t *pa, spt_t *pb)
1005{
1006 spt_t pt;
1007
1008 tag_get_pt(&pane.sel_start, pa);
1009 tag_get_pt(&pane.caret_pos, pb);
1010
1011 if (spt_cmp(pa, pb) > 0) {
1012 pt = *pa;
1013 *pa = *pb;
1014 *pb = pt;
1015 }
1016}
1017
[0f24c57]1018/** Delete selected text. */
1019static void selection_delete(void)
1020{
1021 spt_t pa, pb;
1022 coord_t ca, cb;
1023 int rel;
1024
1025 tag_get_pt(&pane.sel_start, &pa);
1026 tag_get_pt(&pane.caret_pos, &pb);
1027 spt_get_coord(&pa, &ca);
1028 spt_get_coord(&pb, &cb);
1029 rel = coord_cmp(&ca, &cb);
1030
1031 if (rel == 0)
1032 return;
1033
1034 if (rel < 0)
1035 sheet_delete(&doc.sh, &pa, &pb);
1036 else
1037 sheet_delete(&doc.sh, &pb, &pa);
1038
1039 if (ca.row == cb.row)
1040 pane.rflags |= REDRAW_ROW;
1041 else
1042 pane.rflags |= REDRAW_TEXT;
1043}
[3052ff4]1044
[cedd33b]1045static void selection_sel_all(void)
1046{
1047 spt_t spt, ept;
1048
1049 pt_get_sof(&spt);
1050 pt_get_eof(&ept);
1051 sheet_remove_tag(&doc.sh, &pane.sel_start);
1052 sheet_place_tag(&doc.sh, &spt, &pane.sel_start);
1053 sheet_remove_tag(&doc.sh, &pane.caret_pos);
1054 sheet_place_tag(&doc.sh, &ept, &pane.caret_pos);
1055
1056 pane.rflags |= REDRAW_TEXT;
[cd82bb1]1057 caret_update();
[cedd33b]1058}
1059
[0902edfe]1060static void selection_copy(void)
1061{
1062 spt_t pa, pb;
1063 char *str;
1064
1065 selection_get_points(&pa, &pb);
1066 str = range_get_str(&pa, &pb);
1067 if (str == NULL || clipboard_put_str(str) != EOK) {
1068 status_display("Copying to clipboard failed!");
1069 }
1070 free(str);
1071}
1072
1073static void insert_clipboard_data(void)
1074{
1075 char *str;
1076 size_t off;
1077 wchar_t c;
1078 int rc;
1079
1080 rc = clipboard_get_str(&str);
1081 if (rc != EOK || str == NULL)
1082 return;
1083
1084 off = 0;
1085
1086 while (true) {
1087 c = str_decode(str, &off, STR_NO_LIMIT);
1088 if (c == '\0')
1089 break;
1090
1091 insert_char(c);
1092 }
1093
1094 free(str);
1095}
1096
[3052ff4]1097/** Get start-of-file s-point. */
1098static void pt_get_sof(spt_t *pt)
1099{
1100 coord_t coord;
1101
1102 coord.row = coord.column = 1;
1103 sheet_get_cell_pt(&doc.sh, &coord, dir_before, pt);
1104}
1105
1106/** Get end-of-file s-point. */
1107static void pt_get_eof(spt_t *pt)
1108{
1109 coord_t coord;
1110 int num_rows;
1111
1112 sheet_get_num_rows(&doc.sh, &num_rows);
[00413c5c]1113 coord.row = num_rows + 1;
[3052ff4]1114 coord.column = 1;
1115
1116 sheet_get_cell_pt(&doc.sh, &coord, dir_after, pt);
1117}
1118
[0f24c57]1119/** Compare tags. */
1120static int tag_cmp(tag_t const *a, tag_t const *b)
1121{
1122 spt_t pa, pb;
1123
1124 tag_get_pt(a, &pa);
1125 tag_get_pt(b, &pb);
1126
1127 return spt_cmp(&pa, &pb);
1128}
1129
1130/** Compare s-points. */
1131static int spt_cmp(spt_t const *a, spt_t const *b)
1132{
1133 coord_t ca, cb;
1134
1135 spt_get_coord(a, &ca);
1136 spt_get_coord(b, &cb);
1137
1138 return coord_cmp(&ca, &cb);
1139}
1140
1141/** Compare coordinats. */
1142static int coord_cmp(coord_t const *a, coord_t const *b)
1143{
1144 if (a->row - b->row != 0)
1145 return a->row - b->row;
1146
1147 return a->column - b->column;
1148}
1149
[3052ff4]1150/** Display text in the status line. */
1151static void status_display(char const *str)
1152{
[9f1362d4]1153 console_set_pos(con, 0, scr_rows - 1);
1154 console_set_style(con, STYLE_INVERTED);
[3052ff4]1155 printf(" %*s ", -(scr_columns - 3), str);
1156 fflush(stdout);
[9f1362d4]1157 console_set_style(con, STYLE_NORMAL);
[3052ff4]1158
1159 pane.rflags |= REDRAW_CARET;
1160}
1161
1162/** @}
1163 */
Note: See TracBrowser for help on using the repository browser.