X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fwreadln.c;h=5d019d9003d3438b1287141082fa380349dbf167;hb=3a92aa17478c901b1704fb935723783b11f193e7;hp=930cb5429f85ad51462f0f295c1ceeab2b80e4d5;hpb=9a500d13055f16b70796a5fce5ec6de4a2844187;p=ncmpc.git diff --git a/src/wreadln.c b/src/wreadln.c index 930cb54..5d019d9 100644 --- a/src/wreadln.c +++ b/src/wreadln.c @@ -1,6 +1,4 @@ -/* - * $Id$ - * +/* * (c) 2004 by Kalle Wallin * * This program is free software; you can redistribute it and/or modify @@ -18,333 +16,363 @@ * */ +#include "wreadln.h" +#include "charset.h" +#include "screen_utils.h" +#include "config.h" + #include #include -#include #include -#include "config.h" -#include "wreadln.h" - #define KEY_CTRL_A 1 -#define KEY_CTRL_D 4 +#define KEY_CTRL_B 2 +#define KEY_CTRL_C 3 +#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_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 */ + gint x, y; + + /** the screen width of the input field */ + gint width; + + /** is the input masked, i.e. characters displayed as '*'? */ + const gboolean masked; + + /** the byte position of the cursor */ + gint cursor; + + /** the byte position displayed at the origin (for horizontal + scrolling) */ + gint start; + + /** the current value */ + gchar line[1024]; +}; + +/** max items stored in the history list */ +static const guint wrln_max_history_length = 32; + +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; -extern void screen_bell(void); +/* move the cursor one step to the right */ +static inline void cursor_move_right(struct wreadln *wr) +{ + if (wr->cursor < (int)strlen(wr->line)) { + ++wr->cursor; + if (wr->cursor >= wr->width && + wr->start < wr->cursor - wr->width + 1) + ++wr->start; + } +} -gchar * -wreadln(WINDOW *w, - gchar *prompt, - gchar *initial_value, - gint x1, - GList **history, - GCompletion *gcmp) +/* move the cursor one step to the left */ +static inline void cursor_move_left(struct wreadln *wr) { - GList *hlist = NULL, *hcurrent = NULL; - gchar *line; - gint x0, y, width; - gint cursor = 0, start = 0; - gint key = 0, i; - - /* move the cursor one step to the right */ - void cursor_move_right(void) { - if( cursor < strlen(line) && cursor= x1 && start 0 ) - { - if( cursor==start && start > 0 ) - start--; - cursor--; - } - } - /* move the cursor to the end of the line */ - void cursor_move_to_eol(void) { - cursor = strlen(line); - if( cursor+x0 >= x1 ) - start = cursor-width+1; - } - /* draw line buffer and update cursor position */ - void drawline() { - wmove(w, y, x0); - /* clear input area */ - whline(w, ' ', width); - /* print visible part of the line buffer */ - 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(); - } - - - /* 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); + if (wr->cursor > 0) { + if (wr->cursor == wr->start && wr->start > 0) + --wr->start; + --wr->cursor; } - cursor_move_to_eol(); - drawline(); - } - 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(); - drawline(); - } - - 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) - { +} + +/* 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 (wr->cursor >= wr->width) + wr->start = wr->cursor - wr->width + 1; +} + +/* draw line buffer and update cursor position */ +static inline void drawline(const struct wreadln *wr) +{ + 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, wr->width); + /* move the cursor to the correct position */ + wmove(wr->w, wr->y, wr->x + wr->cursor - wr->start); + /* tell ncurses to redraw the screen */ + doupdate(); +} + +static void +wreadln_insert_byte(struct wreadln *wr, gint key) +{ + size_t rest = strlen(wr->line + wr->cursor) + 1; + const size_t length = 1; + + memmove(wr->line + wr->cursor + length, + wr->line + wr->cursor, rest); + wr->line[wr->cursor] = key; + + cursor_move_right(wr); +} + +/* libcurses version */ + +static gchar * +_wreadln(WINDOW *w, + const gchar *prompt, + const gchar *initial_value, + gint x1, + GList **history, + GCompletion *gcmp, + gboolean masked) +{ + struct wreadln wr = { + .w = w, + .masked = masked, + .cursor = 0, + .start = 0, + }; + GList *hlist = NULL, *hcurrent = NULL; + gint key = 0, i; + + /* 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, wr.y, wr.x); + /* check the x1 value */ + if (x1 <= wr.x || x1 > 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(&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 == KEY_F(i)) { + key = KEY_F(1); + i = 64; + } + + switch (key) { #ifdef HAVE_GETMOUSE - case KEY_MOUSE: /* ignore mouse events */ + 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(); - } - /* 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); - list = g_completion_complete(gcmp, line, &prefix); - if( prefix ) - { - g_strlcpy(line, prefix, wrln_max_line_size); - cursor_move_to_eol(); - g_free(prefix); - } - else - screen_bell(); - if( wrln_post_completion_callback ) - wrln_post_completion_callback(gcmp, line, list); - } - 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(); - break; - case KEY_RIGHT: - cursor_move_right(); - break; - case KEY_HOME: - case KEY_CTRL_A: - cursor = 0; - start = 0; - break; - case KEY_END: - case KEY_CTRL_E: - cursor_move_to_eol(); - break; - case KEY_CTRL_K: - line[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(); - } - break; - case KEY_DC: /* handle delete key. As above */ - case KEY_CTRL_D: - if( cursor <= 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(); - 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(); - 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(); - } - else - { - line[cursor + 1] = 0; - line[cursor] = key; - cursor_move_right(); + case ERR: /* ingnore errors */ + break; + + case TAB: + 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); + } + 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 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 */ + for (i = wr.cursor - 1; wr.line[i] != 0; i++) + wr.line[i] = wr.line[i + 1]; + cursor_move_left(&wr); + } + break; + case KEY_DC: /* handle delete key. As above */ + case KEY_CTRL_D: + if (wr.cursor <= (gint)utf8_width(wr.line) - 1) { + for (i = wr.cursor; wr.line[i] != 0; i++) + wr.line[i] = wr.line[i + 1]; + } + 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); } - } - } - drawline(); - } - - /* 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); + drawline(&wr); } - while( g_list_length(*history) > wrln_max_history_length ) - { - GList *first = g_list_first(*history); + /* 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); + } + + 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); + /* 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 g_strdup(wr.line); } - +gchar * +wreadln(WINDOW *w, + const gchar *prompt, + const gchar *initial_value, + gint x1, + GList **history, + GCompletion *gcmp) +{ + return _wreadln(w, prompt, initial_value, x1, history, gcmp, FALSE); +} + +gchar * +wreadln_masked(WINDOW *w, + const gchar *prompt, + const gchar *initial_value, + gint x1, + GList **history, + GCompletion *gcmp) +{ + return _wreadln(w, prompt, initial_value, x1, history, gcmp, TRUE); +}