/* * $Id$ * * (c) 2004 by Kalle Wallin * * 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 * */ #include "config.h" #include #include #include #ifdef USE_NCURSESW #include #else #include #endif #include "wreadln.h" #define KEY_CTRL_A 1 #define KEY_CTRL_C 3 #define KEY_CTRL_D 4 #define KEY_CTRL_E 5 #define KEY_CTRL_G 7 #define KEY_CTRL_K 11 #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; wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL; wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL; extern void sigstop(void); extern void screen_bell(void); extern size_t my_strlen(char *str); #ifndef USE_NCURSESW /* libcurses version */ 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; /* move the cursor one step to the right */ void cursor_move_right(void) { if( cursor < my_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 = my_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 */ 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(); } /* 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(); 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) { #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(); } /* 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 <= 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(); 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(); } } } 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); } 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); } } return g_realloc(line, strlen(line)+1); } #else /* libcursesw version */ gchar * _wreadln(WINDOW *w, gchar *prompt, gchar *initial_value, gint 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; /* move the cursor to the beginning of the line */ void cursor_move_home(void) { x=0; cursor=0; start=0; } /* move the cursor to the end of the line */ void cursor_move_to_eol(void) { cursor = wcslen(wline); //x=wcswidth(wline,cursor); if( cursor+x0 >= x1 ) start = cursor-width+1; } /* move the cursor one step to the left */ void cursor_move_left(void) { if( cursor > 0 ) { if( cursor==start && start > 0 ) start--; //x-=wcwidth(wline[cursor]); cursor--; } } /* move the cursor one step to the right */ void cursor_move_right(void) { if( cursor < wcslen(wline) && cursor= x1 && start 0 ) { for (i = cursor - 1; wline[i] != 0; i++) wline[i] = wline[i + 1]; cursor_move_left(); } } /* handle delete */ void delete() { if( cursor <= wcslen(wline) - 1 ) { for (i = cursor; wline[i] != 0; i++) wline[i] = wline[i + 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 */ 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(); } /* 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); } cursor_move_to_eol(); drawline(); } else if( initial_value ) { /* copy the initial value to the line buffer */ mbstowcs(wline, initial_value, wrln_max_line_size); cursor_move_to_eol(); drawline(); } 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: cursor_move_home(); break; case KEY_END: cursor_move_to_eol(); break; case KEY_LEFT: cursor_move_left(); break; case KEY_RIGHT: cursor_move_right(); break; case KEY_DC: delete(); break; case KEY_BCKSPC: case KEY_BACKSPACE: backspace(); 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(); 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); } cursor_move_to_eol(); break; case KEY_RESIZE: /* resize event */ if( x1>COLS ) { x1=COLS; width = x1-x0; cursor_move_to_eol(); } /* make shure the cursor is visible */ curs_set(1); break; } } else if( key!=ERR ) { switch(wch) { case KEY_CTRL_A: cursor_move_home(); break; case KEY_CTRL_C: exit(EXIT_SUCCESS); break; case KEY_CTRL_D: delete(); break; case KEY_CTRL_E: cursor_move_to_eol(); 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); list = g_completion_complete(gcmp, mbline, &prefix); if( prefix ) { mbstowcs(wline, 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, mbline, list); 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_Z: sigstop(); break; case 127: backspace(); 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(); } else { wline[cursor] = wch; wline[cursor+1] = 0; cursor_move_right(); } } } drawline(); } 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); /* remove the oldest history entry */ g_free(first->data); first->data = NULL; *history = g_list_delete_link(*history, first); } } return mbline; } #endif gchar * wreadln(WINDOW *w, gchar *prompt, gchar *initial_value, gint x1, GList **history, GCompletion *gcmp) { 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); }