1
1

patches by Rostislav Beneš: mc-06-input

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
Этот коммит содержится в:
Slava Zanko 2008-12-29 00:52:45 +02:00
родитель 9450950fd4
Коммит 7f71be0d63
3 изменённых файлов: 340 добавлений и 198 удалений

Просмотреть файл

@ -41,6 +41,7 @@
#include "complete.h" #include "complete.h"
#include "main.h" #include "main.h"
#include "key.h" /* XCTRL and ALT macros */ #include "key.h" /* XCTRL and ALT macros */
#include "strutil.h"
typedef char *CompletionFunction (char *, int); 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. */ /* Now that we have some state, we can read the directory. */
while (directory && (entry = mc_readdir (directory))){ while (directory && (entry = mc_readdir (directory))){
if (!str_is_valid_string (entry->d_name)) continue;
/* Special case for no filename. /* Special case for no filename.
All entries except "." and ".." match. */ All entries except "." and ".." match. */
if (!filename_len){ if (!filename_len){
@ -273,20 +276,24 @@ static void fetch_hosts (const char *filename)
{ {
FILE *file = fopen (filename, "r"); FILE *file = fopen (filename, "r");
char buffer[256], *name; char buffer[256], *name;
register int i, start; char *start;
char *bi;
if (!file) if (!file)
return; return;
while (fgets (buffer, 255, file) != NULL){ while (fgets (buffer, 255, file) != NULL){
/* Skip to first character. */ /* 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... */ /* Ignore comments... */
if (buffer[i] == '#') if (bi[0] == '#')
continue; continue;
/* Handle $include. */ /* Handle $include. */
if (!strncmp (buffer + i, "$include ", 9)){ if (!strncmp (bi, "$include ", 9)){
char *includefile = buffer + i + 9; char *includefile = bi + 9;
char *t; char *t;
/* Find start of filename. */ /* Find start of filename. */
@ -295,8 +302,8 @@ static void fetch_hosts (const char *filename)
t = includefile; t = includefile;
/* Find end of filename. */ /* Find end of filename. */
while (*t && !cr_whitespace (*t)) while (t[0] != '\0' && !str_isspace (t))
t++; str_next_char (&t);
*t = '\0'; *t = '\0';
fetch_hosts (includefile); fetch_hosts (includefile);
@ -304,19 +311,22 @@ static void fetch_hosts (const char *filename)
} }
/* Skip IP #s. */ /* Skip IP #s. */
while (buffer[i] && !cr_whitespace (buffer[i])) while (bi[0] != '\0' && !str_isspace (bi))
i++; str_next_char (&bi);
/* Get the host names separated by white space. */ /* Get the host names separated by white space. */
while (buffer[i] && buffer[i] != '#'){ while (bi[0] != '\0' && bi[0] != '#'){
while (buffer[i] && cr_whitespace (buffer[i])) while (bi[0] != '\0' && str_isspace (bi))
i++; str_next_char (&bi);
if (buffer[i] == '#') if (bi[0] == '#')
continue; continue;
for (start = i; buffer[i] && !cr_whitespace (buffer[i]); i++); for (start = bi;
if (i - start == 0) bi[0] != '\0' && !str_isspace (bi);
continue; str_next_char (&bi));
name = g_strndup (buffer + start, i - start);
if (bi - start == 0) continue;
name = g_strndup (start, bi - start);
{ {
char **host_p; char **host_p;
@ -566,19 +576,30 @@ completion_matches (char *text, CompletionFunction entry_function)
j = i + 1; j = i + 1;
while (j < matches + 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++) for (si = match_list[i], sj = match_list[j];
if (c1 != c2) break; 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]); g_free (match_list [j]);
j++; j++;
if (j > matches) if (j > matches)
break; break;
continue; /* Look for a run of equal strings */ continue; /* Look for a run of equal strings */
} else } 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 */ if (i + 1 != j) /* So there's some gap */
match_list [i + 1] = match_list [j]; match_list [i + 1] = match_list [j];
i++; j++; i++; j++;
@ -598,7 +619,8 @@ completion_matches (char *text, CompletionFunction entry_function)
static int static int
check_is_cd (const char *text, int start, int flags) 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) if (flags & INPUT_COMPLETE_CD)
return 1; return 1;
@ -607,14 +629,18 @@ check_is_cd (const char *text, int start, int flags)
return 0; return 0;
/* Skip initial spaces */ /* Skip initial spaces */
p = text; p = (char*)text;
q = text + start; q = (char*)text + start;
while (p < q && *p && isspace ((unsigned char) *p)) while (p < q && p[0] != '\0' && str_isspace (p))
p++; str_next_char (&p);
/* Check if the command is "cd" and the cursor is after it */ /* Check if the command is "cd" and the cursor is after it */
if (p[0] == 'c' && p[1] == 'd' && isspace ((unsigned char) p[2]) text+= p[0] == 'c';
&& (p + 2 < q)) str_next_char (&p);
text+= p[0] == 'd';
str_next_char (&p);
text+= str_isspace (p);
if (test == 3 && (p < q))
return 1; return 1;
return 0; return 0;
@ -624,44 +650,44 @@ check_is_cd (const char *text, int start, int flags)
static char ** static char **
try_complete (char *text, int *start, int *end, int flags) try_complete (char *text, int *start, int *end, int flags)
{ {
int in_command_position = 0, i; int in_command_position = 0;
char *word, c; char *word;
char **matches = NULL; char **matches = NULL;
const char *command_separator_chars = ";|&{(`"; const char *command_separator_chars = ";|&{(`";
char *p = NULL, *q = NULL, *r = NULL; char *p = NULL, *q = NULL, *r = NULL;
int is_cd = check_is_cd (text, *start, flags); int is_cd = check_is_cd (text, *start, flags);
char *ti;
ignore_filenames = 0; ignore_filenames = 0;
c = text [*end]; word = g_strndup (text + *start, *end - *start);
text [*end] = 0;
word = g_strdup (text + *start);
text [*end] = c;
/* Determine if this could be a command word. It is if it appears at /* 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 the start of the line (ignoring preceding whitespace), or if it
appears after a character that separates commands. And we have to appears after a character that separates commands. And we have to
be in a INPUT_COMPLETE_COMMANDS flagged Input line. */ be in a INPUT_COMPLETE_COMMANDS flagged Input line. */
if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS)){ if (!is_cd && (flags & INPUT_COMPLETE_COMMANDS)){
i = *start - 1; ti = str_get_prev_char (&text[*start]);
while (i > -1 && (text[i] == ' ' || text[i] == '\t')) while (ti > text && (ti[0] == ' ' || ti[0] == '\t'))
i--; str_prev_char (&ti);
if (i < 0)
if (ti <= text&& (ti[0] == ' ' || ti[0] == '\t'))
in_command_position++; 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; register int this_char, prev_char;
in_command_position++; in_command_position++;
if (i){ if (ti > text){
/* Handle the two character tokens `>&', `<&', and `>|'. /* Handle the two character tokens `>&', `<&', and `>|'.
We are not in a command position after one of these. */ We are not in a command position after one of these. */
this_char = text[i]; this_char = ti[0];
prev_char = text[i - 1]; prev_char = str_get_prev_char (ti)[0];
if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) || if ((this_char == '&' && (prev_char == '<' || prev_char == '>')) ||
(this_char == '|' && prev_char == '>')) (this_char == '|' && prev_char == '>'))
in_command_position = 0; 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; in_command_position = 0;
} }
} }
@ -675,15 +701,15 @@ try_complete (char *text, int *start, int *end, int flags)
r = strrchr (word, '@'); r = strrchr (word, '@');
if (q && q [1] == '(' && INPUT_COMPLETE_COMMANDS){ if (q && q [1] == '(' && INPUT_COMPLETE_COMMANDS){
if (q > p) if (q > p)
p = q + 1; p = str_get_next_char (q);
q = NULL; q = NULL;
} }
/* Command substitution? */ /* Command substitution? */
if (p > q && p > r){ 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) if (matches)
*start += p + 1 - word; *start += str_get_next_char (p) - word;
} }
/* Variable name? */ /* Variable name? */
@ -721,9 +747,9 @@ try_complete (char *text, int *start, int *end, int flags)
if (!matches && is_cd && *word != PATH_SEP && *word != '~'){ if (!matches && is_cd && *word != PATH_SEP && *word != '~'){
char *p, *q = text + *start; 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)) 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){ if (p == q){
char * const cdpath_ref = g_strdup (getenv ("CDPATH")); char * const cdpath_ref = g_strdup (getenv ("CDPATH"));
char *cdpath = cdpath_ref; char *cdpath = cdpath_ref;
@ -747,7 +773,7 @@ try_complete (char *text, int *start, int *end, int flags)
g_free (r); g_free (r);
} }
*s = c; *s = c;
cdpath = s + 1; cdpath = str_get_next_char (s);
} }
g_free (cdpath_ref); g_free (cdpath_ref);
} }
@ -776,49 +802,57 @@ static WInput *input;
static int min_end; static int min_end;
static int start, 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; int buff_len = str_length (in->buffer);
if (strlen (in->buffer) + len >= (size_t) in->current_max_len){
size = min (size, (ssize_t) strlen (text)) + start - end;
if (strlen (in->buffer) + size >= (size_t) in->current_max_size){
/* Expand the buffer */ /* 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){ if (narea){
in->buffer = 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 (strlen (in->buffer)+1 < (size_t) in->current_max_size){
if (len > 0){ if (size > 0){
int i = strlen (&in->buffer [end]); int i = strlen (&in->buffer [end]);
for (; i >= 0; i--) for (; i >= 0; i--)
in->buffer [end + len + i] = in->buffer [end + i]; in->buffer [end + size + i] = in->buffer [end + i];
} else if (len < 0){ } else if (size < 0){
char *p = in->buffer + end + len, *q = in->buffer + end; char *p = in->buffer + end + size, *q = in->buffer + end;
while (*q) while (*q)
*(p++) = *(q++); *(p++) = *(q++);
*p = 0; *p = 0;
} }
memcpy (in->buffer + start, text, len - start + end); memcpy (in->buffer + start, text, size - start + end);
in->point += len; in->point+= str_length (in->buffer) - buff_len;
update_input (in, 1); update_input (in, 1);
end += len; end+= size;
} }
return len != 0; return size != 0;
} }
static cb_ret_t static cb_ret_t
query_callback (Dlg_head *h, dlg_msg_t msg, int parm) query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
{ {
static char buff[MB_LEN_MAX] = "";
static int bl = 0;
switch (msg) { switch (msg) {
case DLG_KEY: case DLG_KEY:
switch (parm) { switch (parm) {
case KEY_LEFT: case KEY_LEFT:
case KEY_RIGHT: case KEY_RIGHT:
bl = 0;
h->ret_value = 0; h->ret_value = 0;
dlg_stop (h); dlg_stop (h);
return MSG_HANDLED; return MSG_HANDLED;
case KEY_BACKSPACE: case KEY_BACKSPACE:
bl = 0;
if (end == min_end) { if (end == min_end) {
h->ret_value = 0; h->ret_value = 0;
dlg_stop (h); dlg_stop (h);
@ -828,13 +862,13 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
e1 = e = ((WListbox *) (h->current))->list; e1 = e = ((WListbox *) (h->current))->list;
do { do {
if (!strncmp if (!strncmp (input->buffer + start,
(input->buffer + start, e1->text, e1->text, end - start - 1)) {
end - start - 1)) {
listbox_select_entry ((WListbox *) (h->current), listbox_select_entry ((WListbox *) (h->current), e1);
e1); end = str_get_prev_char (&(input->buffer[end]))
- input->buffer;
handle_char (input, parm); handle_char (input, parm);
end--;
send_message (h->current, WIDGET_DRAW, 0); send_message (h->current, WIDGET_DRAW, 0);
break; break;
} }
@ -844,7 +878,8 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
return MSG_HANDLED; return MSG_HANDLED;
default: default:
if (parm > 0xff || !is_printable (parm)) { if (parm < 32 || parm > 256) {
bl = 0;
if (is_in_input_map (input, parm) == 2) { if (is_in_input_map (input, parm) == 2) {
if (end == min_end) if (end == min_end)
return MSG_HANDLED; return MSG_HANDLED;
@ -860,21 +895,43 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
int low = 4096; int low = 4096;
char *last_text = NULL; 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; e1 = e = ((WListbox *) (h->current))->list;
do { do {
if (!strncmp if (!strncmp (input->buffer + start,
(input->buffer + start, e1->text, end - start)) { e1->text, end - start)) {
if (e1->text[end - start] == parm) {
if (strncmp (&e1->text[end - start], buff, bl) == 0) {
if (need_redraw) { 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; last_text = e1->text;
need_redraw = 2; need_redraw = 2;
} else { } else {
@ -895,6 +952,7 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
h->ret_value = B_ENTER; h->ret_value = B_ENTER;
dlg_stop (h); dlg_stop (h);
} }
bl = 0;
} }
return MSG_HANDLED; return MSG_HANDLED;
} }
@ -911,15 +969,19 @@ query_callback (Dlg_head *h, dlg_msg_t msg, int parm)
static int static int
complete_engine (WInput *in, int what_to_do) 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); free_completions (in);
if (!in->completions){ if (!in->completions){
end = in->point; end = str_offset_to_pos (in->buffer, in->point);
for (start = end ? end - 1 : 0; start > -1; start--) for (s = in->point ? in->point - 1 : 0; s >= 0; s--) {
if (strchr (" \t;|<>", in->buffer [start])) 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; break;
if (start < end) }
start++; }
in->completions = try_complete (in->buffer, &start, &end, in->completion_flags); in->completions = try_complete (in->buffer, &start, &end, in->completion_flags);
} }
if (in->completions){ if (in->completions){
@ -941,7 +1003,7 @@ complete_engine (WInput *in, int what_to_do)
WListbox *query_list; WListbox *query_list;
for (p=in->completions + 1; *p; count++, p++) for (p=in->completions + 1; *p; count++, p++)
if ((i = strlen (*p)) > maxlen) if ((i = str_term_width1 (*p)) > maxlen)
maxlen = i; maxlen = i;
start_x = in->widget.x; start_x = in->widget.x;
start_y = in->widget.y; start_y = in->widget.y;
@ -957,7 +1019,7 @@ complete_engine (WInput *in, int what_to_do)
h = LINES - start_y - 1; 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; w = maxlen + 4;
if (x + w > COLS) if (x + w > COLS)
x = COLS - w; x = COLS - w;
@ -999,6 +1061,8 @@ void complete (WInput *in)
{ {
int engine_flags; int engine_flags;
if (!str_is_valid_string (in->buffer)) return;
if (in->completions) if (in->completions)
engine_flags = DO_QUERY; engine_flags = DO_QUERY;
else else

Просмотреть файл

@ -789,27 +789,21 @@ gauge_new (int y, int x, int shown, int max, int current)
#endif #endif
#define should_show_history_button(in) \ #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) static void draw_history_button (WInput * in)
{ {
char c; char c;
c = in->history->next ? (in->history->prev ? '|' : 'v') : '^'; 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 #ifdef LARGE_HISTORY_BUTTON
{ {
Dlg_head *h; Dlg_head *h;
h = in->widget.parent; h = in->widget.parent;
#if 0
attrset (NORMALC); /* button has the same color as other buttons */
addstr ("[ ]");
attrset (HOT_NORMALC);
#else
attrset (NORMAL_COLOR); attrset (NORMAL_COLOR);
addstr ("[ ]"); addstr ("[ ]");
/* Too distracting: attrset (MARKED_COLOR); */ /* Too distracting: attrset (MARKED_COLOR); */
#endif widget_move (&in->widget, 0, in->field_width - HISTORY_BUTTON_WIDTH + 1);
widget_move (&in->widget, 0, in->field_len - HISTORY_BUTTON_WIDTH + 1);
addch (c); addch (c);
} }
#else #else
@ -829,9 +823,10 @@ void
update_input (WInput *in, int clear_first) update_input (WInput *in, int clear_first)
{ {
int has_history = 0; int has_history = 0;
int i, j; int i;
unsigned char c; int buf_len = str_length (in->buffer);
int buf_len = strlen (in->buffer); const char *cp;
int pw;
if (should_show_history_button (in)) if (should_show_history_button (in))
has_history = HISTORY_BUTTON_WIDTH; has_history = HISTORY_BUTTON_WIDTH;
@ -839,12 +834,15 @@ update_input (WInput *in, int clear_first)
if (in->disable_update) if (in->disable_update)
return; return;
pw = str_term_width2 (in->buffer, in->point);
/* Make the point visible */ /* Make the point visible */
if ((in->point < in->first_shown) || if ((pw < in->term_first_shown) ||
(in->point >= in->first_shown+in->field_len - has_history)){ (pw >= in->term_first_shown + in->field_width - has_history)) {
in->first_shown = in->point - (in->field_len / 3);
if (in->first_shown < 0) in->term_first_shown = pw - (in->field_width / 3);
in->first_shown = 0; if (in->term_first_shown < 0)
in->term_first_shown = 0;
} }
/* Adjust the mark */ /* Adjust the mark */
@ -857,28 +855,29 @@ update_input (WInput *in, int clear_first)
attrset (in->color); attrset (in->color);
widget_move (&in->widget, 0, 0); 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++){ if (!in->is_password) {
c = in->buffer [j++]; addstr (str_term_substring (in->buffer, in->term_first_shown,
c = is_printable (c) ? c : '.'; in->field_width - has_history));
if (in->is_password) } else {
c = '*'; cp = in->buffer;
addch (c); 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) if (clear_first)
in->first = 0; in->first = 0;
} }
void 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->widget.x = x;
in->field_len = in->widget.cols = field_len; in->field_width = in->widget.cols = field_width;
update_input (in, 0); update_input (in, 0);
} }
@ -997,11 +996,7 @@ history_put (const char *input_name, GList *h)
static const char * static const char *
i18n_htitle (void) i18n_htitle (void)
{ {
static const char *history_title = NULL; return _(" History ");
if (history_title == NULL)
history_title = _(" History ");
return history_title;
} }
static WLEntry *listbox_select_pos (WListbox *l, WLEntry *base, int 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) show_hist (GList *history, int widget_x, int widget_y)
{ {
GList *hi, *z; 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; int x, y, w, h;
char *q, *r = 0; char *q, *r = 0;
Dlg_head *query_dlg; Dlg_head *query_dlg;
@ -1034,7 +1029,7 @@ show_hist (GList *history, int widget_x, int widget_y)
z = g_list_first (history); z = g_list_first (history);
hi = z; hi = z;
while (hi) { while (hi) {
if ((i = strlen ((char *) hi->data)) > maxlen) if ((i = str_term_width1 ((char *) hi->data)) > maxlen)
maxlen = i; maxlen = i;
count++; count++;
hi = g_list_next (hi); hi = g_list_next (hi);
@ -1202,37 +1197,75 @@ new_input (WInput *in)
if (in->buffer) if (in->buffer)
push_history (in, in->buffer); push_history (in, in->buffer);
in->need_push = 1; in->need_push = 1;
in->buffer [0] = 0; in->buffer[0] = '\0';
in->point = 0; in->point = 0;
in->charpoint = 0;
in->mark = 0; in->mark = 0;
free_completions (in); free_completions (in);
update_input (in, 0); 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 static cb_ret_t
insert_char (WInput *in, int c_code) insert_char (WInput *in, int c_code)
{ {
size_t i; size_t i;
int res;
if (c_code == -1) if (c_code == -1)
return MSG_NOT_HANDLED; 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; 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 */ /* 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){ if (narea){
in->buffer = 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]); if (strlen (in->buffer) + in->charpoint < in->current_max_size) {
for (i = l+1; i > 0; i--) /* bytes from begin */
in->buffer [in->point+i] = in->buffer [in->point+i-1]; size_t ins_point = str_offset_to_pos (in->buffer, in->point);
in->buffer [in->point] = c_code; /* 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->point++;
} }
in->charpoint = 0;
return MSG_HANDLED; return MSG_HANDLED;
} }
@ -1240,54 +1273,70 @@ static void
beginning_of_line (WInput *in) beginning_of_line (WInput *in)
{ {
in->point = 0; in->point = 0;
in->charpoint = 0;
} }
static void static void
end_of_line (WInput *in) end_of_line (WInput *in)
{ {
in->point = strlen (in->buffer); in->point = str_length (in->buffer);
in->charpoint = 0;
} }
static void static void
backward_char (WInput *in) backward_char (WInput *in)
{ {
if (in->point) const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
in->point--;
if (in->point > 0) {
in->point-= str_cprev_noncomb_char (&act, in->buffer);
}
in->charpoint = 0;
} }
static void static void
forward_char (WInput *in) forward_char (WInput *in)
{ {
if (in->buffer [in->point]) const char *act = in->buffer + str_offset_to_pos (in->buffer, in->point);
in->point++; if (act[0] != '\0') {
in->point+= str_cnext_noncomb_char (&act);
}
in->charpoint = 0;
} }
static void static void
forward_word (WInput * in) 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 while (p[0] != '\0' && (str_isspace (p) || str_ispunct (p))) {
&& (isspace ((unsigned char) *p) str_cnext_char (&p);
|| ispunct ((unsigned char) *p))) in->point++;
p++; }
while (*p && isalnum ((unsigned char) *p)) while (p[0] != '\0' && !str_isspace (p) && !str_ispunct (p)) {
p++; str_cnext_char (&p);
in->point = p - in->buffer; in->point++;
}
} }
static void static void
backward_word (WInput *in) 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)) while ((p != in->buffer) && (p[0] == '\0')) {
|| ispunct ((unsigned char)
*(p - 1))))
p--; p--;
while (p - 1 > in->buffer - 1 && isalnum ((unsigned char) *(p - 1))) in->point--;
p--; }
in->point = p - in->buffer;
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 static void
@ -1316,23 +1365,29 @@ key_ctrl_right (WInput *in)
static void static void
backward_delete (WInput *in) 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; 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->need_push = 1;
in->point--; in->point = start;
} }
static void static void
delete_char (WInput *in) 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++) move_buffer_backward(in, in->point, end);
in->buffer [i] = in->buffer [i+1]; in->charpoint = 0;
in->need_push = 1; in->need_push = 1;
} }
@ -1347,7 +1402,10 @@ copy_region (WInput *in, int x_first, int x_last)
g_free (kill_buffer); 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 static void
@ -1355,11 +1413,15 @@ delete_region (WInput *in, int x_first, int x_last)
{ {
int first = min (x_first, x_last); int first = min (x_first, x_last);
int last = max (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->point = first;
in->mark = 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; in->need_push = 1;
} }
@ -1376,6 +1438,8 @@ kill_word (WInput *in)
copy_region (in, old_point, new_point); copy_region (in, old_point, new_point);
delete_region (in, old_point, new_point); delete_region (in, old_point, new_point);
in->need_push = 1; in->need_push = 1;
in->charpoint = 0;
in->charpoint = 0;
} }
static void static void
@ -1419,16 +1483,20 @@ yank (WInput *in)
if (!kill_buffer) if (!kill_buffer)
return; return;
in->charpoint = 0;
for (p = kill_buffer; *p; p++) for (p = kill_buffer; *p; p++)
insert_char (in, *p); insert_char (in, *p);
in->charpoint = 0;
} }
static void static void
kill_line (WInput *in) kill_line (WInput *in)
{ {
int chp = str_offset_to_pos (in->buffer, in->point);
g_free (kill_buffer); g_free (kill_buffer);
kill_buffer = g_strdup (&in->buffer [in->point]); kill_buffer = g_strdup (&in->buffer[chp]);
in->buffer [in->point] = 0; in->buffer[chp] = '\0';
in->charpoint = 0;
} }
void void
@ -1437,10 +1505,11 @@ assign_text (WInput *in, const char *text)
free_completions (in); free_completions (in);
g_free (in->buffer); g_free (in->buffer);
in->buffer = g_strdup (text); /* was in->buffer->text */ in->buffer = g_strdup (text); /* was in->buffer->text */
in->current_max_len = strlen (in->buffer) + 1; in->current_max_size = strlen (in->buffer) + 1;
in->point = strlen (in->buffer); in->point = str_length (in->buffer);
in->mark = 0; in->mark = 0;
in->need_push = 1; in->need_push = 1;
in->charpoint = 0;
} }
static void static void
@ -1564,9 +1633,10 @@ is_in_input_map (WInput *in, int c_code)
static void static void
port_region_marked_for_delete (WInput *in) port_region_marked_for_delete (WInput *in)
{ {
*in->buffer = 0; in->buffer[0] = '\0';
in->point = 0; in->point = 0;
in->first = 0; in->first = 0;
in->charpoint = 0;
} }
cb_ret_t cb_ret_t
@ -1595,7 +1665,7 @@ handle_char (WInput *in, int c_code)
} }
} }
if (!input_map [i].fn){ if (!input_map [i].fn){
if (c_code > 255 || !is_printable (c_code)) if (c_code > 255)
return MSG_NOT_HANDLED; return MSG_NOT_HANDLED;
if (in->first){ if (in->first){
port_region_marked_for_delete (in); port_region_marked_for_delete (in);
@ -1623,11 +1693,14 @@ stuff (WInput *in, const char *text, int insert_extra_space)
void void
input_set_point (WInput *in, int pos) input_set_point (WInput *in, int pos)
{ {
if (pos > in->current_max_len) int max_pos = str_length (in->buffer);
pos = in->current_max_len;
if (pos > max_pos)
pos = max_pos;
if (pos != in->point) if (pos != in->point)
free_completions (in); free_completions (in);
in->point = pos; in->point = pos;
in->charpoint = 0;
update_input (in, 1); update_input (in, 1);
} }
@ -1668,7 +1741,8 @@ input_callback (Widget *w, widget_msg_t msg, int parm)
return MSG_HANDLED; return MSG_HANDLED;
case WIDGET_CURSOR: 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; return MSG_HANDLED;
case WIDGET_DESTROY: case WIDGET_DESTROY:
@ -1688,15 +1762,17 @@ input_event (Gpm_Event * event, void *data)
if (event->type & (GPM_DOWN | GPM_DRAG)) { if (event->type & (GPM_DOWN | GPM_DRAG)) {
dlg_select_widget (in); 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)) { && should_show_history_button (in)) {
do_show_hist (in); do_show_hist (in);
} else { } else {
in->point = strlen (in->buffer); in->point = str_length (in->buffer);
if (event->x - in->first_shown - 1 < in->point) if (event->x + in->term_first_shown - 1 <
in->point = event->x - in->first_shown - 1; str_term_width1 (in->buffer))
if (in->point < 0)
in->point = 0; in->point = str_column_to_pos (in->buffer, event->x
+ in->term_first_shown - 1);
} }
update_input (in, 1); update_input (in, 1);
} }
@ -1704,13 +1780,13 @@ input_event (Gpm_Event * event, void *data)
} }
WInput * 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) const char *histname)
{ {
WInput *in = g_new (WInput, 1); WInput *in = g_new (WInput, 1);
int initial_buffer_len; 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 */ /* history setup */
in->history = NULL; 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 = ""; def_text = "";
if (def_text == INPUT_LAST_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) if (in->history->data)
def_text = (char *) 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->widget.options |= W_IS_INPUT;
in->completions = NULL; in->completions = NULL;
in->completion_flags = in->completion_flags =
INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_HOSTNAMES |
INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES; INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES;
in->current_max_len = initial_buffer_len; in->current_max_size = initial_buffer_len;
in->buffer = g_malloc (initial_buffer_len); in->buffer = g_new (char, initial_buffer_len);
in->color = color; in->color = color;
in->field_len = len; in->field_width = width;
in->first = 1; in->first = 1;
in->first_shown = 0; in->term_first_shown = 0;
in->disable_update = 0; in->disable_update = 0;
in->mark = 0; in->mark = 0;
in->need_push = 1; in->need_push = 1;
in->is_password = 0; in->is_password = 0;
strcpy (in->buffer, def_text); strcpy (in->buffer, def_text);
in->point = strlen (in->buffer); in->point = str_length (in->buffer);
in->charpoint = 0;
return in; return in;
} }
/* Listbox widget */ /* Listbox widget */
/* Should draw the scrollbar, but currently draws only /* Should draw the scrollbar, but currently draws only

Просмотреть файл

@ -74,11 +74,11 @@ char *show_hist (GList *history, int widget_y, int widget_x);
typedef struct { typedef struct {
Widget widget; Widget widget;
int point; /* cursor position in the input line */ int point; /* cursor position in the input line in characters */
int mark; /* The mark position */ int mark; /* The mark position in characters */
int first_shown; /* Index of the first shown character */ int term_first_shown; /* column of the first shown character */
int current_max_len; /* Maximum length of input line */ size_t current_max_size; /* Maximum length of input line (bytes) */
int field_len; /* Length of the editing field */ int field_width; /* width of the editing field */
int color; /* color used */ int color; /* color used */
int first; /* Is first keystroke? */ int first; /* Is first keystroke? */
int disable_update; /* Do we want to skip updates? */ int disable_update; /* Do we want to skip updates? */
@ -89,6 +89,8 @@ typedef struct {
char **completions; /* Possible completions array */ char **completions; /* Possible completions array */
int completion_flags; /* INPUT_COMPLETE* bitwise flags(complete.h) */ int completion_flags; /* INPUT_COMPLETE* bitwise flags(complete.h) */
char *history_name; /* name of history for loading and saving */ 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; } WInput;
/* For history load-save functions */ /* 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); bcback callback);
WRadio *radio_new (int y, int x, int count, const char **text); WRadio *radio_new (int y, int x, int count, const char **text);
WCheck *check_new (int y, int x, int state, 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); WLabel *label_new (int y, int x, const char *text);
WGauge *gauge_new (int y, int x, int shown, int max, int current); 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); 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); WGroupbox *groupbox_new (int x, int y, int width, int height, const char *title);
/* Input lines */ /* 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); cb_ret_t handle_char (WInput *in, int c_code);
int is_in_input_map (WInput *in, int c_code); int is_in_input_map (WInput *in, int c_code);
void update_input (WInput *in, int clear_first); void update_input (WInput *in, int clear_first);