X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fwreadln.c;h=c6dbda7a195b74d62772865b800401de095199e6;hb=0796b270b1699336f29908d11bca92d64da0653c;hp=fd653329b06f1d1f4210df4b804c969ba6da57bb;hpb=6e3f91f094c0c95036e11a4f070494ab850c3a6d;p=ncmpc.git diff --git a/src/wreadln.c b/src/wreadln.c index fd65332..c6dbda7 100644 --- a/src/wreadln.c +++ b/src/wreadln.c @@ -1,811 +1,649 @@ -/* - * $Id$ - * - * (c) 2004 by Kalle Wallin - * +/* ncmpc (Ncurses MPD Client) + * (c) 2004-2010 The Music Player Daemon Project + * Project homepage: http://musicpd.org + * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "wreadln.h" +#include "charset.h" +#include "screen_utils.h" #include "config.h" +#include #include #include #include -#ifdef USE_NCURSESW -#include -#else -#include +#if defined(ENABLE_WIDE) || defined(ENABLE_MULTIBYTE) +#include #endif -#include "wreadln.h" - #define KEY_CTRL_A 1 +#define KEY_CTRL_B 2 #define KEY_CTRL_C 3 -#define KEY_CTRL_D 4 +#define KEY_CTRL_D 4 #define KEY_CTRL_E 5 +#define KEY_CTRL_F 6 #define KEY_CTRL_G 7 #define KEY_CTRL_K 11 +#define KEY_CTRL_N 14 +#define KEY_CTRL_P 16 #define KEY_CTRL_U 21 +#define KEY_CTRL_W 23 #define KEY_CTRL_Z 26 #define KEY_BCKSPC 8 #define TAB 9 -#define WRLN_MAX_LINE_SIZE 1024 -#define WRLN_MAX_HISTORY_LENGTH 32 - -guint wrln_max_line_size = WRLN_MAX_LINE_SIZE; -guint wrln_max_history_length = WRLN_MAX_HISTORY_LENGTH; -wrln_wgetch_fn_t wrln_wgetch = NULL; +struct wreadln { + /** the ncurses window where this field is displayed */ + WINDOW *const w; + + /** the origin coordinates in the window */ + unsigned x, y; + + /** the screen width of the input field */ + unsigned width; + + /** is the input masked, i.e. characters displayed as '*'? */ + const gboolean masked; + + /** the byte position of the cursor */ + size_t cursor; + + /** the byte position displayed at the origin (for horizontal + scrolling) */ + size_t start; + + /** the current value */ + gchar line[1024]; +}; + +/** max items stored in the history list */ +static const guint wrln_max_history_length = 32; + +#ifndef NCMPC_MINI void *wrln_completion_callback_data = NULL; wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL; wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL; +#endif -extern void sigstop(void); -extern void screen_bell(void); -extern size_t my_strlen(char *str); - -#ifndef USE_NCURSESW -/* move the cursor one step to the right */ -static inline void cursor_move_right(gint *cursor, - gint *start, - gint width, - gint x0, - gint x1, - gchar *line) +/** converts a byte position to a screen column */ +static unsigned +byte_to_screen(const gchar *data, size_t x) { - if( *cursor < strlen(line) && *cursor= x1 && *start<*cursor-width+1) - (*start)++; - } +#if defined(ENABLE_WIDE) || defined(ENABLE_MULTIBYTE) + gchar *dup; + char *p; + unsigned width; + + assert(x <= strlen(data)); + + dup = g_strdup(data); + dup[x] = 0; + p = replace_locale_to_utf8(dup); + + width = utf8_width(p); + g_free(p); + + return width; +#else + (void)data; + + return (unsigned)x; +#endif } -/* move the cursor one step to the left */ -static inline void cursor_move_left(gint *cursor, - gint *start) +/** finds the first character which doesn't fit on the screen */ +static size_t +screen_to_bytes(const gchar *data, unsigned width) { - if( *cursor > 0 ) - { - if( *cursor==*start && *start > 0 ) - (*start)--; - (*cursor)--; - } +#if defined(ENABLE_WIDE) || defined(ENABLE_MULTIBYTE) + size_t length = strlen(data); + gchar *dup = g_strdup(data); + char *p; + unsigned p_width; + + while (true) { + dup[length] = 0; + p = locale_to_utf8(dup); + p_width = utf8_width(p); + g_free(p); + if (p_width <= width) + break; + + --length; + } + + g_free(dup); + + return length; +#else + (void)data; + + return (size_t)width; +#endif } -/* move the cursor to the end of the line */ -static inline void cursor_move_to_eol(gint *cursor, - gint *start, - gint width, - gint x0, - gint x1, - gchar *line) +/** returns the screen column where the cursor is located */ +static unsigned +cursor_column(const struct wreadln *wr) { - *cursor = strlen(line); - if( *cursor+x0 >= x1 ) - *start = *cursor-width+1; + return byte_to_screen(wr->line + wr->start, + wr->cursor - wr->start); } -/* draw line buffer and update cursor position */ -static inline void drawline(gint cursor, - gint start, - gint width, - gint x0, - gint x1, - gint y, - gboolean masked, - gchar *line, - WINDOW *w) +/** returns the offset in the string to align it at the right border + of the screen */ +static inline size_t +right_align_bytes(const gchar *data, size_t right, unsigned width) { - wmove(w, y, x0); - /* clear input area */ - whline(w, ' ', width); - /* print visible part of the line buffer */ - if(masked == TRUE) whline(w, '*', my_strlen(line)-start); - else waddnstr(w, line+start, width); - /* move the cursor to the correct position */ - wmove(w, y, x0 + cursor-start); - /* tell ncurses to redraw the screen */ - doupdate(); -} +#if defined(ENABLE_WIDE) || defined(ENABLE_MULTIBYTE) + gchar *dup; + size_t start = 0; + assert(right <= strlen(data)); -/* libcurses version */ + dup = g_strdup(data); + dup[right] = 0; -gchar * -_wreadln(WINDOW *w, - gchar *prompt, - gchar *initial_value, - gint x1, - GList **history, - GCompletion *gcmp, - gboolean masked) -{ - GList *hlist = NULL, *hcurrent = NULL; - gchar *line; - gint x0, y, width; - gint cursor = 0, start = 0; - gint key = 0, i; - - /* allocate a line buffer */ - line = g_malloc0(wrln_max_line_size); - /* turn off echo */ - noecho(); - /* make shure the cursor is visible */ - curs_set(1); - /* print prompt string */ - if( prompt ) - waddstr(w, prompt); - /* retrive y and x0 position */ - getyx(w, y, x0); - /* check the x1 value */ - if( x1<=x0 || x1>COLS ) - x1 = COLS; - width = x1-x0; - /* clear input area */ - mvwhline(w, y, x0, ' ', width); - - if( history ) - { - /* append the a new line to our history list */ - *history = g_list_append(*history, g_malloc0(wrln_max_line_size)); - /* hlist points to the current item in the history list */ - hlist = g_list_last(*history); - hcurrent = hlist; - } - - if( initial_value == (char *) -1 ) - { - /* get previous history entry */ - if( history && hlist->prev ) - { - if( hlist==hcurrent ) - { - /* save the current line */ - g_strlcpy(hlist->data, line, wrln_max_line_size); - } - /* get previous line */ - hlist = hlist->prev; - g_strlcpy(line, hlist->data, wrln_max_line_size); - } - cursor_move_to_eol(&cursor, &start, width, x0, x1, line); - drawline(cursor, start, width, x0, x1, y, masked, line, w); - } - else if( initial_value ) - { - /* copy the initial value to the line buffer */ - g_strlcpy(line, initial_value, wrln_max_line_size); - cursor_move_to_eol(&cursor, &start, width, x0, x1, line); - drawline(cursor, start, width, x0, x1, y, masked, line, w); - } - - while( key!=13 && key!='\n' ) - { - if( wrln_wgetch ) - key = wrln_wgetch(w); - else - key = wgetch(w); - - /* check if key is a function key */ - for(i=0; i<63; i++) - if( key==KEY_F(i) ) - { - key=KEY_F(1); - i=64; - } - - switch (key) - { -#ifdef HAVE_GETMOUSE - case KEY_MOUSE: /* ignore mouse events */ -#endif - case ERR: /* ingnore errors */ - break; - - case KEY_RESIZE: - /* a resize event */ - if( x1>COLS ) - { - x1=COLS; - width = x1-x0; - cursor_move_to_eol(&cursor, &start, width, x0, x1, line); - } - /* make shure the cursor is visible */ - curs_set(1); - break; - - case TAB: - if( gcmp ) - { - char *prefix = NULL; - GList *list; - - if(wrln_pre_completion_callback) - wrln_pre_completion_callback(gcmp, line, - wrln_completion_callback_data); - list = g_completion_complete(gcmp, line, &prefix); - if( prefix ) - { - g_strlcpy(line, prefix, wrln_max_line_size); - cursor_move_to_eol(&cursor, &start, width, x0, x1, line); - g_free(prefix); - } - else - screen_bell(); - if( wrln_post_completion_callback ) - wrln_post_completion_callback(gcmp, line, list, - wrln_completion_callback_data); - } - break; - - case KEY_CTRL_G: - screen_bell(); - g_free(line); - if( history ) - { - g_free(hcurrent->data); - hcurrent->data = NULL; - *history = g_list_delete_link(*history, hcurrent); - } - return NULL; - - case KEY_LEFT: - cursor_move_left(&cursor, &start); - break; - case KEY_RIGHT: - cursor_move_right(&cursor, &start, width, x0, x1, line); - break; - case KEY_HOME: - case KEY_CTRL_A: - cursor = 0; - start = 0; - break; - case KEY_END: - case KEY_CTRL_E: - cursor_move_to_eol(&cursor, &start, width, x0, x1, line); - break; - case KEY_CTRL_K: - line[cursor] = 0; - break; - case KEY_CTRL_U: - cursor = my_strlen(line); - for (i = 0;i < cursor; i++) - line[i] = '\0'; - cursor = 0; - break; - case 127: - case KEY_BCKSPC: /* handle backspace: copy all */ - case KEY_BACKSPACE: /* chars starting from curpos */ - if( cursor > 0 ) /* - 1 from buf[n+1] to buf */ - { - for (i = cursor - 1; line[i] != 0; i++) - line[i] = line[i + 1]; - cursor_move_left(&cursor, &start); - } - break; - case KEY_DC: /* handle delete key. As above */ - case KEY_CTRL_D: - if( cursor <= my_strlen(line) - 1 ) - { - for (i = cursor; line[i] != 0; i++) - line[i] = line[i + 1]; - } - break; - case KEY_UP: - /* get previous history entry */ - if( history && hlist->prev ) - { - if( hlist==hcurrent ) - { - /* save the current line */ - g_strlcpy(hlist->data, line, wrln_max_line_size); - } - /* get previous line */ - hlist = hlist->prev; - g_strlcpy(line, hlist->data, wrln_max_line_size); - } - cursor_move_to_eol(&cursor, &start, width, x0, x1, line); - break; - case KEY_DOWN: - /* get next history entry */ - if( history && hlist->next ) - { - /* get next line */ - hlist = hlist->next; - g_strlcpy(line, hlist->data, wrln_max_line_size); - } - cursor_move_to_eol(&cursor, &start, width, x0, x1, line); - break; - - case '\n': - case 13: - case KEY_IC: - case KEY_PPAGE: - case KEY_NPAGE: - case KEY_F(1): - /* ignore char */ - break; - default: - if (key >= 32) - { - if (strlen (line + cursor)) /* if the cursor is */ - { /* not at the last pos */ - gchar *tmp = 0; - gsize size = strlen(line + cursor) + 1; - - tmp = g_malloc0(size); - g_strlcpy (tmp, line + cursor, size); - line[cursor] = key; - line[cursor + 1] = 0; - g_strlcat (&line[cursor + 1], tmp, size); - g_free(tmp); - cursor_move_right(&cursor, &start, width, x0, x1, line); - } - else - { - line[cursor + 1] = 0; - line[cursor] = key; - cursor_move_right(&cursor, &start, width, x0, x1, line); + while (dup[start] != 0) { + char *p = locale_to_utf8(dup + start), *q; + unsigned p_width = utf8_width(p); + gunichar c; + + if (p_width < width) { + g_free(p); + break; } - } - } - drawline(cursor, start, width, x0, x1, y, masked, line, w); - } - - /* update history */ - if( history ) - { - if( strlen(line) ) - { - /* update the current history entry */ - size_t size = strlen(line)+1; - hcurrent->data = g_realloc(hcurrent->data, size); - g_strlcpy(hcurrent->data, line, size); - } - else - { - /* the line was empty - remove the current history entry */ - g_free(hcurrent->data); - hcurrent->data = NULL; - *history = g_list_delete_link(*history, hcurrent); + c = g_utf8_get_char(p); + p[g_unichar_to_utf8(c, NULL)] = 0; + q = utf8_to_locale(p); + g_free(p); + + start += strlen(q); + g_free(q); } - while( g_list_length(*history) > wrln_max_history_length ) - { - GList *first = g_list_first(*history); + g_free(dup); - /* remove the oldest history entry */ - g_free(first->data); - first->data = NULL; - *history = g_list_delete_link(*history, first); - } - } - - return g_realloc(line, strlen(line)+1); + return start; +#else + (void)data; + + return right >= width ? right + 1 - width : 0; +#endif +} + +/** returns the size (in bytes) of the next character */ +static inline size_t +next_char_size(const gchar *data) +{ +#if defined(ENABLE_WIDE) || defined(ENABLE_MULTIBYTE) + char *p = locale_to_utf8(data), *q; + gunichar c; + size_t size; + + c = g_utf8_get_char(p); + p[g_unichar_to_utf8(c, NULL)] = 0; + q = utf8_to_locale(p); + g_free(p); + + size = strlen(q); + g_free(q); + + return size; +#else + (void)data; + + return 1; +#endif } +/** returns the size (in bytes) of the previous character */ +static inline size_t +prev_char_size(const gchar *data, size_t x) +{ +#if defined(ENABLE_WIDE) || defined(ENABLE_MULTIBYTE) + char *p = locale_to_utf8(data), *q; + gunichar c; + size_t size; + + assert(x > 0); + + q = p; + while (true) { + c = g_utf8_get_char(q); + size = g_unichar_to_utf8(c, NULL); + if (size > x) + size = x; + x -= size; + if (x == 0) { + g_free(p); + return size; + } + + q += size; + } #else + (void)data; + (void)x; + + return 1; +#endif +} /* move the cursor one step to the right */ -static inline void cursor_move_right(gint *cursor, - gint *start, - gint width, - gint x0, - gint x1, - wchar_t *wline) +static inline void cursor_move_right(struct wreadln *wr) { - if( *cursor < wcslen(wline) && *cursor= x1 && *start<*cursor-width+1) - (*start)++; - } + size_t size; + + if (wr->line[wr->cursor] == 0) + return; + + size = next_char_size(wr->line + wr->cursor); + wr->cursor += size; + if (cursor_column(wr) >= wr->width) + wr->start = right_align_bytes(wr->line, wr->cursor, wr->width); } /* move the cursor one step to the left */ -static inline void cursor_move_left(gint *cursor, - gint *start, - gint width, - gint x0, - gint x1, - wchar_t *line) +static inline void cursor_move_left(struct wreadln *wr) { - if( *cursor > 0 ) - { - if( *cursor==*start && *start > 0 ) - (*start)--; - (*cursor)--; - } + size_t size; + + if (wr->cursor == 0) + return; + + size = prev_char_size(wr->line, wr->cursor); + assert(wr->cursor >= size); + wr->cursor -= size; + if (wr->cursor < wr->start) + wr->start = wr->cursor; } +/* move the cursor to the end of the line */ +static inline void cursor_move_to_eol(struct wreadln *wr) +{ + wr->cursor = strlen(wr->line); + if (cursor_column(wr) >= wr->width) + wr->start = right_align_bytes(wr->line, wr->cursor, wr->width); +} -static inline void backspace(gint *cursor, - gint *start, - gint width, - gint x0, - gint x1, - wchar_t *wline) +/* draw line buffer and update cursor position */ +static inline void drawline(const struct wreadln *wr) { - int i; - if( *cursor > 0 ) - { - for (i = *cursor - 1; wline[i] != 0; i++) - wline[i] = wline[i + 1]; - cursor_move_left(cursor, start, width, x0, x1, wline); - } + wmove(wr->w, wr->y, wr->x); + /* clear input area */ + whline(wr->w, ' ', wr->width); + /* print visible part of the line buffer */ + if (wr->masked) + whline(wr->w, '*', utf8_width(wr->line + wr->start)); + else + waddnstr(wr->w, wr->line + wr->start, + screen_to_bytes(wr->line, wr->width)); + /* move the cursor to the correct position */ + wmove(wr->w, wr->y, wr->x + cursor_column(wr)); + /* tell ncurses to redraw the screen */ + doupdate(); } -/* handle delete */ -static inline void delete(gint *cursor, - wchar_t *wline) +#if defined(ENABLE_WIDE) || defined(ENABLE_MULTIBYTE) +static bool +multibyte_is_complete(const char *p, size_t length) { - int i; - if( *cursor <= wcslen(wline) - 1 ) - { - for (i = *cursor; wline[i] != 0; i++) - wline[i] = wline[i + 1]; - } + GError *error = NULL; + gchar *q = g_locale_to_utf8(p, length, + NULL, NULL, &error); + if (q != NULL) { + g_free(q); + return true; + } else { + g_error_free(error); + return false; + } } +#endif -/* move the cursor to the end of the line */ -static inline void cursor_move_to_eol(gint *cursor, - gint *start, - gint width, - gint x0, - gint x1, - wchar_t *line) +static void +wreadln_insert_byte(struct wreadln *wr, gint key) { - *cursor = wcslen(line); - if( *cursor+x0 >= x1 ) - *start = *cursor-width+1; + size_t rest = strlen(wr->line + wr->cursor) + 1; +#if defined(ENABLE_WIDE) || defined(ENABLE_MULTIBYTE) + char buffer[32] = { key }; + size_t length = 1; + struct pollfd pfd = { + .fd = 0, + .events = POLLIN, + }; + int ret; + + /* wide version: try to complete the multibyte sequence */ + + while (length < sizeof(buffer)) { + if (multibyte_is_complete(buffer, length)) + /* sequence is complete */ + break; + + /* poll for more bytes on stdin, without timeout */ + + ret = poll(&pfd, 1, 0); + if (ret <= 0) + /* no more input from keyboard */ + break; + + buffer[length++] = wgetch(wr->w); + } + + memmove(wr->line + wr->cursor + length, + wr->line + wr->cursor, rest); + memcpy(wr->line + wr->cursor, buffer, length); + +#else + const size_t length = 1; + + memmove(wr->line + wr->cursor + length, + wr->line + wr->cursor, rest); + wr->line[wr->cursor] = key; + +#endif + + wr->cursor += length; + if (cursor_column(wr) >= wr->width) + wr->start = right_align_bytes(wr->line, wr->cursor, wr->width); } -/* draw line buffer and update cursor position */ -static inline void drawline(gint cursor, - gint start, - gint width, - gint x0, - gint x1, - gint y, - gboolean masked, - wchar_t *line, - WINDOW *w) +static void +wreadln_delete_char(struct wreadln *wr, size_t x) { - wmove(w, y, x0); - /* clear input area */ - whline(w, ' ', width); - /* print visible part of the line buffer */ - if(masked == TRUE) whline(w, '*', wcslen(line)-start); - else waddnwstr(w, line+start, width); - FILE *dbg = fopen ("dbg", "a+"); - fprintf (dbg, "%i,%s---%i---", width, line, wcslen (line)); - /* move the cursor to the correct position */ - wmove(w, y, x0 + cursor-start); - /* tell ncurses to redraw the screen */ - doupdate(); + size_t rest, length; + + assert(x < strlen(wr->line)); + + length = next_char_size(&wr->line[x]); + rest = strlen(&wr->line[x + length]) + 1; + memmove(&wr->line[x], &wr->line[x + length], rest); } -/* libcursesw version */ +/* libcurses version */ -gchar * -_wreadln(WINDOW *w, - gchar *prompt, - gchar *initial_value, - gint x1, - GList **history, - GCompletion *gcmp, - gboolean masked) +static gchar * +_wreadln(WINDOW *w, + const gchar *prompt, + const gchar *initial_value, + unsigned x1, + GList **history, + GCompletion *gcmp, + gboolean masked) { - GList *hlist = NULL, *hcurrent = NULL; - wchar_t *wline; - gchar *mbline; - gint x0, x, y, width, start; - gint cursor; - wint_t wch; - gint key; - gint i; - - - /* initialize variables */ - start = 0; - x = 0; - cursor = 0; - mbline = NULL; - - /* allocate a line buffer */ - wline = g_malloc0(wrln_max_line_size*sizeof(wchar_t)); - /* turn off echo */ - noecho(); - /* make shure the cursor is visible */ - curs_set(1); - /* print prompt string */ - if( prompt ) - waddstr(w, prompt); - /* retrive y and x0 position */ - getyx(w, y, x0); - /* check the x1 value */ - if( x1<=x0 || x1>COLS ) - x1 = COLS; - width = x1-x0; - /* clear input area */ - mvwhline(w, y, x0, ' ', width); - - - if( history ) - { - /* append the a new line to our history list */ - *history = g_list_append(*history, g_malloc0(wrln_max_line_size)); - /* hlist points to the current item in the history list */ - hlist = g_list_last(*history); - hcurrent = hlist; - } - if( initial_value == (char *) -1 ) - { - /* get previous history entry */ - if( history && hlist->prev ) - { - if( hlist==hcurrent ) - { - /* save the current line */ - //g_strlcpy(hlist->data, line, wrln_max_line_size); - } - /* get previous line */ - hlist = hlist->prev; - mbstowcs(wline, hlist->data, wrln_max_line_size); + struct wreadln wr = { + .w = w, + .masked = masked, + .cursor = 0, + .start = 0, + }; + GList *hlist = NULL, *hcurrent = NULL; + gint key = 0; + size_t i; + +#ifdef NCMPC_MINI + (void)gcmp; +#endif + + /* turn off echo */ + noecho(); + /* make sure the cursor is visible */ + curs_set(1); + /* print prompt string */ + if (prompt) { + waddstr(w, prompt); + waddstr(w, ": "); } - cursor_move_to_eol(&cursor, &start, width, x0, x1, wline); - drawline(cursor, start, width, x0, x1, y, masked, wline, w); - } - else if( initial_value ) - { - /* copy the initial value to the line buffer */ - mbstowcs(wline, initial_value, wrln_max_line_size); - cursor_move_to_eol(&cursor, &start, width, x0, x1, wline); - drawline(cursor, start, width, x0, x1, y, masked, wline, w); - } - - wch=0; - key=0; - while( wch!=13 && wch!='\n' ) - { - key = wget_wch(w, &wch); - - if( key==KEY_CODE_YES ) - { - /* function key */ - switch(wch) - { - case KEY_HOME: - x=0; - cursor=0; - start=0; - break; - case KEY_END: - cursor_move_to_eol(&cursor, &start, width, x0, x1, wline); - break; - case KEY_LEFT: - cursor_move_left(&cursor, &start, width, x0, x1, wline); - break; - case KEY_RIGHT: - cursor_move_right(&cursor, &start, width, x0, x1, wline); - break; - case KEY_DC: - delete(&cursor, wline); - break; - case KEY_BCKSPC: - case KEY_BACKSPACE: - backspace(&cursor, &start, width, x0, x1, wline); - break; - case KEY_UP: - /* get previous history entry */ - if( history && hlist->prev ) - { - if( hlist==hcurrent ) - { - /* save the current line */ - wcstombs(hlist->data, wline, wrln_max_line_size); - } - /* get previous line */ - hlist = hlist->prev; - mbstowcs(wline, hlist->data, wrln_max_line_size); - } - cursor_move_to_eol(&cursor, &start, width, x0, x1, wline); - break; - case KEY_DOWN: - /* get next history entry */ - if( history && hlist->next ) - { - /* get next line */ - hlist = hlist->next; - mbstowcs(wline, hlist->data, wrln_max_line_size); + /* retrieve y and x0 position */ + getyx(w, wr.y, wr.x); + /* check the x1 value */ + if (x1 <= wr.x || x1 > (unsigned)COLS) + x1 = COLS; + wr.width = x1 - wr.x; + /* clear input area */ + mvwhline(w, wr.y, wr.x, ' ', wr.width); + + if (history) { + /* append the a new line to our history list */ + *history = g_list_append(*history, g_malloc0(sizeof(wr.line))); + /* hlist points to the current item in the history list */ + hlist = g_list_last(*history); + hcurrent = hlist; + } + + if (initial_value == (char *)-1) { + /* get previous history entry */ + if (history && hlist->prev) { + if (hlist == hcurrent) + /* save the current line */ + g_strlcpy(hlist->data, wr.line, sizeof(wr.line)); + + /* get previous line */ + hlist = hlist->prev; + g_strlcpy(wr.line, hlist->data, sizeof(wr.line)); } - cursor_move_to_eol(&cursor, &start, width, x0, x1, wline); - break; - case KEY_RESIZE: - /* resize event */ - if( x1>COLS ) - { - x1=COLS; - width = x1-x0; - cursor_move_to_eol(&cursor, &start, width, x0, x1, wline); + cursor_move_to_eol(&wr); + drawline(&wr); + } else if (initial_value) { + /* copy the initial value to the line buffer */ + g_strlcpy(wr.line, initial_value, sizeof(wr.line)); + cursor_move_to_eol(&wr); + drawline(&wr); + } + + while (key != 13 && key != '\n') { + key = wgetch(w); + + /* check if key is a function key */ + for (i = 0; i < 63; i++) + if (key == (int)KEY_F(i)) { + key = KEY_F(1); + i = 64; + } + + switch (key) { +#ifdef HAVE_GETMOUSE + case KEY_MOUSE: /* ignore mouse events */ +#endif + case ERR: /* ignore errors */ + break; + + case TAB: +#ifndef NCMPC_MINI + if (gcmp) { + char *prefix = NULL; + GList *list; + + if (wrln_pre_completion_callback) + wrln_pre_completion_callback(gcmp, wr.line, + wrln_completion_callback_data); + list = g_completion_complete(gcmp, wr.line, &prefix); + if (prefix) { + g_strlcpy(wr.line, prefix, sizeof(wr.line)); + cursor_move_to_eol(&wr); + g_free(prefix); + } else + screen_bell(); + + if (wrln_post_completion_callback) + wrln_post_completion_callback(gcmp, wr.line, list, + wrln_completion_callback_data); + } +#endif + break; + + case KEY_CTRL_G: + screen_bell(); + if (history) { + g_free(hcurrent->data); + hcurrent->data = NULL; + *history = g_list_delete_link(*history, hcurrent); + } + return NULL; + + case KEY_LEFT: + case KEY_CTRL_B: + cursor_move_left(&wr); + break; + case KEY_RIGHT: + case KEY_CTRL_F: + cursor_move_right(&wr); + break; + case KEY_HOME: + case KEY_CTRL_A: + wr.cursor = 0; + wr.start = 0; + break; + case KEY_END: + case KEY_CTRL_E: + cursor_move_to_eol(&wr); + break; + case KEY_CTRL_K: + wr.line[wr.cursor] = 0; + break; + case KEY_CTRL_U: + wr.cursor = utf8_width(wr.line); + for (i = 0; i < wr.cursor; i++) + wr.line[i] = '\0'; + wr.cursor = 0; + break; + case KEY_CTRL_W: + /* Firstly remove trailing spaces. */ + for (i = wr.cursor; i > 0 && wr.line[i-1] == ' '; i--) + { + cursor_move_left(&wr); + wreadln_delete_char(&wr, wr.cursor); + } + /* Then remove word until next space. */ + for (; i > 0 && wr.line[i-1] != ' '; i--) + { + cursor_move_left(&wr); + wreadln_delete_char(&wr, wr.cursor); + } + break; + case 127: + case KEY_BCKSPC: /* handle backspace: copy all */ + case KEY_BACKSPACE: /* chars starting from curpos */ + if (wr.cursor > 0) { /* - 1 from buf[n+1] to buf */ + cursor_move_left(&wr); + wreadln_delete_char(&wr, wr.cursor); + } + break; + case KEY_DC: /* handle delete key. As above */ + case KEY_CTRL_D: + if (wr.line[wr.cursor] != 0) + wreadln_delete_char(&wr, wr.cursor); + break; + case KEY_UP: + case KEY_CTRL_P: + /* get previous history entry */ + if (history && hlist->prev) { + if (hlist == hcurrent) + /* save the current line */ + g_strlcpy(hlist->data, wr.line, + sizeof(wr.line)); + + /* get previous line */ + hlist = hlist->prev; + g_strlcpy(wr.line, hlist->data, + sizeof(wr.line)); + } + cursor_move_to_eol(&wr); + break; + case KEY_DOWN: + case KEY_CTRL_N: + /* get next history entry */ + if (history && hlist->next) { + /* get next line */ + hlist = hlist->next; + g_strlcpy(wr.line, hlist->data, + sizeof(wr.line)); + } + cursor_move_to_eol(&wr); + break; + + case '\n': + case 13: + case KEY_IC: + case KEY_PPAGE: + case KEY_NPAGE: + case KEY_F(1): + /* ignore char */ + break; + default: + if (key >= 32) + wreadln_insert_byte(&wr, key); } - /* make shure the cursor is visible */ - curs_set(1); - break; - } + drawline(&wr); } - else if( key!=ERR ) - { - switch(wch) - { - case KEY_CTRL_A: - x=0; - cursor=0; - start=0; - break; - case KEY_CTRL_C: - exit(EXIT_SUCCESS); - break; - case KEY_CTRL_D: - delete(&cursor, wline); - break; - case KEY_CTRL_E: - cursor_move_to_eol(&cursor, &start, width, x0, x1, wline); - break; - case TAB: - if( gcmp ) - { - char *prefix = NULL; - GList *list; - - i = wcstombs(NULL,wline,0)+1; - mbline = g_malloc0(i); - wcstombs(mbline, wline, i); - - if(wrln_pre_completion_callback) - wrln_pre_completion_callback(gcmp, mbline, - wrln_completion_callback_data); - list = g_completion_complete(gcmp, mbline, &prefix); - if( prefix ) - { - mbstowcs(wline, prefix, wrln_max_line_size); - cursor_move_to_eol(&cursor, &start, width, x0, x1, wline); - g_free(prefix); - } - else - screen_bell(); - if( wrln_post_completion_callback ) - wrln_post_completion_callback(gcmp, mbline, list, - wrln_completion_callback_data); - - g_free(mbline); - } - break; - case KEY_CTRL_G: - screen_bell(); - g_free(wline); - if( history ) - { - g_free(hcurrent->data); - hcurrent->data = NULL; - *history = g_list_delete_link(*history, hcurrent); - } - return NULL; - case KEY_CTRL_K: - wline[cursor] = 0; - break; - case KEY_CTRL_U: - cursor = wcslen(wline); - for (i = 0;i < cursor; i++) - wline[i] = '\0'; - cursor = 0; - break; - case KEY_CTRL_Z: - sigstop(); - break; - case 127: - backspace(&cursor, &start, width, x0, x1, wline); - break; - case '\n': - case 13: - /* ignore char */ - break; - default: - if( (wcslen(wline+cursor)) ) - { - /* the cursor is not at the last pos */ - wchar_t *tmp = NULL; - gsize len = (wcslen(wline+cursor)+1); - tmp = g_malloc0(len*sizeof(wchar_t)); - wmemcpy(tmp, wline+cursor, len); - wline[cursor] = wch; - wline[cursor+1] = 0; - wcscat(&wline[cursor+1], tmp); - g_free(tmp); - cursor_move_right(&cursor, &start, width, x0, x1, wline); + + /* update history */ + if (history) { + if (strlen(wr.line)) { + /* update the current history entry */ + size_t size = strlen(wr.line) + 1; + hcurrent->data = g_realloc(hcurrent->data, size); + g_strlcpy(hcurrent->data, wr.line, size); + } else { + /* the line was empty - remove the current history entry */ + g_free(hcurrent->data); + hcurrent->data = NULL; + *history = g_list_delete_link(*history, hcurrent); } - else - { - FILE *ff = fopen ("curspr", "a+"); - fprintf (ff, "%i", cursor); - wline[cursor] = wch; - wline[cursor+1] = 0; - cursor_move_right(&cursor, &start, width, x0, x1, wline); + + while (g_list_length(*history) > wrln_max_history_length) { + GList *first = g_list_first(*history); + + /* remove the oldest history entry */ + g_free(first->data); + first->data = NULL; + *history = g_list_delete_link(*history, first); } - } - } - drawline(cursor, start, width, x0, x1, y, masked, wline, w); - } - i = wcstombs(NULL,wline,0)+1; - mbline = g_malloc0(i); - wcstombs(mbline, wline, i); - - /* update history */ - if( history ) - { - if( strlen(mbline) ) - { - /* update the current history entry */ - size_t size = strlen(mbline)+1; - hcurrent->data = g_realloc(hcurrent->data, size); - g_strlcpy(hcurrent->data, mbline, size); - } - else - { - /* the line was empty - remove the current history entry */ - g_free(hcurrent->data); - hcurrent->data = NULL; - *history = g_list_delete_link(*history, hcurrent); } - while( g_list_length(*history) > wrln_max_history_length ) - { - GList *first = g_list_first(*history); + if (wr.line[0] == 0) + return NULL; - /* remove the oldest history entry */ - g_free(first->data); - first->data = NULL; - *history = g_list_delete_link(*history, first); - } - } - return mbline; + return g_strdup(wr.line); } - -#endif gchar * -wreadln(WINDOW *w, - gchar *prompt, - gchar *initial_value, - gint x1, - GList **history, +wreadln(WINDOW *w, + const gchar *prompt, + const gchar *initial_value, + unsigned x1, + GList **history, GCompletion *gcmp) - { - return _wreadln(w, prompt, initial_value, x1, history, gcmp, FALSE); - } - +{ + return _wreadln(w, prompt, initial_value, x1, history, gcmp, FALSE); +} + gchar * -wreadln_masked(WINDOW *w, - gchar *prompt, - gchar *initial_value, - gint x1, - GList **history, - GCompletion *gcmp) - { - return _wreadln(w, prompt, initial_value, x1, history, gcmp, TRUE); - } +wreadln_masked(WINDOW *w, + const gchar *prompt, + const gchar *initial_value, + unsigned x1, + GList **history, + GCompletion *gcmp) +{ + return _wreadln(w, prompt, initial_value, x1, history, gcmp, TRUE); +}