source: mainline/uspace/app/edit/edit.c@ 69cf3a4

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

Make sheet_t opaque.

  • Property mode set to 100644
File size: 30.3 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;
[69cf3a4]93 sheet_t *sh;
[3052ff4]94} doc_t;
95
[79ae36dd]96static console_ctrl_t *con;
[3052ff4]97static doc_t doc;
98static bool done;
99static pane_t pane;
[8190e63]100static bool cursor_visible;
[3052ff4]101
[96b02eb9]102static sysarg_t scr_rows;
103static sysarg_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
[79ae36dd]117static void key_handle_unmod(kbd_event_t const *ev);
118static void key_handle_ctrl(kbd_event_t const *ev);
119static void key_handle_shift(kbd_event_t const *ev);
[8f6bffdd]120static void key_handle_shift_ctrl(kbd_event_t const *ev);
[0f24c57]121static void key_handle_movement(unsigned int key, bool shift);
122
[3052ff4]123static int file_save(char const *fname);
[1352fc1]124static void file_save_as(void);
[3052ff4]125static int file_insert(char *fname);
126static int file_save_range(char const *fname, spt_t const *spos,
127 spt_t const *epos);
[0902edfe]128static char *range_get_str(spt_t const *spos, spt_t const *epos);
[0f24c57]129
[b8b742e]130static char *prompt(char const *prompt, char const *init_value);
131
[3052ff4]132static void pane_text_display(void);
133static void pane_row_display(void);
134static void pane_row_range_display(int r0, int r1);
135static void pane_status_display(void);
136static void pane_caret_display(void);
[0f24c57]137
[3052ff4]138static void insert_char(wchar_t c);
139static void delete_char_before(void);
140static void delete_char_after(void);
141static void caret_update(void);
142static void caret_move(int drow, int dcolumn, enum dir_spec align_dir);
[8f6bffdd]143static void caret_move_word_left(void);
144static void caret_move_word_right(void);
[b8b742e]145static void caret_move_to_line(int row);
146static void caret_go_to_line_ask(void);
[0f24c57]147
148static bool selection_active(void);
[cedd33b]149static void selection_sel_all(void);
[8f6bffdd]150static void selection_sel_range(spt_t pa, spt_t pb);
151static void selection_sel_prev_word(void);
152static void selection_sel_next_word(void);
[0902edfe]153static void selection_get_points(spt_t *pa, spt_t *pb);
[0f24c57]154static void selection_delete(void);
[0902edfe]155static void selection_copy(void);
156static void insert_clipboard_data(void);
[0f24c57]157
[3052ff4]158static void pt_get_sof(spt_t *pt);
159static void pt_get_eof(spt_t *pt);
[8f6bffdd]160static void pt_get_sol(spt_t *cpt, spt_t *spt);
161static void pt_get_eol(spt_t *cpt, spt_t *ept);
162static bool pt_is_word_beginning(spt_t *pt);
163static bool pt_is_delimiter(spt_t *pt);
164static bool pt_is_punctuation(spt_t *pt);
[0f24c57]165static int tag_cmp(tag_t const *a, tag_t const *b);
166static int spt_cmp(spt_t const *a, spt_t const *b);
167static int coord_cmp(coord_t const *a, coord_t const *b);
168
[3052ff4]169static void status_display(char const *str);
170
171
172int main(int argc, char *argv[])
173{
[79ae36dd]174 kbd_event_t ev;
[3052ff4]175 coord_t coord;
176 bool new_file;
[69cf3a4]177 int rc;
[3052ff4]178
179 spt_t pt;
180
[79ae36dd]181 con = console_init(stdin, stdout);
[3052ff4]182 console_clear(con);
183
184 console_get_size(con, &scr_columns, &scr_rows);
185
186 pane.rows = scr_rows - 1;
[99e5526]187 pane.columns = scr_columns;
[3052ff4]188 pane.sh_row = 1;
[99e5526]189 pane.sh_column = 1;
[3052ff4]190
191 /* Start with an empty sheet. */
[69cf3a4]192 rc = sheet_create(&doc.sh);
193 if (rc != EOK) {
194 printf("Out of memory.\n");
195 return -1;
196 }
[3052ff4]197
198 /* Place caret at the beginning of file. */
199 coord.row = coord.column = 1;
[69cf3a4]200 sheet_get_cell_pt(doc.sh, &coord, dir_before, &pt);
201 sheet_place_tag(doc.sh, &pt, &pane.caret_pos);
[743e17b]202 pane.ideal_column = coord.column;
[3052ff4]203
204 if (argc == 2) {
[1352fc1]205 doc.file_name = str_dup(argv[1]);
[3052ff4]206 } else if (argc > 1) {
207 printf("Invalid arguments.\n");
208 return -2;
209 } else {
[1352fc1]210 doc.file_name = NULL;
[3052ff4]211 }
212
213 new_file = false;
214
[1352fc1]215 if (doc.file_name == NULL || file_insert(doc.file_name) != EOK)
[3052ff4]216 new_file = true;
217
218 /* Move to beginning of file. */
219 caret_move(-ED_INFTY, -ED_INFTY, dir_before);
220
[0f24c57]221 /* Place selection start tag. */
222 tag_get_pt(&pane.caret_pos, &pt);
[69cf3a4]223 sheet_place_tag(doc.sh, &pt, &pane.sel_start);
[0f24c57]224
[3052ff4]225 /* Initial display */
[8190e63]226 cursor_visible = true;
227
228 cursor_hide();
[3052ff4]229 console_clear(con);
230 pane_text_display();
231 pane_status_display();
[1352fc1]232 if (new_file && doc.file_name != NULL)
233 status_display("File not found. Starting empty file.");
[3052ff4]234 pane_caret_display();
[8190e63]235 cursor_show();
[3052ff4]236
237 done = false;
238
239 while (!done) {
[79ae36dd]240 console_get_kbd_event(con, &ev);
[3052ff4]241 pane.rflags = 0;
242
243 if (ev.type == KEY_PRESS) {
244 /* Handle key press. */
245 if (((ev.mods & KM_ALT) == 0) &&
[0f24c57]246 ((ev.mods & KM_SHIFT) == 0) &&
[3052ff4]247 (ev.mods & KM_CTRL) != 0) {
248 key_handle_ctrl(&ev);
[0f24c57]249 } else if (((ev.mods & KM_ALT) == 0) &&
250 ((ev.mods & KM_CTRL) == 0) &&
251 (ev.mods & KM_SHIFT) != 0) {
252 key_handle_shift(&ev);
[8f6bffdd]253 } else if (((ev.mods & KM_ALT) == 0) &&
254 ((ev.mods & KM_CTRL) != 0) &&
255 (ev.mods & KM_SHIFT) != 0) {
256 key_handle_shift_ctrl(&ev);
[0f24c57]257 } else if ((ev.mods & (KM_CTRL | KM_ALT | KM_SHIFT)) == 0) {
[3052ff4]258 key_handle_unmod(&ev);
259 }
260 }
261
262 /* Redraw as necessary. */
263
[8190e63]264 cursor_hide();
265
[3052ff4]266 if (pane.rflags & REDRAW_TEXT)
267 pane_text_display();
268 if (pane.rflags & REDRAW_ROW)
269 pane_row_display();
270 if (pane.rflags & REDRAW_STATUS)
271 pane_status_display();
272 if (pane.rflags & REDRAW_CARET)
273 pane_caret_display();
[8190e63]274
275 cursor_show();
[3052ff4]276 }
277
278 console_clear(con);
279
280 return 0;
281}
282
[8190e63]283static void cursor_show(void)
284{
285 cursor_setvis(true);
286}
287
288static void cursor_hide(void)
289{
290 cursor_setvis(false);
291}
292
293static void cursor_setvis(bool visible)
294{
295 if (cursor_visible != visible) {
296 console_cursor_visibility(con, visible);
297 cursor_visible = visible;
298 }
299}
300
[3052ff4]301/** Handle key without modifier. */
[79ae36dd]302static void key_handle_unmod(kbd_event_t const *ev)
[3052ff4]303{
304 switch (ev->key) {
305 case KC_ENTER:
[0f24c57]306 selection_delete();
[3052ff4]307 insert_char('\n');
308 caret_update();
309 break;
310 case KC_LEFT:
311 case KC_RIGHT:
312 case KC_UP:
313 case KC_DOWN:
314 case KC_HOME:
315 case KC_END:
316 case KC_PAGE_UP:
317 case KC_PAGE_DOWN:
[0f24c57]318 key_handle_movement(ev->key, false);
[3052ff4]319 break;
320 case KC_BACKSPACE:
[0f24c57]321 if (selection_active())
322 selection_delete();
323 else
324 delete_char_before();
[3052ff4]325 caret_update();
326 break;
327 case KC_DELETE:
[0f24c57]328 if (selection_active())
329 selection_delete();
330 else
331 delete_char_after();
[3052ff4]332 caret_update();
333 break;
334 default:
335 if (ev->c >= 32 || ev->c == '\t') {
[0f24c57]336 selection_delete();
337 insert_char(ev->c);
338 caret_update();
339 }
340 break;
341 }
342}
343
344/** Handle Shift-key combination. */
[79ae36dd]345static void key_handle_shift(kbd_event_t const *ev)
[0f24c57]346{
347 switch (ev->key) {
348 case KC_LEFT:
349 case KC_RIGHT:
350 case KC_UP:
351 case KC_DOWN:
352 case KC_HOME:
353 case KC_END:
354 case KC_PAGE_UP:
355 case KC_PAGE_DOWN:
356 key_handle_movement(ev->key, true);
357 break;
358 default:
359 if (ev->c >= 32 || ev->c == '\t') {
360 selection_delete();
[3052ff4]361 insert_char(ev->c);
362 caret_update();
363 }
364 break;
365 }
366}
367
368/** Handle Ctrl-key combination. */
[79ae36dd]369static void key_handle_ctrl(kbd_event_t const *ev)
[3052ff4]370{
371 switch (ev->key) {
372 case KC_Q:
373 done = true;
374 break;
375 case KC_S:
[1352fc1]376 if (doc.file_name != NULL)
377 file_save(doc.file_name);
378 else
379 file_save_as();
380 break;
381 case KC_E:
382 file_save_as();
[3052ff4]383 break;
[0902edfe]384 case KC_C:
385 selection_copy();
386 break;
387 case KC_V:
388 selection_delete();
389 insert_clipboard_data();
390 pane.rflags |= REDRAW_TEXT;
391 caret_update();
392 break;
[cedd33b]393 case KC_X:
394 selection_copy();
395 selection_delete();
396 pane.rflags |= REDRAW_TEXT;
397 caret_update();
398 break;
399 case KC_A:
400 selection_sel_all();
401 break;
[8f6bffdd]402 case KC_W:
403 if (selection_active())
404 break;
405 selection_sel_prev_word();
406 selection_delete();
407 break;
408 case KC_RIGHT:
409 caret_move_word_right();
410 break;
411 case KC_LEFT:
412 caret_move_word_left();
413 break;
[b8b742e]414 case KC_L:
415 caret_go_to_line_ask();
416 break;
[8f6bffdd]417 default:
418 break;
419 }
420}
421
422static void key_handle_shift_ctrl(kbd_event_t const *ev)
423{
424 switch(ev->key) {
425 case KC_LEFT:
426 selection_sel_prev_word();
427 break;
428 case KC_RIGHT:
429 selection_sel_next_word();
430 break;
[3052ff4]431 default:
432 break;
433 }
434}
435
[c8444d8]436/** Move caret while preserving or resetting selection. */
437static void caret_movement(int drow, int dcolumn, enum dir_spec align_dir,
438 bool select)
[0f24c57]439{
440 spt_t pt;
441 spt_t caret_pt;
442 coord_t c_old, c_new;
443 bool had_sel;
444
445 /* Check if we had selection before. */
446 tag_get_pt(&pane.caret_pos, &caret_pt);
447 tag_get_pt(&pane.sel_start, &pt);
448 had_sel = !spt_equal(&caret_pt, &pt);
449
[c8444d8]450 caret_move(drow, dcolumn, align_dir);
[0f24c57]451
452 if (select == false) {
453 /* Move sel_start to the same point as caret. */
[69cf3a4]454 sheet_remove_tag(doc.sh, &pane.sel_start);
[0f24c57]455 tag_get_pt(&pane.caret_pos, &pt);
[69cf3a4]456 sheet_place_tag(doc.sh, &pt, &pane.sel_start);
[0f24c57]457 }
458
459 if (select) {
460 tag_get_pt(&pane.caret_pos, &pt);
461 spt_get_coord(&caret_pt, &c_old);
462 spt_get_coord(&pt, &c_new);
463
464 if (c_old.row == c_new.row)
465 pane.rflags |= REDRAW_ROW;
466 else
467 pane.rflags |= REDRAW_TEXT;
468
469 } else if (had_sel == true) {
470 /* Redraw because text was unselected. */
471 pane.rflags |= REDRAW_TEXT;
472 }
473}
474
[c8444d8]475static void key_handle_movement(unsigned int key, bool select)
476{
477 switch (key) {
478 case KC_LEFT:
479 caret_movement(0, -1, dir_before, select);
480 break;
481 case KC_RIGHT:
482 caret_movement(0, 0, dir_after, select);
483 break;
484 case KC_UP:
485 caret_movement(-1, 0, dir_before, select);
486 break;
487 case KC_DOWN:
488 caret_movement(+1, 0, dir_before, select);
489 break;
490 case KC_HOME:
491 caret_movement(0, -ED_INFTY, dir_before, select);
492 break;
493 case KC_END:
494 caret_movement(0, +ED_INFTY, dir_before, select);
495 break;
496 case KC_PAGE_UP:
497 caret_movement(-pane.rows, 0, dir_before, select);
498 break;
499 case KC_PAGE_DOWN:
500 caret_movement(+pane.rows, 0, dir_before, select);
501 break;
502 default:
503 break;
504 }
505}
506
[3052ff4]507/** Save the document. */
508static int file_save(char const *fname)
509{
510 spt_t sp, ep;
511 int rc;
512
513 status_display("Saving...");
514 pt_get_sof(&sp);
515 pt_get_eof(&ep);
516
517 rc = file_save_range(fname, &sp, &ep);
[1352fc1]518
519 switch (rc) {
520 case EINVAL:
521 status_display("Error opening file!");
522 break;
523 case EIO:
524 status_display("Error writing data!");
525 break;
526 default:
527 status_display("File saved.");
528 break;
529 }
[3052ff4]530
531 return rc;
532}
533
[1352fc1]534/** Change document name and save. */
535static void file_save_as(void)
536{
[a000878c]537 const char *old_fname = (doc.file_name != NULL) ? doc.file_name : "";
538 char *fname;
539
[b8b742e]540 fname = prompt("Save As", old_fname);
[1352fc1]541 if (fname == NULL) {
542 status_display("Save cancelled.");
543 return;
544 }
545
[a000878c]546 int rc = file_save(fname);
[1352fc1]547 if (rc != EOK)
548 return;
549
550 if (doc.file_name != NULL)
551 free(doc.file_name);
552 doc.file_name = fname;
553}
554
[b8b742e]555/** Ask for a string. */
556static char *prompt(char const *prompt, char const *init_value)
[1352fc1]557{
[79ae36dd]558 kbd_event_t ev;
[1352fc1]559 char *str;
[ba26129]560 wchar_t buffer[INFNAME_MAX_LEN + 1];
561 int max_len;
[1352fc1]562 int nc;
563 bool done;
564
565 asprintf(&str, "%s: %s", prompt, init_value);
566 status_display(str);
[9f1362d4]567 console_set_pos(con, 1 + str_length(str), scr_rows - 1);
[1352fc1]568 free(str);
569
[9f1362d4]570 console_set_style(con, STYLE_INVERTED);
[1352fc1]571
[ba26129]572 max_len = min(INFNAME_MAX_LEN, scr_columns - 4 - str_length(prompt));
573 str_to_wstr(buffer, max_len + 1, init_value);
[1352fc1]574 nc = wstr_length(buffer);
575 done = false;
576
577 while (!done) {
[79ae36dd]578 console_get_kbd_event(con, &ev);
[1352fc1]579
580 if (ev.type == KEY_PRESS) {
581 /* Handle key press. */
582 if (((ev.mods & KM_ALT) == 0) &&
583 (ev.mods & KM_CTRL) != 0) {
584 ;
585 } else if ((ev.mods & (KM_CTRL | KM_ALT)) == 0) {
586 switch (ev.key) {
587 case KC_ESCAPE:
588 return NULL;
589 case KC_BACKSPACE:
590 if (nc > 0) {
591 putchar('\b');
[79ae36dd]592 console_flush(con);
[1352fc1]593 --nc;
594 }
595 break;
596 case KC_ENTER:
597 done = true;
598 break;
599 default:
[ba26129]600 if (ev.c >= 32 && nc < max_len) {
[1352fc1]601 putchar(ev.c);
[79ae36dd]602 console_flush(con);
[1352fc1]603 buffer[nc++] = ev.c;
604 }
605 break;
606 }
607 }
608 }
609 }
610
611 buffer[nc] = '\0';
[b67c7d64]612 str = wstr_to_astr(buffer);
[1352fc1]613
[9f1362d4]614 console_set_style(con, STYLE_NORMAL);
[1352fc1]615
616 return str;
617}
618
[3052ff4]619/** Insert file at caret position.
620 *
621 * Reads in the contents of a file and inserts them at the current position
622 * of the caret.
623 */
624static int file_insert(char *fname)
625{
626 FILE *f;
627 wchar_t c;
628 char buf[BUF_SIZE];
629 int bcnt;
630 int n_read;
631 size_t off;
632
633 f = fopen(fname, "rt");
634 if (f == NULL)
635 return EINVAL;
636
637 bcnt = 0;
638
639 while (true) {
640 if (bcnt < STR_BOUNDS(1)) {
641 n_read = fread(buf + bcnt, 1, BUF_SIZE - bcnt, f);
642 bcnt += n_read;
643 }
644
645 off = 0;
646 c = str_decode(buf, &off, bcnt);
647 if (c == '\0')
648 break;
649
650 bcnt -= off;
651 memcpy(buf, buf + off, bcnt);
652
653 insert_char(c);
654 }
655
656 fclose(f);
657
658 return EOK;
659}
660
661/** Save a range of text into a file. */
662static int file_save_range(char const *fname, spt_t const *spos,
663 spt_t const *epos)
664{
665 FILE *f;
666 char buf[BUF_SIZE];
667 spt_t sp, bep;
668 size_t bytes, n_written;
669
670 f = fopen(fname, "wt");
671 if (f == NULL)
672 return EINVAL;
673
674 sp = *spos;
675
676 do {
[69cf3a4]677 sheet_copy_out(doc.sh, &sp, epos, buf, BUF_SIZE, &bep);
[3052ff4]678 bytes = str_size(buf);
679
680 n_written = fwrite(buf, 1, bytes, f);
681 if (n_written != bytes) {
682 return EIO;
683 }
684
685 sp = bep;
686 } while (!spt_equal(&bep, epos));
687
[1352fc1]688 if (fclose(f) != EOK)
689 return EIO;
[3052ff4]690
691 return EOK;
692}
693
[0902edfe]694/** Return contents of range as a new string. */
695static char *range_get_str(spt_t const *spos, spt_t const *epos)
696{
697 char *buf;
698 spt_t sp, bep;
699 size_t bytes;
700 size_t buf_size, bpos;
701
702 buf_size = 1;
703
704 buf = malloc(buf_size);
705 if (buf == NULL)
706 return NULL;
707
708 bpos = 0;
709 sp = *spos;
710
711 while (true) {
[69cf3a4]712 sheet_copy_out(doc.sh, &sp, epos, &buf[bpos], buf_size - bpos,
[0902edfe]713 &bep);
714 bytes = str_size(&buf[bpos]);
715 bpos += bytes;
716 sp = bep;
717
718 if (spt_equal(&bep, epos))
719 break;
720
721 buf_size *= 2;
722 buf = realloc(buf, buf_size);
723 if (buf == NULL)
724 return NULL;
725 }
726
727 return buf;
728}
729
[3052ff4]730static void pane_text_display(void)
731{
732 int sh_rows, rows;
733
[69cf3a4]734 sheet_get_num_rows(doc.sh, &sh_rows);
[3052ff4]735 rows = min(sh_rows - pane.sh_row + 1, pane.rows);
736
737 /* Draw rows from the sheet. */
738
[9f1362d4]739 console_set_pos(con, 0, 0);
[3052ff4]740 pane_row_range_display(0, rows);
741
742 /* Clear the remaining rows if file is short. */
[9f1362d4]743
744 int i;
[96b02eb9]745 sysarg_t j;
[3052ff4]746 for (i = rows; i < pane.rows; ++i) {
[9f1362d4]747 console_set_pos(con, 0, i);
[3052ff4]748 for (j = 0; j < scr_columns; ++j)
749 putchar(' ');
[79ae36dd]750 console_flush(con);
[3052ff4]751 }
752
753 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
754 pane.rflags &= ~REDRAW_ROW;
755}
756
757/** Display just the row where the caret is. */
758static void pane_row_display(void)
759{
760 spt_t caret_pt;
761 coord_t coord;
762 int ridx;
763
764 tag_get_pt(&pane.caret_pos, &caret_pt);
765 spt_get_coord(&caret_pt, &coord);
766
767 ridx = coord.row - pane.sh_row;
768 pane_row_range_display(ridx, ridx + 1);
769 pane.rflags |= (REDRAW_STATUS | REDRAW_CARET);
770}
771
772static void pane_row_range_display(int r0, int r1)
773{
774 int i, j, fill;
[0f24c57]775 spt_t rb, re, dep, pt;
[3052ff4]776 coord_t rbc, rec;
777 char row_buf[ROW_BUF_SIZE];
778 wchar_t c;
779 size_t pos, size;
[36e9cd1]780 int s_column;
[0f24c57]781 coord_t csel_start, csel_end, ctmp;
782
783 /* Determine selection start and end. */
784
785 tag_get_pt(&pane.sel_start, &pt);
786 spt_get_coord(&pt, &csel_start);
787
788 tag_get_pt(&pane.caret_pos, &pt);
789 spt_get_coord(&pt, &csel_end);
790
791 if (coord_cmp(&csel_start, &csel_end) > 0) {
792 ctmp = csel_start;
793 csel_start = csel_end;
794 csel_end = ctmp;
795 }
[3052ff4]796
797 /* Draw rows from the sheet. */
798
[9f1362d4]799 console_set_pos(con, 0, 0);
[3052ff4]800 for (i = r0; i < r1; ++i) {
[99e5526]801 /* Starting point for row display */
802 rbc.row = pane.sh_row + i;
803 rbc.column = pane.sh_column;
[69cf3a4]804 sheet_get_cell_pt(doc.sh, &rbc, dir_before, &rb);
[3052ff4]805
[99e5526]806 /* Ending point for row display */
807 rec.row = pane.sh_row + i;
808 rec.column = pane.sh_column + pane.columns;
[69cf3a4]809 sheet_get_cell_pt(doc.sh, &rec, dir_before, &re);
[3052ff4]810
811 /* Copy the text of the row to the buffer. */
[69cf3a4]812 sheet_copy_out(doc.sh, &rb, &re, row_buf, ROW_BUF_SIZE, &dep);
[3052ff4]813
814 /* Display text from the buffer. */
815
[0f24c57]816 if (coord_cmp(&csel_start, &rbc) <= 0 &&
817 coord_cmp(&rbc, &csel_end) < 0) {
[79ae36dd]818 console_flush(con);
[9f1362d4]819 console_set_style(con, STYLE_SELECTED);
[79ae36dd]820 console_flush(con);
[0f24c57]821 }
822
[9f1362d4]823 console_set_pos(con, 0, i);
[3052ff4]824 size = str_size(row_buf);
825 pos = 0;
[cd82bb1]826 s_column = pane.sh_column;
[3052ff4]827 while (pos < size) {
[36e9cd1]828 if ((csel_start.row == rbc.row) && (csel_start.column == s_column)) {
[79ae36dd]829 console_flush(con);
[9f1362d4]830 console_set_style(con, STYLE_SELECTED);
[79ae36dd]831 console_flush(con);
[0f24c57]832 }
833
[36e9cd1]834 if ((csel_end.row == rbc.row) && (csel_end.column == s_column)) {
[79ae36dd]835 console_flush(con);
[9f1362d4]836 console_set_style(con, STYLE_NORMAL);
[79ae36dd]837 console_flush(con);
[0f24c57]838 }
839
[3052ff4]840 c = str_decode(row_buf, &pos, size);
841 if (c != '\t') {
[7e752b2]842 printf("%lc", (wint_t) c);
[3052ff4]843 s_column += 1;
844 } else {
845 fill = 1 + ALIGN_UP(s_column, TAB_WIDTH)
846 - s_column;
847
848 for (j = 0; j < fill; ++j)
849 putchar(' ');
850 s_column += fill;
851 }
852 }
853
[36e9cd1]854 if ((csel_end.row == rbc.row) && (csel_end.column == s_column)) {
[79ae36dd]855 console_flush(con);
[9f1362d4]856 console_set_style(con, STYLE_NORMAL);
[79ae36dd]857 console_flush(con);
[0f24c57]858 }
859
[3052ff4]860 /* Fill until the end of display area. */
861
[c5a6076]862 if ((unsigned)s_column - 1 < scr_columns)
863 fill = scr_columns - (s_column - 1);
[3052ff4]864 else
865 fill = 0;
866
867 for (j = 0; j < fill; ++j)
868 putchar(' ');
[79ae36dd]869 console_flush(con);
[9f1362d4]870 console_set_style(con, STYLE_NORMAL);
[3052ff4]871 }
872
873 pane.rflags |= REDRAW_CARET;
874}
875
876/** Display pane status in the status line. */
877static void pane_status_display(void)
878{
879 spt_t caret_pt;
880 coord_t coord;
[69cf3a4]881 int last_row;
[3052ff4]882
883 tag_get_pt(&pane.caret_pos, &caret_pt);
884 spt_get_coord(&caret_pt, &coord);
885
[69cf3a4]886 sheet_get_num_rows(doc.sh, &last_row);
887
[a000878c]888 const char *fname = (doc.file_name != NULL) ? doc.file_name : "<unnamed>";
[1352fc1]889
[9f1362d4]890 console_set_pos(con, 0, scr_rows - 1);
891 console_set_style(con, STYLE_INVERTED);
[69cf3a4]892 int n = printf(" %d, %d (%d): File '%s'. Ctrl-Q Quit Ctrl-S Save "
893 "Ctrl-E Save As", coord.row, coord.column, last_row, fname);
[7e752b2]894
895 int pos = scr_columns - 1 - n;
896 printf("%*s", pos, "");
[79ae36dd]897 console_flush(con);
[9f1362d4]898 console_set_style(con, STYLE_NORMAL);
[3052ff4]899
900 pane.rflags |= REDRAW_CARET;
901}
902
903/** Set cursor to reflect position of the caret. */
904static void pane_caret_display(void)
905{
906 spt_t caret_pt;
907 coord_t coord;
908
909 tag_get_pt(&pane.caret_pos, &caret_pt);
910
911 spt_get_coord(&caret_pt, &coord);
[9f1362d4]912 console_set_pos(con, coord.column - pane.sh_column,
[99e5526]913 coord.row - pane.sh_row);
[3052ff4]914}
915
916/** Insert a character at caret position. */
917static void insert_char(wchar_t c)
918{
919 spt_t pt;
920 char cbuf[STR_BOUNDS(1) + 1];
921 size_t offs;
922
923 tag_get_pt(&pane.caret_pos, &pt);
924
925 offs = 0;
926 chr_encode(c, cbuf, &offs, STR_BOUNDS(1) + 1);
927 cbuf[offs] = '\0';
928
[69cf3a4]929 (void) sheet_insert(doc.sh, &pt, dir_before, cbuf);
[884b461]930
931 pane.rflags |= REDRAW_ROW;
932 if (c == '\n')
933 pane.rflags |= REDRAW_TEXT;
[3052ff4]934}
935
936/** Delete the character before the caret. */
937static void delete_char_before(void)
938{
939 spt_t sp, ep;
940 coord_t coord;
941
942 tag_get_pt(&pane.caret_pos, &ep);
943 spt_get_coord(&ep, &coord);
944
945 coord.column -= 1;
[69cf3a4]946 sheet_get_cell_pt(doc.sh, &coord, dir_before, &sp);
[3052ff4]947
[69cf3a4]948 (void) sheet_delete(doc.sh, &sp, &ep);
[884b461]949
950 pane.rflags |= REDRAW_ROW;
951 if (coord.column < 1)
952 pane.rflags |= REDRAW_TEXT;
[3052ff4]953}
954
955/** Delete the character after the caret. */
956static void delete_char_after(void)
957{
958 spt_t sp, ep;
[884b461]959 coord_t sc, ec;
[3052ff4]960
961 tag_get_pt(&pane.caret_pos, &sp);
[884b461]962 spt_get_coord(&sp, &sc);
[3052ff4]963
[69cf3a4]964 sheet_get_cell_pt(doc.sh, &sc, dir_after, &ep);
[884b461]965 spt_get_coord(&ep, &ec);
[3052ff4]966
[69cf3a4]967 (void) sheet_delete(doc.sh, &sp, &ep);
[884b461]968
969 pane.rflags |= REDRAW_ROW;
970 if (ec.row != sc.row)
971 pane.rflags |= REDRAW_TEXT;
[3052ff4]972}
973
974/** Scroll pane after caret has moved.
975 *
976 * After modifying the position of the caret, this is called to scroll
977 * the pane to ensure that the caret is in the visible area.
978 */
979static void caret_update(void)
980{
981 spt_t pt;
982 coord_t coord;
983
984 tag_get_pt(&pane.caret_pos, &pt);
985 spt_get_coord(&pt, &coord);
986
[99e5526]987 /* Scroll pane vertically. */
[3052ff4]988
989 if (coord.row < pane.sh_row) {
990 pane.sh_row = coord.row;
991 pane.rflags |= REDRAW_TEXT;
992 }
[99e5526]993
[3052ff4]994 if (coord.row > pane.sh_row + pane.rows - 1) {
995 pane.sh_row = coord.row - pane.rows + 1;
996 pane.rflags |= REDRAW_TEXT;
997 }
998
[99e5526]999 /* Scroll pane horizontally. */
1000
1001 if (coord.column < pane.sh_column) {
1002 pane.sh_column = coord.column;
1003 pane.rflags |= REDRAW_TEXT;
1004 }
[3052ff4]1005
[99e5526]1006 if (coord.column > pane.sh_column + pane.columns - 1) {
1007 pane.sh_column = coord.column - pane.columns + 1;
1008 pane.rflags |= REDRAW_TEXT;
1009 }
1010
1011 pane.rflags |= (REDRAW_CARET | REDRAW_STATUS);
[3052ff4]1012}
1013
1014/** Change the caret position.
1015 *
1016 * Moves caret relatively to the current position. Looking at the first
1017 * character cell after the caret and moving by @a drow and @a dcolumn, we get
1018 * to a new character cell, and thus a new character. Then we either go to the
1019 * point before the the character or after it, depending on @a align_dir.
1020 */
1021static void caret_move(int drow, int dcolumn, enum dir_spec align_dir)
1022{
1023 spt_t pt;
1024 coord_t coord;
1025 int num_rows;
[743e17b]1026 bool pure_vertical;
[3052ff4]1027
1028 tag_get_pt(&pane.caret_pos, &pt);
1029 spt_get_coord(&pt, &coord);
1030 coord.row += drow; coord.column += dcolumn;
1031
1032 /* Clamp coordinates. */
1033 if (drow < 0 && coord.row < 1) coord.row = 1;
[8f6bffdd]1034 if (dcolumn < 0 && coord.column < 1) {
1035 if (coord.row < 2)
1036 coord.column = 1;
1037 else {
1038 coord.row--;
[69cf3a4]1039 sheet_get_row_width(doc.sh, coord.row, &coord.column);
[8f6bffdd]1040 }
1041 }
[3052ff4]1042 if (drow > 0) {
[69cf3a4]1043 sheet_get_num_rows(doc.sh, &num_rows);
[3052ff4]1044 if (coord.row > num_rows) coord.row = num_rows;
1045 }
1046
[743e17b]1047 /* For purely vertical movement try attaining @c ideal_column. */
1048 pure_vertical = (dcolumn == 0 && align_dir == dir_before);
1049 if (pure_vertical)
1050 coord.column = pane.ideal_column;
1051
[3052ff4]1052 /*
1053 * Select the point before or after the character at the designated
1054 * coordinates. The character can be wider than one cell (e.g. tab).
1055 */
[69cf3a4]1056 sheet_get_cell_pt(doc.sh, &coord, align_dir, &pt);
1057 sheet_remove_tag(doc.sh, &pane.caret_pos);
1058 sheet_place_tag(doc.sh, &pt, &pane.caret_pos);
[3052ff4]1059
[743e17b]1060 /* For non-vertical movement set the new value for @c ideal_column. */
1061 if (!pure_vertical) {
1062 spt_get_coord(&pt, &coord);
1063 pane.ideal_column = coord.column;
1064 }
1065
[3052ff4]1066 caret_update();
1067}
1068
[8f6bffdd]1069static void caret_move_word_left(void)
1070{
1071 spt_t pt;
1072
1073 do {
1074 caret_move(0, -1, dir_before);
1075
1076 tag_get_pt(&pane.caret_pos, &pt);
1077
[69cf3a4]1078 sheet_remove_tag(doc.sh, &pane.sel_start);
1079 sheet_place_tag(doc.sh, &pt, &pane.sel_start);
[8f6bffdd]1080 } while (!pt_is_word_beginning(&pt));
1081
1082 pane.rflags |= REDRAW_TEXT;
1083}
1084
1085static void caret_move_word_right(void)
1086{
1087 spt_t pt;
1088
1089 do {
1090 caret_move(0, 0, dir_after);
1091
1092 tag_get_pt(&pane.caret_pos, &pt);
1093
[69cf3a4]1094 sheet_remove_tag(doc.sh, &pane.sel_start);
1095 sheet_place_tag(doc.sh, &pt, &pane.sel_start);
[8f6bffdd]1096 } while (!pt_is_word_beginning(&pt));
1097
1098 pane.rflags |= REDRAW_TEXT;
1099}
1100
[b8b742e]1101/** Change the caret position to a beginning of a given line
1102 */
1103static void caret_move_to_line(int row)
1104{
1105 spt_t pt;
1106 coord_t coord;
1107
1108 tag_get_pt(&pane.caret_pos, &pt);
1109 spt_get_coord(&pt, &coord);
1110
[c8444d8]1111 caret_movement(row - coord.row, 0, dir_before, false);
[b8b742e]1112}
1113
1114/** Ask for line and go to it. */
1115static void caret_go_to_line_ask(void)
1116{
1117 char *sline;
1118
1119 sline = prompt("Go to line", "");
1120 if (sline == NULL) {
1121 status_display("Go to line cancelled.");
1122 return;
1123 }
1124
1125 char *endptr;
1126 int line = strtol(sline, &endptr, 10);
1127 if (*endptr != '\0') {
1128 status_display("Invalid number entered.");
1129 return;
1130 }
1131
1132 caret_move_to_line(line);
1133}
1134
1135
[0f24c57]1136/** Check for non-empty selection. */
1137static bool selection_active(void)
1138{
1139 return (tag_cmp(&pane.caret_pos, &pane.sel_start) != 0);
1140}
1141
[0902edfe]1142static void selection_get_points(spt_t *pa, spt_t *pb)
1143{
1144 spt_t pt;
1145
1146 tag_get_pt(&pane.sel_start, pa);
1147 tag_get_pt(&pane.caret_pos, pb);
1148
1149 if (spt_cmp(pa, pb) > 0) {
1150 pt = *pa;
1151 *pa = *pb;
1152 *pb = pt;
1153 }
1154}
1155
[0f24c57]1156/** Delete selected text. */
1157static void selection_delete(void)
1158{
1159 spt_t pa, pb;
1160 coord_t ca, cb;
1161 int rel;
1162
1163 tag_get_pt(&pane.sel_start, &pa);
1164 tag_get_pt(&pane.caret_pos, &pb);
1165 spt_get_coord(&pa, &ca);
1166 spt_get_coord(&pb, &cb);
1167 rel = coord_cmp(&ca, &cb);
1168
1169 if (rel == 0)
1170 return;
1171
1172 if (rel < 0)
[69cf3a4]1173 sheet_delete(doc.sh, &pa, &pb);
[0f24c57]1174 else
[69cf3a4]1175 sheet_delete(doc.sh, &pb, &pa);
[0f24c57]1176
1177 if (ca.row == cb.row)
1178 pane.rflags |= REDRAW_ROW;
1179 else
1180 pane.rflags |= REDRAW_TEXT;
1181}
[3052ff4]1182
[8f6bffdd]1183/** Select all text in the editor */
[cedd33b]1184static void selection_sel_all(void)
1185{
1186 spt_t spt, ept;
1187
1188 pt_get_sof(&spt);
1189 pt_get_eof(&ept);
[8f6bffdd]1190
1191 selection_sel_range(spt, ept);
1192}
1193
1194/** Select select all text in a given range with the given direction */
1195static void selection_sel_range(spt_t pa, spt_t pb)
1196{
[69cf3a4]1197 sheet_remove_tag(doc.sh, &pane.sel_start);
1198 sheet_place_tag(doc.sh, &pa, &pane.sel_start);
1199 sheet_remove_tag(doc.sh, &pane.caret_pos);
1200 sheet_place_tag(doc.sh, &pb, &pane.caret_pos);
[cedd33b]1201
1202 pane.rflags |= REDRAW_TEXT;
[cd82bb1]1203 caret_update();
[cedd33b]1204}
1205
[8f6bffdd]1206/** Add the previous word to the selection */
1207static void selection_sel_prev_word(void)
1208{
1209 spt_t cpt, wpt, spt, ept;
1210
1211 selection_get_points(&spt, &ept);
1212
1213 tag_get_pt(&pane.caret_pos, &cpt);
1214 caret_move_word_left();
1215 tag_get_pt(&pane.caret_pos, &wpt);
1216
1217 if (spt_cmp(&spt, &cpt) == 0)
1218 selection_sel_range(ept, wpt);
1219 else
1220 selection_sel_range(spt, wpt);
1221}
1222
1223/** Add the next word to the selection */
1224static void selection_sel_next_word(void)
1225{
1226 spt_t cpt, wpt, spt, ept;
1227
1228 selection_get_points(&spt, &ept);
1229
1230 tag_get_pt(&pane.caret_pos, &cpt);
1231 caret_move_word_right();
1232 tag_get_pt(&pane.caret_pos, &wpt);
1233
1234 if (spt_cmp(&ept, &cpt) == 0)
1235 selection_sel_range(spt, wpt);
1236 else
1237 selection_sel_range(ept, wpt);
1238}
1239
[0902edfe]1240static void selection_copy(void)
1241{
1242 spt_t pa, pb;
1243 char *str;
1244
1245 selection_get_points(&pa, &pb);
1246 str = range_get_str(&pa, &pb);
1247 if (str == NULL || clipboard_put_str(str) != EOK) {
1248 status_display("Copying to clipboard failed!");
1249 }
1250 free(str);
1251}
1252
1253static void insert_clipboard_data(void)
1254{
1255 char *str;
1256 size_t off;
1257 wchar_t c;
1258 int rc;
1259
1260 rc = clipboard_get_str(&str);
1261 if (rc != EOK || str == NULL)
1262 return;
1263
1264 off = 0;
1265
1266 while (true) {
1267 c = str_decode(str, &off, STR_NO_LIMIT);
1268 if (c == '\0')
1269 break;
1270
1271 insert_char(c);
1272 }
1273
1274 free(str);
1275}
1276
[3052ff4]1277/** Get start-of-file s-point. */
1278static void pt_get_sof(spt_t *pt)
1279{
1280 coord_t coord;
1281
1282 coord.row = coord.column = 1;
[69cf3a4]1283 sheet_get_cell_pt(doc.sh, &coord, dir_before, pt);
[3052ff4]1284}
1285
1286/** Get end-of-file s-point. */
1287static void pt_get_eof(spt_t *pt)
1288{
1289 coord_t coord;
1290 int num_rows;
1291
[69cf3a4]1292 sheet_get_num_rows(doc.sh, &num_rows);
[00413c5c]1293 coord.row = num_rows + 1;
[3052ff4]1294 coord.column = 1;
1295
[69cf3a4]1296 sheet_get_cell_pt(doc.sh, &coord, dir_after, pt);
[3052ff4]1297}
1298
[8f6bffdd]1299/** Get start-of-line s-point for given s-point cpt */
1300static void pt_get_sol(spt_t *cpt, spt_t *spt)
1301{
1302 coord_t coord;
1303
1304 spt_get_coord(cpt, &coord);
1305 coord.column = 1;
1306
[69cf3a4]1307 sheet_get_cell_pt(doc.sh, &coord, dir_before, spt);
[8f6bffdd]1308}
1309
1310/** Get end-of-line s-point for given s-point cpt */
1311static void pt_get_eol(spt_t *cpt, spt_t *ept)
1312{
1313 coord_t coord;
1314 int row_width;
1315
1316 spt_get_coord(cpt, &coord);
[69cf3a4]1317 sheet_get_row_width(doc.sh, coord.row, &row_width);
[8f6bffdd]1318 coord.column = row_width - 1;
1319
[69cf3a4]1320 sheet_get_cell_pt(doc.sh, &coord, dir_after, ept);
[8f6bffdd]1321}
1322
1323/** Check whether the spt is at a beginning of a word */
1324static bool pt_is_word_beginning(spt_t *pt)
1325{
1326 spt_t lp, sfp, efp, slp, elp;
1327 coord_t coord;
1328
1329 pt_get_sof(&sfp);
1330 pt_get_eof(&efp);
1331 pt_get_sol(pt, &slp);
1332 pt_get_eol(pt, &elp);
1333
1334 /* the spt is at the beginning or end of the file or line */
1335 if ((spt_cmp(&sfp, pt) == 0) || (spt_cmp(&efp, pt) == 0)
1336 || (spt_cmp(&slp, pt) == 0) || (spt_cmp(&elp, pt) == 0))
1337 return true;
1338
1339 /* the spt is a delimiter */
1340 if (pt_is_delimiter(pt))
1341 return false;
1342
1343 spt_get_coord(pt, &coord);
1344
1345 coord.column -= 1;
[69cf3a4]1346 sheet_get_cell_pt(doc.sh, &coord, dir_before, &lp);
[8f6bffdd]1347
1348 return pt_is_delimiter(&lp)
1349 || (pt_is_punctuation(pt) && !pt_is_punctuation(&lp))
1350 || (pt_is_punctuation(&lp) && !pt_is_punctuation(pt));
1351}
1352
1353static wchar_t get_first_wchar(const char *str)
1354{
1355 size_t offset = 0;
1356 return str_decode(str, &offset, str_size(str));
1357}
1358
1359static bool pt_is_delimiter(spt_t *pt)
1360{
1361 spt_t rp;
1362 coord_t coord;
1363 char *ch = NULL;
1364
1365 spt_get_coord(pt, &coord);
1366
1367 coord.column += 1;
[69cf3a4]1368 sheet_get_cell_pt(doc.sh, &coord, dir_after, &rp);
[8f6bffdd]1369
1370 ch = range_get_str(pt, &rp);
1371 if (ch == NULL)
1372 return false;
1373
1374 wchar_t first_char = get_first_wchar(ch);
1375 switch(first_char) {
1376 case ' ':
1377 case '\t':
1378 case '\n':
1379 return true;
1380 default:
1381 return false;
1382 }
1383}
1384
1385static bool pt_is_punctuation(spt_t *pt)
1386{
1387 spt_t rp;
1388 coord_t coord;
1389 char *ch = NULL;
1390
1391 spt_get_coord(pt, &coord);
1392
1393 coord.column += 1;
[69cf3a4]1394 sheet_get_cell_pt(doc.sh, &coord, dir_after, &rp);
[8f6bffdd]1395
1396 ch = range_get_str(pt, &rp);
1397 if (ch == NULL)
1398 return false;
1399
1400 wchar_t first_char = get_first_wchar(ch);
1401 switch(first_char) {
1402 case ',':
1403 case '.':
1404 case ';':
1405 case ':':
1406 case '/':
1407 case '?':
1408 case '\\':
1409 case '|':
1410 case '_':
1411 case '+':
1412 case '-':
1413 case '*':
1414 case '=':
1415 case '<':
1416 case '>':
1417 return true;
1418 default:
1419 return false;
1420 }
1421}
1422
[0f24c57]1423/** Compare tags. */
1424static int tag_cmp(tag_t const *a, tag_t const *b)
1425{
1426 spt_t pa, pb;
1427
1428 tag_get_pt(a, &pa);
1429 tag_get_pt(b, &pb);
1430
1431 return spt_cmp(&pa, &pb);
1432}
1433
1434/** Compare s-points. */
1435static int spt_cmp(spt_t const *a, spt_t const *b)
1436{
1437 coord_t ca, cb;
1438
1439 spt_get_coord(a, &ca);
1440 spt_get_coord(b, &cb);
1441
1442 return coord_cmp(&ca, &cb);
1443}
1444
1445/** Compare coordinats. */
1446static int coord_cmp(coord_t const *a, coord_t const *b)
1447{
1448 if (a->row - b->row != 0)
1449 return a->row - b->row;
1450
1451 return a->column - b->column;
1452}
1453
[3052ff4]1454/** Display text in the status line. */
1455static void status_display(char const *str)
1456{
[9f1362d4]1457 console_set_pos(con, 0, scr_rows - 1);
1458 console_set_style(con, STYLE_INVERTED);
[7e752b2]1459
1460 int pos = -(scr_columns - 3);
1461 printf(" %*s ", pos, str);
[79ae36dd]1462 console_flush(con);
[9f1362d4]1463 console_set_style(con, STYLE_NORMAL);
[3052ff4]1464
1465 pane.rflags |= REDRAW_CARET;
1466}
1467
1468/** @}
1469 */
Note: See TracBrowser for help on using the repository browser.