#include "owl.h" #include #include #include #include #define VALID_EXCURSION (0x9a2b4729) typedef struct _owl_editwin_excursion { /*noproto*/ int valid; int index; int mark; int goal_column; int lock; struct _owl_editwin_excursion *next; } oe_excursion; struct _owl_editwin { /*noproto*/ int refcount; char *buff; owl_history *hist; int bufflen; int allocated; int index; int mark; int goal_column; int topindex; int column; int winlines, wincols, fillcol, wrapcol; owl_window *win; gulong repaint_id; gulong resized_id; int style; int lock; int dotsend; int echochar; oe_excursion *excursions; void (*callback)(struct _owl_editwin*); void (*destroy_cbdata)(void *); void *cbdata; }; static void oe_set_window(owl_editwin *e, owl_window *w, int winlines, int wincols); static void oe_redraw(owl_window *win, WINDOW *curswin, void *user_data); static void oe_reframe(owl_editwin *e); static void oe_save_excursion(owl_editwin *e, oe_excursion *x); static void oe_release_excursion(owl_editwin *e, oe_excursion *x); static void oe_restore_excursion(owl_editwin *e, oe_excursion *x); static void oe_restore_mark_only(owl_editwin *e, oe_excursion *x); static int oe_char_width(gunichar c, int column); static int oe_region_column(owl_editwin *e, int start, int end, int offset); static int oe_region_width(owl_editwin *e, int start, int end, int offset); static int oe_find_display_line(owl_editwin *e, int *x, int index, int *hard); static void oe_insert_char(owl_editwin *e, gunichar c); static int owl_editwin_limit_maxcols(int width, int cols); static int owl_editwin_check_dotsend(owl_editwin *e); static int owl_editwin_is_char_in(owl_editwin *e, const char *set); static gunichar owl_editwin_get_char_at_point(owl_editwin *e); static int owl_editwin_replace_internal(owl_editwin *e, int replace, const char *s); static const char *oe_copy_buf(owl_editwin *e, const char *buf, int len); static int oe_copy_region(owl_editwin *e); static CALLER_OWN char *oe_chunk(owl_editwin *e, int start, int end); static void oe_destroy_cbdata(owl_editwin *e); static void oe_dirty(owl_editwin *e); static void oe_window_resized(owl_window *w, owl_editwin *e); #define INCR 4096 #define WHITESPACE " \n\t" static CALLER_OWN owl_editwin *owl_editwin_allocate(void) { owl_editwin *e = g_new0(owl_editwin, 1); e->refcount = 1; return e; } static void _owl_editwin_delete(owl_editwin *e) { if (e->win) { g_signal_handler_disconnect(e->win, e->repaint_id); g_signal_handler_disconnect(e->win, e->resized_id); g_object_unref(e->win); } g_free(e->buff); /* just in case someone forgot to clean up */ while (e->excursions) { oe_release_excursion(e, e->excursions); } oe_destroy_cbdata(e); g_free(e); } static inline void oe_set_index(owl_editwin *e, int index) { if (index != e->index) { e->goal_column = -1; e->column = -1; } e->index = index; oe_dirty(e); } static inline void oe_set_mark(owl_editwin *e, int mark) { e->mark = mark; } void owl_editwin_set_mark(owl_editwin *e) { oe_set_mark(e, e->index); /* owl_function_makemsg("Mark set."); */ } static void _owl_editwin_init(owl_editwin *e, int winlines, int wincols, int style, owl_history *hist) { e->buff=g_new(char, INCR); e->buff[0]='\0'; e->bufflen=0; e->hist=hist; e->allocated=INCR; oe_set_index(e, 0); oe_set_mark(e, -1); e->goal_column = -1; e->column = -1; e->topindex = 0; e->excursions = NULL; e->style=style; if ((style!=OWL_EDITWIN_STYLE_MULTILINE) && (style!=OWL_EDITWIN_STYLE_ONELINE)) { e->style=OWL_EDITWIN_STYLE_MULTILINE; } e->lock=0; e->dotsend=0; e->echochar='\0'; } CALLER_OWN owl_editwin *owl_editwin_new(owl_window *win, int winlines, int wincols, int style, owl_history *hist) { owl_editwin *e = owl_editwin_allocate(); _owl_editwin_init(e, winlines, wincols, style, hist); oe_set_window(e, win, winlines, wincols); return e; } owl_editwin *owl_editwin_ref(owl_editwin *e) { e->refcount++; return e; } void owl_editwin_unref(owl_editwin *e) { e->refcount--; if (e->refcount <= 0) _owl_editwin_delete(e); } /* TODO: collapse this window into oe_window_resized. Need to stop * passing winlines and wincols to oe_set_window and get them out of * the owl_window. The tester code will first need to pass in an * owl_window headless or so. */ static void oe_set_window_size(owl_editwin *e, int winlines, int wincols) { e->winlines=winlines; e->wincols=wincols; /* fillcol and wrapcol may have changed. */ e->fillcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxfillcols(&g)); if (e->style == OWL_EDITWIN_STYLE_MULTILINE) e->wrapcol=owl_editwin_limit_maxcols(wincols-7, owl_global_get_edit_maxwrapcols(&g)); else e->wrapcol = 0; } static void oe_window_resized(owl_window *w, owl_editwin *e) { int winlines, wincols; owl_window_get_position(w, &winlines, &wincols, NULL, NULL); oe_set_window_size(e, winlines, wincols); } static void oe_set_window(owl_editwin *e, owl_window *w, int winlines, int wincols) { e->win=w; oe_set_window_size(e, winlines, wincols); if (e->win) { g_object_ref(e->win); e->repaint_id = g_signal_connect(w, "redraw", G_CALLBACK(oe_redraw), e); e->resized_id = g_signal_connect(w, "resized", G_CALLBACK(oe_window_resized), e); owl_window_dirty(e->win); } } owl_window *owl_editwin_get_window(owl_editwin *e) { return e->win; } /* echo the character 'ch' for each normal character keystroke, * excepting locktext. This is useful for entering passwords etc. If * ch=='\0' characters are echo'd normally */ void owl_editwin_set_echochar(owl_editwin *e, int ch) { e->echochar=ch; oe_dirty(e); } owl_history *owl_editwin_get_history(owl_editwin *e) { return(e->hist); } void owl_editwin_set_dotsend(owl_editwin *e) { e->dotsend=1; } void owl_editwin_set_callback(owl_editwin *e, void (*cb)(owl_editwin*)) { e->callback = cb; } void (*owl_editwin_get_callback(owl_editwin *e))(owl_editwin*) { return e->callback; } static void oe_destroy_cbdata(owl_editwin *e) { if (e->destroy_cbdata) e->destroy_cbdata(e->cbdata); e->cbdata = NULL; e->destroy_cbdata = NULL; } void owl_editwin_set_cbdata(owl_editwin *e, void *data, void (*destroy)(void *)) { oe_destroy_cbdata(e); e->cbdata = data; e->destroy_cbdata = destroy; } void *owl_editwin_get_cbdata(owl_editwin *e) { return e->cbdata; } void owl_editwin_do_callback(owl_editwin *e) { void (*cb)(owl_editwin*); cb=owl_editwin_get_callback(e); if(!cb) { owl_function_error("Internal error: No editwin callback!"); } else { /* owl_function_error("text: |%s|", owl_editwin_get_text(e)); */ cb(e); } } static int owl_editwin_limit_maxcols(int width, int cols) { if (cols == 0) return width; return cols; } /* set text to be 'locked in' at the beginning of the buffer, any * previous text (including locked text) will be overwritten */ void owl_editwin_set_locktext(owl_editwin *e, const char *text) { oe_set_index(e, 0); e->lock = 0; owl_editwin_replace(e, e->bufflen, text); e->buff[e->bufflen] = 0; e->lock=e->bufflen; oe_set_index(e, e->lock); oe_dirty(e); } int owl_editwin_get_style(owl_editwin *e) { return(e->style); } /* clear all text except for locktext and put the cursor at the * beginning */ void owl_editwin_clear(owl_editwin *e) { int lock = e->lock; int dotsend=e->dotsend; char *locktext=NULL; char echochar=e->echochar; if (lock > 0) { locktext = g_strndup(e->buff, lock); } g_free(e->buff); _owl_editwin_init(e, e->winlines, e->wincols, e->style, e->hist); if (lock > 0) { owl_editwin_set_locktext(e, locktext); } if (dotsend) { owl_editwin_set_dotsend(e); } if (echochar) { owl_editwin_set_echochar(e, echochar); } g_free(locktext); oe_set_index(e, lock); } void owl_editwin_recenter(owl_editwin *e) { e->topindex = -1; oe_dirty(e); } static void oe_save_excursion(owl_editwin *e, oe_excursion *x) { x->index = e->index; x->mark = e->mark; x->goal_column = e->goal_column; x->lock = e->lock; x->valid = VALID_EXCURSION; x->next = e->excursions; e->excursions = x; } static void oe_release_excursion(owl_editwin *e, oe_excursion *x) { oe_excursion **px; x->valid = 0; for (px = &e->excursions; *px != NULL; px = &(*px)->next) if (*px == x) { *px = x->next; return; } abort(); } static void oe_restore_excursion(owl_editwin *e, oe_excursion *x) { if (x->valid == VALID_EXCURSION) { oe_set_index(e, x->index); e->goal_column = x->goal_column; e->mark = x->mark; e->lock = x->lock; oe_release_excursion(e, x); } } static void oe_restore_mark_only(owl_editwin *e, oe_excursion *x) { if (x->valid == VALID_EXCURSION) { e->mark = x->mark; oe_release_excursion(e, x); } } /* External interface to oe_save_excursion */ owl_editwin_excursion *owl_editwin_begin_excursion(owl_editwin *e) { owl_editwin_excursion *x = g_new(owl_editwin_excursion, 1); oe_save_excursion(e, x); return x; } void owl_editwin_end_excursion(owl_editwin *e, owl_editwin_excursion *x) { oe_restore_excursion(e, x); g_free(x); } static inline const char *oe_next_point(owl_editwin *e, const char *p) { const char *boundary = e->buff + e->bufflen + 1; const char *q; q = g_utf8_find_next_char(p, boundary); while (q && g_unichar_ismark(g_utf8_get_char(q))) q = g_utf8_find_next_char(q, boundary); if (q == p) return NULL; return q; } static inline const char *oe_prev_point(owl_editwin *e, const char *p) { const char *boundary = e->buff + e->lock; p = g_utf8_find_prev_char(boundary, p); while (p && g_unichar_ismark(g_utf8_get_char(p))) p = g_utf8_find_prev_char(boundary, p); return p; } static int oe_char_width(gunichar c, int column) { int cw; if (c == 9) /* TAB */ return TABSIZE - column % TABSIZE; cw = mk_wcwidth(c); if (cw < 0) /* control characters */ cw = 0; return cw; } /* Finds the display line of 'e' starting at the character * 'index'. The index just after the line is returned. Whether the * line ends at a hard break (newline) or soft break (wrapping) is * returned in 'hard'. * * If the point (e->index) is contained in the line, its position is * returned in 'x'. */ static int oe_find_display_line(owl_editwin *e, int *x, int index, int *hard) { int width = 0, cw; gunichar c; const char *p; while(1) { /* note the position of the dot */ if (x != NULL && index == e->index && width < e->wincols) *x = width; /* get the current character */ c = g_utf8_get_char(e->buff + index); /* figure out how wide it is */ cw = oe_char_width(c, width); if (width + cw > e->wincols - 1) { if (x != NULL && *x == width) *x = -1; if (hard != NULL) *hard = 0; break; } width += cw; if (c == '\n') { if (width < e->wincols) ++index; /* skip the newline */ if (hard != NULL) *hard = 1; break; } /* find the next character */ p = oe_next_point(e, e->buff + index); if (p == NULL) { /* we ran off the end */ if (x != NULL && e->index > index) *x = width + 1; if (hard != NULL) *hard = 1; break; } index = p - e->buff; } return index; } static void oe_reframe(owl_editwin *e) { oe_excursion x; int goal = 1 + e->winlines / 2; int index; int count = 0; int n, i; int last; oe_save_excursion(e, &x); /* step back line-by-line through the buffer until we have >= goal lines of display text */ e->lock = 0; /* we can (must) tread on the locktext */ last = -1; while (count < goal) { index = e->index; owl_editwin_move_to_beginning_of_line(e); if (last == e->index) break; last = e->index; for (n = 0, i = e->index; i < index; n++) i = oe_find_display_line(e, NULL, i, NULL); count += n == 0 ? 1 : n; if (count < goal) owl_editwin_point_move(e, -1); } e->topindex = e->index; /* if we overshot, backtrack */ for (n = 0; n < (count - goal); n++) e->topindex = oe_find_display_line(e, NULL, e->topindex, NULL); oe_restore_excursion(e, &x); oe_dirty(e); } static void oe_addnec(owl_editwin *e, WINDOW *curswin, int count) { int i; for (i = 0; i < count; i++) waddch(curswin, e->echochar); } static void oe_mvaddnec(owl_editwin *e, WINDOW *curswin, int y, int x, int count) { wmove(curswin, y, x); oe_addnec(e, curswin, count); } /* regenerate the text on the curses window */ static void oe_redraw(owl_window *win, WINDOW *curswin, void *user_data) { int x = -1, y = -1, t, hard; int line, index, lineindex, times = 0; owl_editwin *e = user_data; do { werase(curswin); if (e->topindex == -1 || e->index < e->topindex) oe_reframe(e); line = 0; index = e->topindex; while(line < e->winlines) { lineindex = index; t = -1; index = oe_find_display_line(e, &t, lineindex, &hard); if (x == -1 && t != -1) x = t, y = line; if (index - lineindex) { if (!e->echochar) mvwaddnstr(curswin, line, 0, e->buff + lineindex, index - lineindex); else { if(lineindex < e->lock) { mvwaddnstr(curswin, line, 0, e->buff + lineindex, MIN(index - lineindex, e->lock - lineindex)); if (e->lock < index) oe_addnec(e, curswin, oe_region_width(e, e->lock, index, oe_region_width(e, lineindex, e->lock, 0))); } else oe_mvaddnec(e, curswin, line, 0, oe_region_width(e, lineindex, index, 0)); } if (!hard) waddch(curswin, '\\'); } line++; } if (x == -1) e->topindex = -1; /* force a reframe */ times++; } while(x == -1 && times < 3); wmove(curswin, y, x); } static inline void oe_fixup(int *target, int start, int end, int change) { if (*target > start) { if (*target <= end) *target = end + change; else *target += change; } } int owl_editwin_replace_region(owl_editwin *e, const char *s) { oe_excursion x; int ret; if (e->mark == -1) { owl_function_error("The mark is unset, there is no region to replace."); return 0; } oe_save_excursion(e, &x); if(e->index > e->mark) { owl_editwin_exchange_point_and_mark(e); } ret = owl_editwin_replace_internal(e, e->mark - e->index, s); oe_restore_excursion(e, &x); return ret; } /* replace 'replace' characters at the point with s, returning the change in size */ int owl_editwin_replace(owl_editwin *e, int replace, const char *s) { int start, end, i; const char *p; if (!g_utf8_validate(s, -1, NULL)) { owl_function_debugmsg("owl_editwin_insert_string: received non-utf-8 string."); return 0; } start = e->index; for (i = 0, p = e->buff + start; i < replace && p != NULL; i++) p = oe_next_point(e, p); if (p != NULL) end = p - e->buff; else end = e->bufflen; return owl_editwin_replace_internal(e, end - start, s); } static int owl_editwin_replace_internal(owl_editwin *e, int replace, const char *s) { int start, end, free, need, size, change, oldindex; oe_excursion *x; start = e->index; end = start + replace; free = e->allocated - e->bufflen + end - start; need = strlen(s) - free; if (need > 0) { size = e->allocated + need + INCR - (need % INCR); e->buff = g_renew(char, e->buff, size); e->allocated = size; } memmove(e->buff + start + strlen(s), e->buff + end, e->bufflen + 1 - end); memcpy(e->buff + start, s, strlen(s)); change = start - end + strlen(s); e->bufflen += change; oldindex = e->index; e->index += strlen(s); if (e->column != -1) e->column = oe_region_column(e, oldindex, e->index, e->column); /* fix up the mark */ oe_fixup(&e->mark, start, end, change); oe_fixup(&e->topindex, start, end, change); /* fix up any saved points after the replaced area */ for (x = e->excursions; x != NULL; x = x->next) { oe_fixup(&x->index, start, end, change); oe_fixup(&x->mark, start, end, change); } /* recenter if needed */ if (start <= e->topindex) owl_editwin_recenter(e); oe_dirty(e); return change; } /* linewrap the word just before the cursor. * returns 0 on success * returns -1 if we could not wrap. */ static void _owl_editwin_linewrap_word(owl_editwin *e) { oe_excursion x; gunichar c; oe_save_excursion(e, &x); while (owl_editwin_point_move(e, -1)) { c = owl_editwin_get_char_at_point(e); if (owl_util_can_break_after(c) || c == '\n') { if (c != '\n') owl_editwin_replace(e, c != ' ' ? 0 : 1, "\n"); break; } } oe_restore_excursion(e, &x); } /* delete the character at the current point, following chars * shift left. */ void owl_editwin_delete_char(owl_editwin *e) { owl_editwin_replace(e, 1, ""); } /* Swap the character at point with the character at point-1 and * advance the pointer. If point is at beginning of buffer do * nothing. If point is after the last character swap point-1 with * point-2. (Behaves as observed in tcsh and emacs). */ void owl_editwin_transpose_chars(owl_editwin *e) { const char *middle, *end, *start; char *tmp; if (e->bufflen == 0) return; if (e->index == e->bufflen) owl_editwin_point_move(e, -1); /* point is after last character */ if (owl_editwin_at_beginning_of_buffer(e)) return; /* point is at beginning of buffer, do nothing */ /* Transpose two utf-8 unicode glyphs. */ middle = e->buff + e->index; end = oe_next_point(e, middle); if (end == NULL) return; start = oe_prev_point(e, middle); if (start == NULL) return; tmp = g_new(char, (end - start) + 1); tmp[(end - start)] = 0; memcpy(tmp, middle, end - middle); memcpy(tmp + (end - middle), start, middle - start); owl_editwin_point_move(e, -1); owl_editwin_replace(e, 2, tmp); } /* insert 'string' at the current point, later text is shifted * right */ void owl_editwin_insert_string(owl_editwin *e, const char *s) { owl_editwin_replace(e, 0, s); } /* We assume index is not set to point to a mid-char */ static gunichar owl_editwin_get_char_at_point(owl_editwin *e) { return g_utf8_get_char(e->buff + e->index); } void owl_editwin_exchange_point_and_mark(owl_editwin *e) { int tmp; if (e->mark != -1) { tmp = e->mark; owl_editwin_set_mark(e); oe_set_index(e, tmp); } } int owl_editwin_point_move(owl_editwin *e, int delta) { const char *p; int change, d = 0; change = MAX(delta, - delta); p = e->buff + e->index; while (d < change && p != NULL) { if (delta > 0) p = oe_next_point(e, p); else p = oe_prev_point(e, p); if (p != NULL) { oe_set_index(e, p - e->buff); d++; } } return delta > 0 ? d : -d; } int owl_editwin_at_beginning_of_buffer(owl_editwin *e) { if (e->index == e->lock) return 1; return 0; } int owl_at_end_of_buffer(owl_editwin *e) { if (e->index == e->bufflen) return 1; return 0; } static int owl_editwin_at_beginning_of_line(owl_editwin *e) { oe_excursion x; int ret; if (owl_editwin_at_beginning_of_buffer(e)) return 1; oe_save_excursion(e, &x); owl_editwin_point_move(e, -1); ret = (owl_editwin_get_char_at_point(e) == '\n'); oe_restore_excursion(e, &x); return ret; } static int owl_editwin_is_char_in(owl_editwin *e, const char *set) { const char *p; for (p = set; *p != 0; p = g_utf8_find_next_char(p, NULL)) if (owl_editwin_get_char_at_point(e) == g_utf8_get_char(p)) return 1; return 0; } int owl_editwin_move_if_in(owl_editwin *e, int delta, const char *set) { int change, distance = 0; while (owl_editwin_is_char_in(e, set)) { change = owl_editwin_point_move(e, delta); distance += change; if (change == 0) break; } return distance; } int owl_editwin_move_if_not_in(owl_editwin *e, int delta, const char *set) { int change, distance = 0; while (!owl_editwin_is_char_in(e, set)) { change = owl_editwin_point_move(e, delta); distance += change; if (change == 0) break; } return distance; } int owl_editwin_move_to_beginning_of_line(owl_editwin *e) { int distance = 0; if (!owl_editwin_at_beginning_of_line(e)) { /* move off the \n if were at the end of a line */ distance += owl_editwin_point_move(e, -1); distance += owl_editwin_move_if_not_in(e, -1, "\n"); /* If we stopped because we reached a '\n', rather than because we * hit the top of the buffer, move forward from the end of the * previous line to the start of the current. */ if (owl_editwin_get_char_at_point(e) == '\n') distance += owl_editwin_point_move(e, 1); } e->goal_column = 0; /* subtleties */ return distance; } int owl_editwin_move_to_end_of_line(owl_editwin *e) { return owl_editwin_move_if_not_in(e, 1, "\n"); } int owl_editwin_line_move(owl_editwin *e, int delta) { int goal_column, change, ll, distance; int count = 0; change = MAX(delta, -delta); goal_column = e->goal_column; distance = owl_editwin_move_to_beginning_of_line(e); goal_column = goal_column == -1 ? -distance : goal_column; while(count < change) { if (delta > 0) { distance += owl_editwin_move_if_not_in(e, 1, "\n"); distance += owl_editwin_point_move(e, 1); } else { /* I really want to assert delta < 0 here */ distance += owl_editwin_point_move(e, -1); /* to the newline on the previous line */ distance += owl_editwin_move_to_beginning_of_line(e); } count++; } distance += (ll = owl_editwin_move_to_end_of_line(e)); if (ll > goal_column) distance += owl_editwin_point_move(e, goal_column - ll); e->goal_column = goal_column; oe_dirty(e); return distance; } void owl_editwin_backspace(owl_editwin *e) { /* delete the char before the current one * and shift later chars left */ if(owl_editwin_point_move(e, -1)) owl_editwin_delete_char(e); } void owl_editwin_key_up(owl_editwin *e) { owl_editwin_line_move(e, -1); } void owl_editwin_key_down(owl_editwin *e) { owl_editwin_line_move(e, 1); } void owl_editwin_key_left(owl_editwin *e) { owl_editwin_point_move(e, -1); } void owl_editwin_key_right(owl_editwin *e) { owl_editwin_point_move(e, 1); } int owl_editwin_forward_word(owl_editwin *e) { int distance; /* if we're starting on a space, find the first non-space */ distance = owl_editwin_move_if_in(e, 1, WHITESPACE); /* now find the end of this word */ distance += owl_editwin_move_if_not_in(e, 1, WHITESPACE); return distance; } void owl_editwin_move_to_nextword(owl_editwin *e) { owl_editwin_forward_word(e); } /* go backwards to the last non-space character */ int owl_editwin_backward_word(owl_editwin *e) { oe_excursion x; int distance = 0; int further = 0; int beginning; /* if in middle of word, beginning of word */ /* if at beginning of a word, find beginning of previous word */ if (owl_editwin_is_char_in(e, WHITESPACE)) { /* if in whitespace past end of word, find a word , the find the beginning*/ distance += owl_editwin_move_if_in(e, -1, WHITESPACE); /* leaves us on the last character of the word */ oe_save_excursion(e, &x); /* are we at the beginning of a word? */ owl_editwin_point_move(e, -1); beginning = owl_editwin_is_char_in(e, WHITESPACE); oe_restore_excursion(e, &x); if (beginning) return distance; } else { /* in the middle of the word; */ oe_save_excursion(e, &x); further += owl_editwin_point_move(e, -1); if (owl_editwin_is_char_in(e, WHITESPACE)) { /* we were at the beginning */ distance += owl_editwin_backward_word(e); /* previous case */ oe_release_excursion(e, &x); return distance + further; } else { oe_restore_excursion(e, &x); } } distance += owl_editwin_move_if_not_in(e, -1, WHITESPACE); /* will go past */ if (e->index > e->lock) distance += owl_editwin_point_move(e, 1); return distance; } void owl_editwin_move_to_previousword(owl_editwin *e) { owl_editwin_backward_word(e); } void owl_editwin_delete_nextword(owl_editwin *e) { oe_excursion x; oe_save_excursion(e, &x); oe_set_mark(e, e->index); owl_editwin_forward_word(e); owl_editwin_kill_region(e); oe_restore_mark_only(e, &x); } void owl_editwin_delete_previousword(owl_editwin *e) { oe_excursion x; oe_save_excursion(e, &x); oe_set_mark(e, e->index); owl_editwin_backward_word(e); owl_editwin_kill_region(e); oe_restore_mark_only(e, &x); } void owl_editwin_move_to_line_end(owl_editwin *e) { owl_editwin_move_to_end_of_line(e); } void owl_editwin_delete_to_endofline(owl_editwin *e) { oe_excursion x; int distance; oe_save_excursion(e, &x); owl_editwin_set_mark(e); distance = owl_editwin_move_to_end_of_line(e); if (distance) owl_editwin_kill_region(e); else owl_editwin_replace(e, 1, ""); oe_restore_excursion(e, &x); } void owl_editwin_yank(owl_editwin *e) { const char *killbuf = owl_global_get_kill_buffer(&g); if (killbuf != NULL) owl_editwin_replace(e, 0, killbuf); } static const char *oe_copy_buf(owl_editwin *e, const char *buf, int len) { owl_global_set_kill_buffer(&g, buf, len); return owl_global_get_kill_buffer(&g); } static int oe_copy_region(owl_editwin *e) { const char *p; int start, end; if (e->mark == -1) return 0; start = MIN(e->index, e->mark); end = MAX(e->index, e->mark); p = oe_copy_buf(e, e->buff + start, end - start); if (p != NULL) return end - start; return 0; } void owl_editwin_copy_region_as_kill(owl_editwin *e) { oe_copy_region(e); } void owl_editwin_kill_region(owl_editwin *e) { if (e->index > e->mark) owl_editwin_exchange_point_and_mark(e); owl_editwin_replace_internal(e, oe_copy_region(e), ""); } void owl_editwin_move_to_line_start(owl_editwin *e) { owl_editwin_move_to_beginning_of_line(e); } void owl_editwin_move_to_end(owl_editwin *e) { oe_set_index(e, e->bufflen); } void owl_editwin_move_to_top(owl_editwin *e) { oe_set_index(e, e->lock); } void owl_editwin_backward_paragraph(owl_editwin *e) { owl_editwin_point_move(e, -1); for (; e->index >= e->lock; owl_editwin_point_move(e, -1)) { if (e->index <= e->lock || ((e->buff[e->index] == '\n') && (e->buff[e->index - 1]=='\n'))) break; } } void owl_editwin_forward_paragraph(owl_editwin *e) { owl_editwin_point_move(e, 1); /* scan forward to the start of the next paragraph */ for(; e->index < e->bufflen; owl_editwin_point_move(e, 1)) { if (e->buff[e->index -1] == '\n' && e->buff[e->index] == '\n') break; } } int owl_editwin_current_column(owl_editwin *e) { if (e->column == -1) { oe_excursion x; int lineindex; oe_save_excursion(e, &x); owl_editwin_move_to_beginning_of_line(e); lineindex = e->index; oe_restore_excursion(e, &x); e->column = oe_region_width(e, lineindex, e->index, 0); } return e->column; } void owl_editwin_fill_paragraph(owl_editwin *e) { oe_excursion x; gunichar ch; int sentence; if (e->fillcol < 0) /* auto-fill disabled */ return; oe_save_excursion(e, &x); /* Mark the end of the paragraph */ owl_editwin_forward_paragraph(e); /* Skip the trailing newline */ owl_editwin_point_move(e, -1); owl_editwin_set_mark(e); owl_editwin_backward_paragraph(e); /* Don't mess with the leading newline */ if (owl_editwin_get_char_at_point(e) == '\n') owl_editwin_point_move(e, 1); /* * First pass: Scan forward replacing all series of spaces with ' ' * (or nothing after CJK ideograms) */ sentence = 0; for(;e->index < e->mark; owl_editwin_point_move(e, 1)) { /* bail if we hit a trailing dot on the buffer */ if (strcmp(e->buff + e->index, "\n.") == 0) { owl_editwin_set_mark(e); break; } ch = owl_editwin_get_char_at_point(e); if (owl_util_can_break_after(ch) || ch == '\n') { if (g_unichar_isspace(ch)) { owl_editwin_replace(e, 1, " "); } if (sentence && g_unichar_isspace(owl_editwin_get_char_at_point(e)) && e->index < e->mark) owl_editwin_point_move(e, 1); while(g_unichar_isspace(owl_editwin_get_char_at_point(e)) && e->index < e->mark) { owl_editwin_delete_char(e); } } if(ch == '.' || ch == '!' || ch == '?') sentence = 1; else sentence = 0; } owl_editwin_backward_paragraph(e); /* Now go through inserting newlines as needed */ while(e->index < e->mark) { /* if we've travelled too far, linewrap */ if (owl_editwin_current_column(e) >= e->fillcol) _owl_editwin_linewrap_word(e); owl_editwin_point_move(e, 1); } oe_restore_excursion(e, &x); } /* returns true if only whitespace remains */ int owl_editwin_is_at_end(owl_editwin *e) { return (only_whitespace(e->buff + e->index)); } static int owl_editwin_check_dotsend(owl_editwin *e) { int zdot = 0; oe_excursion x; if (!e->dotsend) return(0); if (!owl_editwin_is_at_end(e)) return (0); oe_save_excursion(e, &x); owl_editwin_point_move(e, -3); if(strncmp(e->buff + e->index, "\n.\n", 3) == 0) { owl_editwin_point_move(e, 1); zdot = 1; } else if(e->index == e->lock && strncmp(e->buff + e->index, ".\n", 2) == 0) { zdot = 1; } if(zdot) { owl_editwin_set_mark(e); owl_editwin_move_to_end(e); owl_editwin_replace_region(e, ""); } oe_restore_excursion(e, &x); return zdot; } void owl_editwin_post_process_char(owl_editwin *e, owl_input j) { /* XXX force a redisplay? */ if ((j.ch==13 || j.ch==10) && owl_editwin_check_dotsend(e)) { owl_command_edit_done(e); return; } } static int oe_region_column(owl_editwin *e, int start, int end, int offset) { const char *p; int column = offset; for(p = e->buff + start; p < e->buff + end; p = g_utf8_find_next_char(p, NULL)) { gunichar c = g_utf8_get_char(p); if (c == '\n') column = 0; else column += oe_char_width(c, column); } return column; } static int oe_region_width(owl_editwin *e, int start, int end, int offset) { const char *p; int width = offset; for(p = e->buff + start; p < e->buff + end; p = g_utf8_find_next_char(p, NULL)) width += oe_char_width(g_utf8_get_char(p), width); return width - offset; } static void oe_insert_char(owl_editwin *e, gunichar c) { oe_excursion x; char tmp[7]; int replaced = -1; int column; if (c == '\r') /* translate CRs to NLs */ c = '\n'; if (!g_unichar_iscntrl(c) || c == '\n' || c== '\t' ) { if (c == '\n' && e->style == OWL_EDITWIN_STYLE_ONELINE) { return; } column = owl_editwin_current_column(e); if (e->wrapcol > 0 && column != -1 && column + oe_char_width(c, column) > e->wrapcol) { /* XXX this is actually wrong: * + If the user went back and inserted a bunch of stuff in the middle of * the line, there may be more than one word past the wrap column. */ oe_save_excursion(e, &x); if (c == ' ' || c == '\t') { owl_editwin_point_move(e, -1); replaced = -owl_editwin_move_if_in(e, -1, " \t"); if (!replaced) { c = '\n'; replaced = -1; } } else { while(!owl_editwin_at_beginning_of_line(e)) { owl_editwin_point_move(e, -1); if (owl_util_can_break_after(owl_editwin_get_char_at_point(e))) { replaced = -owl_editwin_move_if_in(e, -1, " \t"); break; } } if (owl_editwin_at_beginning_of_line(e)) replaced = -1; } if (replaced && !owl_editwin_at_beginning_of_line(e)) owl_editwin_point_move(e, 1); if (replaced >= 0) { owl_editwin_replace(e, replaced, "\n"); } oe_restore_excursion(e, &x); } if (replaced >= 0 && (c == ' ' || c == '\t')) return; /* our work here is done */ tmp[g_unichar_to_utf8(c, tmp)] = '\0'; owl_editwin_replace(e, 0, tmp); } } void owl_editwin_process_char(owl_editwin *e, owl_input j) { if (j.ch == ERR) return; /* Ignore ncurses control characters. */ if (j.ch < 0x100) { oe_insert_char(e, j.uch); } } const char *owl_editwin_get_text(owl_editwin *e) { return(e->buff+e->lock); } CALLER_OWN char *owl_editwin_get_region(owl_editwin *e) { int start, end; start = e->index; end = e->mark; if(start > end) { int tmp = end; end = start; start = tmp; } return oe_chunk(e, start, end); } int owl_editwin_get_echochar(owl_editwin *e) { return e->echochar; } static CALLER_OWN char *oe_chunk(owl_editwin *e, int start, int end) { char *p; p = g_new(char, end - start + 1); memcpy(p, e->buff + start, end - start); p[end - start] = 0; return p; } /* * The only guarantee made about these values is that comparisons * between them, as well as comparison between multiple calls to these * functions without modifying the editwin in-between, are meaningful. */ int owl_editwin_get_point(owl_editwin *e) { return e->index; } int owl_editwin_get_mark(owl_editwin *e) { return e->mark; } static void oe_dirty(owl_editwin *e) { if (e->win) owl_window_dirty(e->win); } /* * Local Variables: * mode:C * c-basic-offset:2 * End: */