diff options
| author | Marc André Tanner <mat@brain-dump.org> | 2015-03-30 23:04:54 +0200 |
|---|---|---|
| committer | Marc André Tanner <mat@brain-dump.org> | 2015-04-03 23:52:06 +0200 |
| commit | 9915b9fd0b8e59bda9e334eb9485c473b06055c9 (patch) | |
| tree | 768f0a04df7e6cfc6d1379de8f19d1d4acdbf3a2 /window.c | |
| parent | b3b1abc76a832e53159b1191120bef56fe3a7041 (diff) | |
| download | vis-9915b9fd0b8e59bda9e334eb9485c473b06055c9.tar.gz vis-9915b9fd0b8e59bda9e334eb9485c473b06055c9.tar.xz | |
Preliminary user interface separation
In theory only ui-curses.[hc] should depend on curses, however in
practice keyboard input is still handled in vis.c. Furthermore the
syntax definitions as well as keyboard bindings and selection code
in window.c still depends on some curses constants.
There is also a slight regression in that the window status bar
does not show the current mode name. This and related global state
should be eliminated in the future.
Diffstat (limited to 'window.c')
| -rw-r--r-- | window.c | 273 |
1 files changed, 80 insertions, 193 deletions
@@ -19,7 +19,6 @@ #include <ctype.h> #include <errno.h> #include <regex.h> -#include <curses.h> #include "editor.h" #include "window.h" #include "syntax.h" @@ -27,29 +26,6 @@ #include "text-motions.h" #include "util.h" -typedef struct { /* used to calculate the display width of a character */ - char c[7]; /* utf8 encoded bytes */ - size_t len; /* number of bytes of the multibyte sequence */ - wchar_t wchar; /* equivalent converted wide character, needed for wcwidth(3) */ -} Char; - -typedef struct { - unsigned char len; /* number of bytes the character displayed in this cell uses, for - character which use more than 1 column to display, their lenght - is stored in the leftmost cell wheras all following cells - occupied by the same character have a length of 0. */ - char data; /* first byte of the utf8 sequence, used for white space handling */ -} Cell; - -typedef struct Line Line; -struct Line { /* a line on the screen, *not* in the file */ - Line *prev, *next; /* pointer to neighbouring screen lines */ - size_t len; /* line length in terms of bytes */ - size_t lineno; /* line number from start of file */ - int width; /* zero based position of last used column cell */ - Cell cells[]; /* win->width cells storing information about the displayed characters */ -}; - typedef struct { /* cursor position */ Filepos pos; /* in bytes from the start of the file */ int row, col; /* in terms of zero based screen coordinates */ @@ -58,19 +34,16 @@ typedef struct { /* cursor position */ struct Win { /* window showing part of a file */ Text *text; /* underlying text management */ - WINDOW *win; /* curses window for the text area */ - WINDOW *winnum; /* curses window for the line numbers area */ + UiWin *ui; int width, height; /* window text area size */ - int w, h, x, y; /* complete window position & dimension (including line numbers) */ Filepos start, end; /* currently displayed area [start, end] in bytes from the start of the file */ + size_t lines_size; /* number of allocated bytes for lines (grows only) */ Line *lines; /* win->height number of lines representing window content */ Line *topline; /* top of the window, first line currently shown */ Line *lastline; /* last currently used line, always <= bottomline */ Line *bottomline; /* bottom of screen, might be unused if lastline < bottomline */ Filerange sel; /* selected text range in bytes from start of file */ Cursor cursor; /* current window cursor position */ - void (*cursor_moved)(Win*, void *); /* registered callback, fires whenever the cursor moved */ - void *cursor_moved_data; /* user supplied data, passed as second argument to the above callback */ Line *line; /* used while drawing window content, line where next char will be drawn */ int col; /* used while drawing window content, column where next char will be drawn */ Syntax *syntax; /* syntax highlighting definitions for this window or NULL */ @@ -78,7 +51,7 @@ struct Win { /* window showing part of a file */ }; static void window_clear(Win *win); -static bool window_addch(Win *win, Char *c); +static bool window_addch(Win *win, Cell *cell); static size_t window_cursor_update(Win *win); /* set/move current cursor position to a given (line, column) pair */ static size_t window_cursor_set(Win *win, Line *line, int col); @@ -105,55 +78,22 @@ void window_selection_clear(Win *win) { curs_set(1); } -static int window_numbers_width(Win *win) { - if (!win->winnum) - return 0; - return snprintf(NULL, 0, "%zd", win->topline->lineno + win->height - 1) + 1; -} - -static void window_numbers_draw(Win *win) { - if (!win->winnum) - return; - werase(win->winnum); - size_t prev_lineno = -1; - int width = window_numbers_width(win), i = 0; - char fmt[32]; - snprintf(fmt, sizeof fmt, "%%%dd", width - 1); - for (Line *l = win->topline; l && l <= win->lastline; l = l->next, i++) { - if (l->lineno != prev_lineno) - mvwprintw(win->winnum, i, 0, fmt, l->lineno); - prev_lineno = l->lineno; - } - mvwvline(win->winnum, 0, width-1, ACS_VLINE, win->height); -} - /* reset internal window data structures (cell matrix, line offsets etc.) */ static void window_clear(Win *win) { /* calculate line number of first line */ + // TODO move elsewhere win->topline = win->lines; win->topline->lineno = text_lineno_by_pos(win->text, win->start); win->lastline = win->topline; - /* based on which we can calculate how much space is needed to display line numbers */ - int lineno_width = window_numbers_width(win); - int width = win->w - lineno_width; - if (win->winnum) { - wresize(win->winnum, win->h, lineno_width); - mvwin(win->winnum, win->y, win->x); - } - - wresize(win->win, win->height, width); - mvwin(win->win, win->y, win->x + lineno_width); - win->width = width; - /* reset all other lines */ size_t line_size = sizeof(Line) + win->width*sizeof(Cell); size_t end = win->height * line_size; Line *prev = NULL; for (size_t i = 0; i < end; i += line_size) { Line *line = (Line*)(((char*)win->lines) + i); - line->len = 0; line->width = 0; + line->len = 0; line->prev = prev; if (prev) prev->next = line; @@ -190,15 +130,15 @@ Filerange window_viewport_get(Win *win) { } /* try to add another character to the window, return whether there was space left */ -static bool window_addch(Win *win, Char *c) { +static bool window_addch(Win *win, Cell *cell) { if (!win->line) return false; int width; - Cell empty = { .len = 0, .data = '\0' }; + static Cell empty; size_t lineno = win->line->lineno; - switch (c->wchar) { + switch (cell->data[0]) { case '\t': width = win->tabwidth - (win->col % win->tabwidth); for (int w = 0; w < width; w++) { @@ -211,68 +151,53 @@ static bool window_addch(Win *win, Char *c) { } if (w == 0) { /* first cell of a tab has a length of 1 */ - win->line->cells[win->col].len = c->len; - win->line->len += c->len; + win->line->cells[win->col].len = cell->len; + win->line->len += cell->len; } else { /* all remaining ones have a lenght of zero */ win->line->cells[win->col].len = 0; } /* but all are marked as part of a tabstop */ - win->line->cells[win->col].data = '\t'; - win->col++; + win->line->cells[win->col].width = 1; + win->line->cells[win->col].data[0] = ' '; + win->line->cells[win->col].data[1] = '\0'; + win->line->cells[win->col].istab = true; win->line->width++; - waddch(win->win, ' '); + win->col++; } return true; case '\n': - width = 1; - if (win->col + width > win->width) { + cell->width = 1; + if (win->col + cell->width > win->width) { win->line = win->line->next; win->col = 0; if (!win->line) return false; - win->line->lineno = lineno + 1; + win->line->lineno = lineno; } - win->line->cells[win->col].len = c->len; - win->line->len += c->len; - win->line->cells[win->col].data = '\n'; + win->line->cells[win->col] = *cell; + win->line->len += cell->len; + win->line->width += cell->width; for (int i = win->col + 1; i < win->width; i++) win->line->cells[i] = empty; - if (win->line == win->bottomline) { - /* XXX: curses bug? the wclrtoeol(win->win); implied by waddch(win->win, '\n') - * doesn't seem to work on the last line!? - * - * Thus explicitly clear the remaining of the line. - */ - for (int i = win->col; i < win->width; i++) - waddch(win->win, ' '); - } else if (win->line->width == 0) { - /* add a single space in an otherwise empty line, makes selection cohorent */ - waddch(win->win, ' '); - } - - waddch(win->win, '\n'); win->line = win->line->next; if (win->line) win->line->lineno = lineno + 1; win->col = 0; return true; default: - if (c->wchar < 128 && !isprint(c->wchar)) { + if ((unsigned char)cell->data[0] < 128 && !isprint((unsigned char)cell->data[0])) { /* non-printable ascii char, represent it as ^(char + 64) */ - Char s = { .c = "^_", .len = 1 }; - s.c[1] = c->c[0] + 64; - *c = s; - width = 2; - } else { - if ((width = wcwidth(c->wchar)) == -1) { - /* this should never happen */ - width = 1; - } + *cell = (Cell) { + .data = { '^', cell->data[0] + 64, '\0' }, + .len = 1, + .width = 2, + .istab = false, + }; } - if (win->col + width > win->width) { + if (win->col + cell->width > win->width) { for (int i = win->col; i < win->width; i++) win->line->cells[i] = empty; win->line = win->line->next; @@ -280,16 +205,14 @@ static bool window_addch(Win *win, Char *c) { } if (win->line) { - win->line->width += width; - win->line->len += c->len; + win->line->width += cell->width; + win->line->len += cell->len; win->line->lineno = lineno; - win->line->cells[win->col].len = c->len; - win->line->cells[win->col].data = c->c[0]; + win->line->cells[win->col] = *cell; win->col++; /* set cells of a character which uses multiple columns */ - for (int i = 1; i < width; i++) + for (int i = 1; i < cell->width; i++) win->line->cells[win->col++] = empty; - waddstr(win->win, c->c); return true; } return false; @@ -317,9 +240,7 @@ static size_t window_cursor_update(Win *win) { win->sel.end = cursor->pos; window_draw(win); } - wmove(win->win, cursor->row, cursor->col); - if (win->cursor_moved) - win->cursor_moved(win, win->cursor_moved_data); + win->ui->cursor_to(win->ui, cursor->col, cursor->row); return cursor->pos; } @@ -385,9 +306,7 @@ void window_cursor_to(Win *win, size_t pos) { /* redraw the complete with data starting from win->start bytes into the file. * stop once the screen is full, update win->end, win->lastline */ void window_draw(Win *win) { - int old_width = win->width; window_clear(win); - wmove(win->win, 0, 0); /* current absolute file position */ size_t pos = win->start; /* number of bytes to read in one go */ @@ -400,8 +319,6 @@ void window_draw(Win *win) { text[rem] = '\0'; /* current position into buffer from which to interpret a character */ char *cur = text; - /* current 'parsed' character' */ - Char c; /* current selection */ Filerange sel = window_selection_get(win); /* syntax definition to use */ @@ -414,6 +331,11 @@ void window_draw(Win *win) { while (rem > 0) { + /* current 'parsed' character' */ + wchar_t wchar; + Cell cell; + memset(&cell, 0, sizeof cell); + if (syntax) { if (matched && cur >= text + matched->rm_eo) { /* end of current match */ @@ -460,13 +382,13 @@ void window_draw(Win *win) { } } - size_t len = mbrtowc(&c.wchar, cur, rem, NULL); + size_t len = mbrtowc(&wchar, cur, rem, NULL); if (len == (size_t)-1 && errno == EILSEQ) { /* ok, we encountered an invalid multibyte sequence, * replace it with the Unicode Replacement Character * (FFFD) and skip until the start of the next utf8 char */ for (len = 1; rem > len && !ISUTF8(cur[len]); len++); - c = (Char){ .c = "\xEF\xBF\xBD", .wchar = 0xFFFD, .len = len }; + cell = (Cell){ .data = "\xEF\xBF\xBD", .len = len, .width = 1, .istab = false }; } else if (len == (size_t)-2) { /* not enough bytes available to convert to a * wide character. advance file position and read @@ -478,74 +400,65 @@ void window_draw(Win *win) { continue; } else if (len == 0) { /* NUL byte encountered, store it and continue */ - len = 1; - c = (Char){ .c = "\x00", .wchar = 0x00, .len = len }; + cell = (Cell){ .data = "\x00", .len = 1, .width = 0, .istab = false }; } else { for (size_t i = 0; i < len; i++) - c.c[i] = cur[i]; - c.c[len] = '\0'; - c.len = len; + cell.data[i] = cur[i]; + cell.data[len] = '\0'; + cell.istab = false; + cell.len = len; + cell.width = wcwidth(wchar); + if (cell.width == -1) + cell.width = 1; } if (cur[0] == '\r' && rem > 1 && cur[1] == '\n') { /* convert windows style newline \r\n into a single char with len = 2 */ - len = 2; - c = (Char){ .c = "\n", .wchar = L'\n', .len = len }; + cell = (Cell){ .data = "\n", .len = 2, .width = 1, .istab = false }; } + cell.attr = attrs; if (sel.start <= pos && pos < sel.end) - wattrset(win->win, attrs | A_REVERSE); - else - wattrset(win->win, attrs); - - if (!window_addch(win, &c)) + cell.attr |= A_REVERSE; + if (!window_addch(win, &cell)) break; - rem -= len; - cur += len; - pos += len; + rem -= cell.len; + cur += cell.len; + pos += cell.len; } /* set end of viewing region */ win->end = pos; win->lastline = win->line ? win->line : win->bottomline; win->lastline->next = NULL; - /* and clear the rest of the unused window */ - wclrtobot(win->win); - /* if the text area width has changed because of the line numbers - * we have to (re)sync the cursor->line pointer */ - if (win->width != old_width) - window_cursor_sync(win); - /* draw line numbers */ - window_numbers_draw(win); + window_cursor_sync(win); + win->ui->draw_text(win->ui, win->topline); } bool window_resize(Win *win, int width, int height) { - // TODO: only grow memory area - size_t line_size = sizeof(Line) + width*sizeof(Cell); - Line *lines = calloc(height, line_size); - if (!lines) - return false; - free(win->lines); - win->w = win->width = width; - win->h = win->height = height; - win->lines = lines; - window_clear(win); + size_t lines_size = height*(sizeof(Line) + width*sizeof(Cell)); + if (lines_size > win->lines_size) { + Line *lines = malloc(lines_size); + if (!lines) + return false; + win->lines = lines; + win->lines_size = lines_size; + } + win->width = width; + win->height = height; + memset(win->lines, 0, win->lines_size); + window_draw(win); return true; } -void window_move(Win *win, int x, int y) { - win->x = x; - win->y = y; +int window_height_get(Win *win) { + return win->height; } void window_free(Win *win) { if (!win) return; - if (win->winnum) - delwin(win->winnum); - if (win->win) - delwin(win->win); free(win->lines); free(win); } @@ -554,27 +467,23 @@ void window_reload(Win *win, Text *text) { win->text = text; window_selection_clear(win); window_cursor_to(win, 0); + win->ui->reload(win->ui, text); } -Win *window_new(Text *text) { - if (!text) +Win *window_new(Text *text, UiWin *ui, int width, int height) { + if (!text || !ui) return NULL; Win *win = calloc(1, sizeof(Win)); - if (!win || !(win->win = newwin(0, 0, 0, 0))) { - window_free(win); + if (!win) return NULL; - } win->text = text; + win->ui = ui; win->tabwidth = 8; - - int width, height; - getmaxyx(win->win, height, width); if (!window_resize(win, width, height)) { window_free(win); return NULL; } - window_selection_clear(win); window_cursor_to(win, 0); @@ -633,9 +542,9 @@ static size_t window_cursor_set(Win *win, Line *line, int col) { } /* for characters which use more than 1 column, make sure we are on the left most */ - while (col > 0 && line->cells[col].len == 0 && line->cells[col].data != '\t') + while (col > 0 && line->cells[col].len == 0) col--; - while (col < line->width && line->cells[col].data == '\t') + while (col < line->width && line->cells[col].istab) col++; /* calculate offset within the line */ @@ -845,12 +754,6 @@ size_t window_screenline_end(Win *win) { return window_cursor_set(win, cursor->line, col >= 0 ? col : 0); } -void window_update(Win *win) { - if (win->winnum) - wnoutrefresh(win->winnum); - wnoutrefresh(win->win); -} - size_t window_delete_key(Win *win) { Cursor *cursor = &win->cursor; Line *line = cursor->line; @@ -899,7 +802,7 @@ size_t window_replace_key(Win *win, const char *c, size_t len) { Line *line = cursor->line; size_t pos = cursor->pos; /* do not overwrite new line which would merge the two lines */ - if (line->cells[cursor->col].data != '\n') { + if (line->cells[cursor->col].data[0] != '\n') { size_t oldlen = line->cells[cursor->col].len; text_delete(win->text, pos, oldlen); } @@ -939,25 +842,9 @@ Syntax *window_syntax_get(Win *win) { return win->syntax; } -void window_cursor_watch(Win *win, void (*cursor_moved)(Win*, void *), void *data) { - win->cursor_moved = cursor_moved; - win->cursor_moved_data = data; -} - size_t window_screenline_goto(Win *win, int n) { size_t pos = win->start; for (Line *line = win->topline; --n > 0 && line != win->lastline; line = line->next) pos += line->len; return pos; } - -void window_line_numbers_show(Win *win, bool show) { - if (show) { - if (!win->winnum) - win->winnum = newwin(0, 0, 0, 0); - } else { - if (win->winnum) - delwin(win->winnum); - win->winnum = NULL; - } -} |
