From 7f71be0d6334d5ec2421041a49b33779f5abf7a4 Mon Sep 17 00:00:00 2001 From: Slava Zanko Date: Mon, 29 Dec 2008 00:52:45 +0200 Subject: [PATCH] =?UTF-8?q?patches=20by=20Rostislav=20Bene=C5=A1:=20mc-06-?= =?UTF-8?q?input?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit in input widget I made a lot of changes: 1. rename some members in WInput to reflect more their functions (field_len -> field_width, ...) 2. buffer for reading multibytes characters - charbuf, (taken from UTF-8 patch) 3. modified insert_char to accept mulstibytes characters, drawing changed 4. works only with valid strings, str_fix_string used to assure that input->buffer is valid changes in complete.c: - some iteration strings with ++/-- replaced with str_next_char, str_prev_char, where was needed. ++ was offset oriented, but str_next_char use directly referecne (char *), so result is not extra beatiful in every cases - other buffer for multibytes characters - complete only valid filenames --- src/complete.c | 244 +++++++++++++++++++++++++++---------------- src/widget.c | 278 +++++++++++++++++++++++++++++++------------------ src/widget.h | 16 +-- 3 files changed, 340 insertions(+), 198 deletions(-) diff --git a/src/complete.c b/src/complete.c index 1742b85be..36e538bd1 100644 --- a/src/complete.c +++ b/src/complete.c @@ -41,6 +41,7 @@ #include "complete.h" #include "main.h" #include "key.h" /* XCTRL and ALT macros */ +#include "strutil.h" typedef char *CompletionFunction (char *, int); @@ -97,6 +98,8 @@ filename_completion_function (char *text, int state) /* Now that we have some state, we can read the directory. */ while (directory && (entry = mc_readdir (directory))){ + if (!str_is_valid_string (entry->d_name)) continue; + /* Special case for no filename. All entries except "." and ".." match. */ if (!filename_len){ @@ -273,20 +276,24 @@ static void fetch_hosts (const char *filename) { FILE *file = fopen (filename, "r"); char buffer[256], *name; - register int i, start; + char *start; + char *bi; if (!file) return; while (fgets (buffer, 255, file) != NULL){ /* Skip to first character. */ - for (i = 0; buffer[i] && cr_whitespace (buffer[i]); i++); + for (bi = buffer; + bi[0] != '\0' && str_isspace (bi); + str_next_char (&bi)); + /* Ignore comments... */ - if (buffer[i] == '#') + if (bi[0] == '#') continue; /* Handle $include. */ - if (!strncmp (buffer + i, "$include ", 9)){ - char *includefile = buffer + i + 9; + if (!strncmp (bi, "$include ", 9)){ + char *includefile = bi + 9; char *t; /* Find start of filename. */ @@ -295,8 +302,8 @@ static void fetch_hosts (const char *filename) t = includefile; /* Find end of filename. */ - while (*t && !cr_whitespace (*t)) - t++; + while (t[0] != '\0' && !str_isspace (t)) + str_next_char (&t); *t = '\0'; fetch_hosts (includefile); @@ -304,19 +311,22 @@ static void fetch_hosts (const char *filename) } /* Skip IP #s. */ - while (buffer[i] && !cr_whitespace (buffer[i])) - i++; + while (bi[0] != '\0' && !str_isspace (bi)) + str_next_char (&bi); /* Get the host names separated by white space. */ - while (buffer[i] && buffer[i] != '#'){ - while (buffer[i] && cr_whitespace (buffer[i])) - i++; - if (buffer[i] == '#') + while (bi[0] != '\0' && bi[0] != '#'){ + while (bi[0] != '\0' && str_isspace (bi)) + str_next_char (&bi); + if (bi[0] == '#') continue; - for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++); - if (i - start == 0) - continue; - name = g_strndup (buffer + start, i - start); + for (start = bi; + bi[0] != '\0' && !str_isspace (bi); + str_next_char (&bi)); + + if (bi - start == 0) continue; + + name = g_strndup (start, bi - start); { char **host_p; @@ -566,19 +576,30 @@ completion_matches (char *text, CompletionFunction entry_function) j = i + 1; while (j < matches + 1) { - register int c1, c2, si; + char *si, *sj; + char *ni, *nj; - for (si = 0;(c1 = match_list [i][si]) && (c2 = match_list [j][si]); si++) - if (c1 != c2) break; + for (si = match_list[i], sj = match_list[j]; + si[0] && sj[0];) { + + ni = str_get_next_char (si); + nj = str_get_next_char (sj); - if (!c1 && !match_list [j][si]){ /* Two equal strings */ + if (ni - si != nj - sj) break; + if (strncmp (si, sj, ni - si) != 0) break; + + si = ni; + sj = nj; + } + + if (si[0] == '\0' && sj[0] == '\0'){ /* Two equal strings */ g_free (match_list [j]); j++; if (j > matches) break; continue; /* Look for a run of equal strings */ } else - if (low > si) low = si; + if (low > si - match_list[i]) low = si - match_list[i]; if (i + 1 != j) /* So there's some gap */ match_list [i + 1] = match_list [j]; i++; j++; @@ -598,7 +619,8 @@ completion_matches (char *text, CompletionFunction entry_function) static int check_is_cd (const char *text, int start, int flags) { - const char *p, *q; + char *p, *q; + int test = 0; if (flags & INPUT_COMPLETE_CD) return 1; @@ -607,14 +629,18 @@ check_is_cd (const char *text, int start, int flags) return 0; /* Skip initial spaces */ - p = text; - q = text + start; - while (p < q && *p && isspace ((unsigned char) *p)) - p++; + p = (char*)text; + q = (char*)text + start; + while (p < q && p[0] != '\0' && str_isspace (p)) + str_next_char (&p); /* Check if the command is "cd" and the cursor is after it */ - if (p[0] == 'c' && p[1] == 'd' && isspace ((unsigned char) p[2]) - && (p + 2 < q)) + text+= p[0] == 'c'; + str_next_char (&p); + text+= p[0] == 'd'; + str_next_char (&p); + text+= str_isspace (p); + if (test == 3 && (p < q)) return 1; return 0; @@ -624,44 +650,44 @@ check_is_cd (const char *text, int start, int flags) static char ** try_complete (char *text, int *start, int *end, int flags) { - int in_command_position = 0, i; - char *word, c; + int in_command_position = 0; + char *word; char **matches = NULL; const char *command_separator_chars = ";|&{(`"; char *p = NULL, *q = NULL, *r = NULL; int is_cd = check_is_cd (text, *start, flags); + char *ti; ignore_filenames = 0; - c = text [*end]; - text [*end] = 0; - word = g_strdup (text + *start); - text [*end] = c; + word = g_strndup (text + *start, *end - *start); /* Determine if this could be a command word. It is if it appears at the start of the line (ignoring preceding whitespace), or if it appears after a character that separates commands. And we have to be in a INPUT_COMPLETE_COMMANDS flagged Input line. */ if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS)){ - i = *start - 1; - while (i > -1 && (text[i] == ' ' || text[i] == '\t')) - i--; - if (i < 0) + ti = str_get_prev_char (&text[*start]); + while (ti > text && (ti[0] == ' ' || ti[0] == '\t')) + str_prev_char (&ti); + + if (ti <= text&& (ti[0] == ' ' || ti[0] == '\t')) in_command_position++; - else if (strchr (command_separator_chars, text[i])){ + else if (strchr (command_separator_chars, ti[0])){ register int this_char, prev_char; in_command_position++; - if (i){ + if (ti > text){ /* Handle the two character tokens `>&', `<&', and `>|'. We are not in a command position after one of these. */ - this_char = text[i]; - prev_char = text[i - 1]; + this_char = ti[0]; + prev_char = str_get_prev_char (ti)[0]; if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) || (this_char == '|' && prev_char == '>')) in_command_position = 0; - else if (i > 0 && text [i-1] == '\\') /* Quoted */ + + else if (ti > text && str_get_prev_char (ti)[0] == '\\') /* Quoted */ in_command_position = 0; } } @@ -675,15 +701,15 @@ try_complete (char *text, int *start, int *end, int flags) r = strrchr (word, '@'); if (q && q [1] == '(' && INPUT_COMPLETE_COMMANDS){ if (q > p) - p = q + 1; + p = str_get_next_char (q); q = NULL; } /* Command substitution? */ if (p > q && p > r){ - matches = completion_matches (p + 1, command_completion_function); + matches = completion_matches (str_get_next_char (p), command_completion_function); if (matches) - *start += p + 1 - word; + *start += str_get_next_char (p) - word; } /* Variable name? */ @@ -721,9 +747,9 @@ try_complete (char *text, int *start, int *end, int flags) if (!matches && is_cd && *word != PATH_SEP && *word != '~'){ char *p, *q = text + *start; - for (p = text; *p && p < q && (*p == ' ' || *p == '\t'); p++); + for (p = text; *p && p < q && (*p == ' ' || *p == '\t'); str_next_char (&p)); if (!strncmp (p, "cd", 2)) - for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); p++); + for (p += 2; *p && p < q && (*p == ' ' || *p == '\t'); str_next_char (&p)); if (p == q){ char * const cdpath_ref = g_strdup (getenv ("CDPATH")); char *cdpath = cdpath_ref; @@ -747,7 +773,7 @@ try_complete (char *text, int *start, int *end, int flags) g_free (r); } *s = c; - cdpath = s + 1; + cdpath = str_get_next_char (s); } g_free (cdpath_ref); } @@ -776,49 +802,57 @@ static WInput *input; static int min_end; static int start, end; -static int insert_text (WInput *in, char *text, ssize_t len) +static int insert_text (WInput *in, char *text, ssize_t size) { - len = min (len, (ssize_t) strlen (text)) + start - end; - if (strlen (in->buffer) + len >= (size_t) in->current_max_len){ + int buff_len = str_length (in->buffer); + + size = min (size, (ssize_t) strlen (text)) + start - end; + if (strlen (in->buffer) + size >= (size_t) in->current_max_size){ /* Expand the buffer */ - char *narea = g_realloc (in->buffer, in->current_max_len + len + in->field_len); + char *narea = g_realloc (in->buffer, in->current_max_size + + size + in->field_width); if (narea){ in->buffer = narea; - in->current_max_len += len + in->field_len; + in->current_max_size += size + in->field_width; } } - if (strlen (in->buffer)+1 < (size_t) in->current_max_len){ - if (len > 0){ + if (strlen (in->buffer)+1 < (size_t) in->current_max_size){ + if (size > 0){ int i = strlen (&in->buffer [end]); for (; i >= 0; i--) - in->buffer [end + len + i] = in->buffer [end + i]; - } else if (len < 0){ - char *p = in->buffer + end + len, *q = in->buffer + end; + in->buffer [end + size + i] = in->buffer [end + i]; + } else if (size < 0){ + char *p = in->buffer + end + size, *q = in->buffer + end; while (*q) *(p++) = *(q++); *p = 0; } - memcpy (in->buffer + start, text, len - start + end); - in->point += len; + memcpy (in->buffer + start, text, size - start + end); + in->point+= str_length (in->buffer) - buff_len; update_input (in, 1); - end += len; + end+= size; } - return len != 0; + return size != 0; } static cb_ret_t query_callback (Dlg_head *h, dlg_msg_t msg, int parm) { + static char buff[MB_LEN_MAX] = ""; + static int bl = 0; + switch (msg) { case DLG_KEY: switch (parm) { case KEY_LEFT: case KEY_RIGHT: + bl = 0; h->ret_value = 0; dlg_stop (h); return MSG_HANDLED; case KEY_BACKSPACE: + bl = 0; if (end == min_end) { h->ret_value = 0; dlg_stop (h); @@ -828,13 +862,13 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm) e1 = e = ((WListbox *) (h->current))->list; do { - if (!strncmp - (input->buffer + start, e1->text, - end - start - 1)) { - listbox_select_entry ((WListbox *) (h->current), - e1); + if (!strncmp (input->buffer + start, + e1->text, end - start - 1)) { + + listbox_select_entry ((WListbox *) (h->current), e1); + end = str_get_prev_char (&(input->buffer[end])) + - input->buffer; handle_char (input, parm); - end--; send_message (h->current, WIDGET_DRAW, 0); break; } @@ -844,7 +878,8 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm) return MSG_HANDLED; default: - if (parm > 0xff || !is_printable (parm)) { + if (parm < 32 || parm > 256) { + bl = 0; if (is_in_input_map (input, parm) == 2) { if (end == min_end) return MSG_HANDLED; @@ -860,21 +895,43 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm) int low = 4096; char *last_text = NULL; + buff[bl] = (char) parm; + bl++; + buff[bl] = '\0'; + switch (str_is_valid_char (buff, bl)) { + case -1: + bl = 0; + case -2: + return MSG_HANDLED; + } + e1 = e = ((WListbox *) (h->current))->list; do { - if (!strncmp - (input->buffer + start, e1->text, end - start)) { - if (e1->text[end - start] == parm) { + if (!strncmp (input->buffer + start, + e1->text, end - start)) { + + if (strncmp (&e1->text[end - start], buff, bl) == 0) { if (need_redraw) { - register int c1, c2, si; + char *si, *sl; + char *ni, *nl; + si = &(e1->text[end - start]); + sl = &(last_text[end - start]); + + for (; si[0] != '\0' && sl[0] != '\0';) { + + ni = str_get_next_char (si); + nl = str_get_next_char (sl); + + if (ni - si != nl - sl) break; + if (strncmp (si, sl, ni - si) != 0) break; + + si = ni; + sl = nl; + } + + if (low > si - &e1->text[start]) + low = si - &e1->text[start]; - for (si = end - start + 1; - (c1 = last_text[si]) - && (c2 = e1->text[si]); si++) - if (c1 != c2) - break; - if (low > si) - low = si; last_text = e1->text; need_redraw = 2; } else { @@ -895,6 +952,7 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm) h->ret_value = B_ENTER; dlg_stop (h); } + bl = 0; } return MSG_HANDLED; } @@ -911,15 +969,19 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm) static int complete_engine (WInput *in, int what_to_do) { - if (in->completions && in->point != end) + int s; + + if (in->completions && (str_offset_to_pos (in->buffer, in->point)) != end) free_completions (in); if (!in->completions){ - end = in->point; - for (start = end ? end - 1 : 0; start > -1; start--) - if (strchr (" \t;|<>", in->buffer [start])) + end = str_offset_to_pos (in->buffer, in->point); + for (s = in->point ? in->point - 1 : 0; s >= 0; s--) { + start = str_offset_to_pos (in->buffer, s); + if (strchr (" \t;|<>", in->buffer [start])) { + if (start < end) start = str_offset_to_pos (in->buffer, s + 1); break; - if (start < end) - start++; + } + } in->completions = try_complete (in->buffer, &start, &end, in->completion_flags); } if (in->completions){ @@ -941,7 +1003,7 @@ complete_engine (WInput *in, int what_to_do) WListbox *query_list; for (p=in->completions + 1; *p; count++, p++) - if ((i = strlen (*p)) > maxlen) + if ((i = str_term_width1 (*p)) > maxlen) maxlen = i; start_x = in->widget.x; start_y = in->widget.y; @@ -957,7 +1019,7 @@ complete_engine (WInput *in, int what_to_do) h = LINES - start_y - 1; } } - x = start - in->first_shown - 2 + start_x; + x = start - in->term_first_shown - 2 + start_x; w = maxlen + 4; if (x + w > COLS) x = COLS - w; @@ -999,6 +1061,8 @@ void complete (WInput *in) { int engine_flags; + if (!str_is_valid_string (in->buffer)) return; + if (in->completions) engine_flags = DO_QUERY; else diff --git a/src/widget.c b/src/widget.c index e87d1ee3d..1801aa226 100644 --- a/src/widget.c +++ b/src/widget.c @@ -789,27 +789,21 @@ gauge_new (int y, int x, int shown, int max, int current) #endif #define should_show_history_button(in) \ - (in->history && in->field_len > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent) +(in->history && in->field_width > HISTORY_BUTTON_WIDTH * 2 + 1 && in->widget.parent) static void draw_history_button (WInput * in) { char c; c = in->history->next ? (in->history->prev ? '|' : 'v') : '^'; - widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH); + widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH); #ifdef LARGE_HISTORY_BUTTON { Dlg_head *h; h = in->widget.parent; -#if 0 - attrset (NORMALC); /* button has the same color as other buttons */ - addstr ("[ ]"); - attrset (HOT_NORMALC); -#else attrset (NORMAL_COLOR); addstr ("[ ]"); /* Too distracting: attrset (MARKED_COLOR); */ -#endif - widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1); + widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1); addch (c); } #else @@ -829,9 +823,10 @@ void update_input (WInput *in, int clear_first) { int has_history = 0; - int i, j; - unsigned char c; - int buf_len = strlen (in->buffer); + int i; + int buf_len = str_length (in->buffer); + const char *cp; + int pw; if (should_show_history_button (in)) has_history = HISTORY_BUTTON_WIDTH; @@ -839,12 +834,15 @@ update_input (WInput *in, int clear_first) if (in->disable_update) return; + pw = str_term_width2 (in->buffer, in->point); + /* Make the point visible */ - if ((in->point < in->first_shown) || - (in->point >= in->first_shown+in->field_len - has_history)){ - in->first_shown = in->point - (in->field_len / 3); - if (in->first_shown < 0) - in->first_shown = 0; + if ((pw < in->term_first_shown) || + (pw >= in->term_first_shown + in->field_width - has_history)) { + + in->term_first_shown = pw - (in->field_width / 3); + if (in->term_first_shown < 0) + in->term_first_shown = 0; } /* Adjust the mark */ @@ -857,28 +855,29 @@ update_input (WInput *in, int clear_first) attrset (in->color); widget_move (&in->widget, 0, 0); - for (i = 0; i < in->field_len - has_history; i++) - addch (' '); - widget_move (&in->widget, 0, 0); - for (i = 0, j = in->first_shown; i < in->field_len - has_history && in->buffer [j]; i++){ - c = in->buffer [j++]; - c = is_printable (c) ? c : '.'; - if (in->is_password) - c = '*'; - addch (c); + if (!in->is_password) { + addstr (str_term_substring (in->buffer, in->term_first_shown, + in->field_width - has_history)); + } else { + cp = in->buffer; + for (i = -in->term_first_shown; i < in->field_width - has_history; i++){ + if (i >= 0) { + addch ((cp[0] != '\0') ? '*' : ' '); + } + if (cp[0] != '\0') str_cnext_char (&cp); + } } - widget_move (&in->widget, 0, in->point - in->first_shown); if (clear_first) in->first = 0; } void -winput_set_origin (WInput *in, int x, int field_len) +winput_set_origin (WInput *in, int x, int field_width) { in->widget.x = x; - in->field_len = in->widget.cols = field_len; + in->field_width = in->widget.cols = field_width; update_input (in, 0); } @@ -997,11 +996,7 @@ history_put (const char *input_name, GList *h) static const char * i18n_htitle (void) { - static const char *history_title = NULL; - - if (history_title == NULL) - history_title = _(" History "); - return history_title; + return _(" History "); } static WLEntry *listbox_select_pos (WListbox *l, WLEntry *base, int @@ -1021,7 +1016,7 @@ char * show_hist (GList *history, int widget_x, int widget_y) { GList *hi, *z; - size_t maxlen = strlen (i18n_htitle ()), i, count = 0; + size_t maxlen = str_term_width1 (i18n_htitle ()), i, count = 0; int x, y, w, h; char *q, *r = 0; Dlg_head *query_dlg; @@ -1034,7 +1029,7 @@ show_hist (GList *history, int widget_x, int widget_y) z = g_list_first (history); hi = z; while (hi) { - if ((i = strlen ((char *) hi->data)) > maxlen) + if ((i = str_term_width1 ((char *) hi->data)) > maxlen) maxlen = i; count++; hi = g_list_next (hi); @@ -1202,37 +1197,75 @@ new_input (WInput *in) if (in->buffer) push_history (in, in->buffer); in->need_push = 1; - in->buffer [0] = 0; + in->buffer[0] = '\0'; in->point = 0; + in->charpoint = 0; in->mark = 0; free_completions (in); update_input (in, 0); } +static void +move_buffer_backward (WInput *in, int start, int end) +{ + int i, pos, len; + int str_len = str_length (in->buffer); + if (start >= str_len || end > str_len + 1) return; + + pos = str_offset_to_pos (in->buffer, start); + len = str_offset_to_pos (in->buffer, end) - pos; + + for (i = pos; in->buffer[i + len - 1]; i++) + in->buffer[i] = in->buffer[i + len]; +} + static cb_ret_t insert_char (WInput *in, int c_code) { size_t i; + int res; if (c_code == -1) return MSG_NOT_HANDLED; + if (in->charpoint >= MB_LEN_MAX) return 1; + + in->charbuf[in->charpoint] = c_code; + in->charpoint++; + + res = str_is_valid_char (in->charbuf, in->charpoint); + if (res < 0) { + if (res != -2) in->charpoint = 0; /* broken multibyte char, skip */ + return 1; + } + in->need_push = 1; - if (strlen (in->buffer)+1 == (size_t) in->current_max_len){ + if (strlen (in->buffer) + 1 + in->charpoint >= in->current_max_size){ /* Expand the buffer */ - char *narea = g_realloc (in->buffer, in->current_max_len + in->field_len); + size_t new_length = in->current_max_size + + in->field_width + in->charpoint; + char *narea = g_try_renew (char, in->buffer, new_length); if (narea){ in->buffer = narea; - in->current_max_len += in->field_len; + in->current_max_size = new_length; } } - if (strlen (in->buffer)+1 < (size_t) in->current_max_len){ - size_t l = strlen (&in->buffer [in->point]); - for (i = l+1; i > 0; i--) - in->buffer [in->point+i] = in->buffer [in->point+i-1]; - in->buffer [in->point] = c_code; + + if (strlen (in->buffer) + in->charpoint < in->current_max_size) { + /* bytes from begin */ + size_t ins_point = str_offset_to_pos (in->buffer, in->point); + /* move chars */ + size_t rest_bytes = strlen (in->buffer + ins_point); + + for (i = rest_bytes + 1; i > 0; i--) + in->buffer[ins_point + i + in->charpoint - 1] = + in->buffer[ins_point + i - 1]; + + memcpy(in->buffer + ins_point, in->charbuf, in->charpoint); in->point++; } + + in->charpoint = 0; return MSG_HANDLED; } @@ -1240,54 +1273,70 @@ static void beginning_of_line (WInput *in) { in->point = 0; + in->charpoint = 0; } static void end_of_line (WInput *in) { - in->point = strlen (in->buffer); + in->point = str_length (in->buffer); + in->charpoint = 0; } static void backward_char (WInput *in) { - if (in->point) - in->point--; + const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point); + + if (in->point > 0) { + in->point-= str_cprev_noncomb_char (&act, in->buffer); + } + in->charpoint = 0; } static void forward_char (WInput *in) { - if (in->buffer [in->point]) - in->point++; + const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point); + if (act[0] != '\0') { + in->point+= str_cnext_noncomb_char (&act); + } + in->charpoint = 0; } static void forward_word (WInput * in) { - char *p = in->buffer + in->point; + const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point); - while (*p - && (isspace ((unsigned char) *p) - || ispunct ((unsigned char) *p))) - p++; - while (*p && isalnum ((unsigned char) *p)) - p++; - in->point = p - in->buffer; + while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) { + str_cnext_char (&p); + in->point++; + } + while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) { + str_cnext_char (&p); + in->point++; + } } static void backward_word (WInput *in) { - char *p = in->buffer + in->point; + const char *p = in->buffer + str_offset_to_pos (in->buffer, in->point); - while (p - 1 > in->buffer - 1 && (isspace ((unsigned char) *(p - 1)) - || ispunct ((unsigned char) - *(p - 1)))) + while ((p != in->buffer) && (p[0] == '\0')) { p--; - while (p - 1 > in->buffer - 1 && isalnum ((unsigned char) *(p - 1))) - p--; - in->point = p - in->buffer; + in->point--; + } + + while ((p != in->buffer) && (str_isspace (p) || str_ispunct (p))) { + str_cprev_char (&p); + in->point--; + } + while ((p != in->buffer) && !str_isspace (p) && !str_ispunct (p)) { + str_cprev_char (&p); + in->point--; + } } static void @@ -1316,23 +1365,29 @@ key_ctrl_right (WInput *in) static void backward_delete (WInput *in) { - int i; + const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point); + int start; - if (!in->point) + if (in->point == 0) return; - for (i = in->point; in->buffer [i-1]; i++) - in->buffer [i-1] = in->buffer [i]; + + start = in->point - str_cprev_noncomb_char (&act, in->buffer); + move_buffer_backward(in, start, in->point); + in->charpoint = 0; in->need_push = 1; - in->point--; + in->point = start; } static void delete_char (WInput *in) { - int i; + const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point); + int end = in->point; + + end+= str_cnext_noncomb_char (&act); - for (i = in->point; in->buffer [i]; i++) - in->buffer [i] = in->buffer [i+1]; + move_buffer_backward(in, in->point, end); + in->charpoint = 0; in->need_push = 1; } @@ -1347,7 +1402,10 @@ copy_region (WInput *in, int x_first, int x_last) g_free (kill_buffer); - kill_buffer = g_strndup(in->buffer+first,last-first); + first = str_offset_to_pos (in->buffer, first); + last = str_offset_to_pos (in->buffer, last); + + kill_buffer = g_strndup(in->buffer + first, last - first); } static void @@ -1355,11 +1413,15 @@ delete_region (WInput *in, int x_first, int x_last) { int first = min (x_first, x_last); int last = max (x_first, x_last); - size_t len = strlen (&in->buffer [last]) + 1; + size_t len; in->point = first; in->mark = first; - memmove (&in->buffer [first], &in->buffer [last], len); + last = str_offset_to_pos (in->buffer, last); + first = str_offset_to_pos (in->buffer, first); + len = strlen (&in->buffer[last]) + 1; + memmove (&in->buffer[first], &in->buffer[last], len); + in->charpoint = 0; in->need_push = 1; } @@ -1376,6 +1438,8 @@ kill_word (WInput *in) copy_region (in, old_point, new_point); delete_region (in, old_point, new_point); in->need_push = 1; + in->charpoint = 0; + in->charpoint = 0; } static void @@ -1419,16 +1483,20 @@ yank (WInput *in) if (!kill_buffer) return; + in->charpoint = 0; for (p = kill_buffer; *p; p++) insert_char (in, *p); + in->charpoint = 0; } static void kill_line (WInput *in) { + int chp = str_offset_to_pos (in->buffer, in->point); g_free (kill_buffer); - kill_buffer = g_strdup (&in->buffer [in->point]); - in->buffer [in->point] = 0; + kill_buffer = g_strdup (&in->buffer[chp]); + in->buffer[chp] = '\0'; + in->charpoint = 0; } void @@ -1437,10 +1505,11 @@ assign_text (WInput *in, const char *text) free_completions (in); g_free (in->buffer); in->buffer = g_strdup (text); /* was in->buffer->text */ - in->current_max_len = strlen (in->buffer) + 1; - in->point = strlen (in->buffer); + in->current_max_size = strlen (in->buffer) + 1; + in->point = str_length (in->buffer); in->mark = 0; in->need_push = 1; + in->charpoint = 0; } static void @@ -1564,9 +1633,10 @@ is_in_input_map (WInput *in, int c_code) static void port_region_marked_for_delete (WInput *in) { - *in->buffer = 0; + in->buffer[0] = '\0'; in->point = 0; in->first = 0; + in->charpoint = 0; } cb_ret_t @@ -1595,7 +1665,7 @@ handle_char (WInput *in, int c_code) } } if (!input_map [i].fn){ - if (c_code > 255 || !is_printable (c_code)) + if (c_code > 255) return MSG_NOT_HANDLED; if (in->first){ port_region_marked_for_delete (in); @@ -1623,11 +1693,14 @@ stuff (WInput *in, const char *text, int insert_extra_space) void input_set_point (WInput *in, int pos) { - if (pos > in->current_max_len) - pos = in->current_max_len; + int max_pos = str_length (in->buffer); + + if (pos > max_pos) + pos = max_pos; if (pos != in->point) free_completions (in); in->point = pos; + in->charpoint = 0; update_input (in, 1); } @@ -1668,7 +1741,8 @@ input_callback (Widget *w, widget_msg_t msg, int parm) return MSG_HANDLED; case WIDGET_CURSOR: - widget_move (&in->widget, 0, in->point - in->first_shown); + widget_move (&in->widget, 0, str_term_width2 (in->buffer, in->point) + - in->term_first_shown); return MSG_HANDLED; case WIDGET_DESTROY: @@ -1688,15 +1762,17 @@ input_event (Gpm_Event * event, void *data) if (event->type & (GPM_DOWN | GPM_DRAG)) { dlg_select_widget (in); - if (event->x >= in->field_len - HISTORY_BUTTON_WIDTH + 1 + if (event->x >= in->field_width - HISTORY_BUTTON_WIDTH + 1 && should_show_history_button (in)) { do_show_hist (in); } else { - in->point = strlen (in->buffer); - if (event->x - in->first_shown - 1 < in->point) - in->point = event->x - in->first_shown - 1; - if (in->point < 0) - in->point = 0; + in->point = str_length (in->buffer); + if (event->x + in->term_first_shown - 1 < + str_term_width1 (in->buffer)) + + in->point = str_column_to_pos (in->buffer, event->x + + in->term_first_shown - 1); + } update_input (in, 1); } @@ -1704,13 +1780,13 @@ input_event (Gpm_Event * event, void *data) } WInput * -input_new (int y, int x, int color, int len, const char *def_text, +input_new (int y, int x, int color, int width, const char *def_text, const char *histname) { WInput *in = g_new (WInput, 1); int initial_buffer_len; - init_widget (&in->widget, y, x, 1, len, input_callback, input_event); + init_widget (&in->widget, y, x, 1, width, input_callback, input_event); /* history setup */ in->history = NULL; @@ -1722,7 +1798,7 @@ input_new (int y, int x, int color, int len, const char *def_text, } } - if (!def_text) + if (def_text == NULL) def_text = ""; if (def_text == INPUT_LAST_TEXT) { @@ -1731,29 +1807,29 @@ input_new (int y, int x, int color, int len, const char *def_text, if (in->history->data) def_text = (char *) in->history->data; } - initial_buffer_len = 1 + max ((size_t) len, strlen (def_text)); + initial_buffer_len = 1 + max ((size_t) width, strlen (def_text)); in->widget.options |= W_IS_INPUT; in->completions = NULL; in->completion_flags = INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES; - in->current_max_len = initial_buffer_len; - in->buffer = g_malloc (initial_buffer_len); + in->current_max_size = initial_buffer_len; + in->buffer = g_new (char, initial_buffer_len); in->color = color; - in->field_len = len; + in->field_width = width; in->first = 1; - in->first_shown = 0; + in->term_first_shown = 0; in->disable_update = 0; in->mark = 0; in->need_push = 1; in->is_password = 0; strcpy (in->buffer, def_text); - in->point = strlen (in->buffer); + in->point = str_length (in->buffer); + in->charpoint = 0; return in; } - /* Listbox widget */ /* Should draw the scrollbar, but currently draws only diff --git a/src/widget.h b/src/widget.h index 50004247e..35d79e16f 100644 --- a/src/widget.h +++ b/src/widget.h @@ -74,11 +74,11 @@ char *show_hist (GList *history, int widget_y, int widget_x); typedef struct { Widget widget; - int point; /* cursor position in the input line */ - int mark; /* The mark position */ - int first_shown; /* Index of the first shown character */ - int current_max_len; /* Maximum length of input line */ - int field_len; /* Length of the editing field */ + int point; /* cursor position in the input line in characters */ + int mark; /* The mark position in characters */ + int term_first_shown; /* column of the first shown character */ + size_t current_max_size; /* Maximum length of input line (bytes) */ + int field_width; /* width of the editing field */ int color; /* color used */ int first; /* Is first keystroke? */ int disable_update; /* Do we want to skip updates? */ @@ -89,6 +89,8 @@ typedef struct { char **completions; /* Possible completions array */ int completion_flags; /* INPUT_COMPLETE* bitwise flags(complete.h) */ char *history_name; /* name of history for loading and saving */ + char charbuf[MB_LEN_MAX]; /* buffer for multibytes characters */ + size_t charpoint; /* point to end of mulibyte sequence in charbuf */ } WInput; /* For history load-save functions */ @@ -144,14 +146,14 @@ WButton *button_new (int y, int x, int action, int flags, const char *text, bcback callback); WRadio *radio_new (int y, int x, int count, const char **text); WCheck *check_new (int y, int x, int state, const char *text); -WInput *input_new (int y, int x, int color, int len, const char *text, const char *histname); +WInput *input_new (int y, int x, int color, int width, const char *text, const char *histname); WLabel *label_new (int y, int x, const char *text); WGauge *gauge_new (int y, int x, int shown, int max, int current); WListbox *listbox_new (int x, int y, int width, int height, lcback callback); WGroupbox *groupbox_new (int x, int y, int width, int height, const char *title); /* Input lines */ -void winput_set_origin (WInput *i, int x, int field_len); +void winput_set_origin (WInput *i, int x, int field_width); cb_ret_t handle_char (WInput *in, int c_code); int is_in_input_map (WInput *in, int c_code); void update_input (WInput *in, int clear_first);