Code

added screen_bell() for optional audible/visible bells
[ncmpc.git] / src / wreadln.c
1 /* 
2  * $Id$
3  *
4  * (c) 2004 by Kalle Wallin <kaw@linux.se>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  */
21 #include <stdlib.h>
22 #include <string.h>
23 #include <ncurses.h>
24 #include <glib.h>
26 #include "wreadln.h"
28 #define KEY_CTRL_A   1
29 #define KEY_CTRL_D   4 
30 #define KEY_CTRL_E   5
31 #define KEY_CTRL_G   7
32 #define KEY_CTRL_K   11
33 #define KEY_BCKSPC   8
34 #define TAB          9
36 #define WRLN_MAX_LINE_SIZE 1024
37 #define WRLN_MAX_HISTORY_LENGTH 32
38  
39 unsigned int wrln_max_line_size = WRLN_MAX_LINE_SIZE;
40 unsigned int wrln_max_history_length = WRLN_MAX_HISTORY_LENGTH;
41 GVoidFunc wrln_resize_callback = NULL;
42 wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL;
43 wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL;
45 extern void screen_bell(void);
47 char *
48 wreadln(WINDOW *w, 
49         char *prompt, 
50         char *initial_value,
51         int x1, 
52         GList **history, 
53         GCompletion *gcmp)
54 {
55   GList *hlist = NULL, *hcurrent = NULL;
56   char *line;
57   int x0, y, width;             
58   int cursor = 0, start = 0;            
59   int key = 0, i;
61   /* move the cursor one step to the right */
62   void cursor_move_right(void) {
63     if( cursor < strlen(line) && cursor<wrln_max_line_size-1 )
64       {
65         cursor++;
66         if( cursor+x0 >= x1 && start<cursor-width+1)
67           start++;
68       }
69   }
70   /* move the cursor one step to the left */
71   void cursor_move_left(void) {
72     if( cursor > 0 )
73       {
74         if( cursor==start && start > 0 )
75           start--;
76         cursor--;
77       }
78   }
79  /* move the cursor to the end of the line */
80   void cursor_move_to_eol(void) {
81     cursor = strlen(line);
82     if( cursor+x0 >= x1 )
83       start = cursor-width+1;
84   }
85   /* draw line buffer and update cursor position */
86   void drawline() {
87     wmove(w, y, x0);
88     /* clear input area */
89     whline(w, ' ', width);
90     /* print visible part of the line buffer */
91     waddnstr(w, line+start, width);
92     /* move the cursor to the correct position */
93     wmove(w, y, x0 + cursor-start);
94     /* tell ncurses to redraw the screen */
95     doupdate();
96   }
99   /* allocate a line buffer */
100   line = g_malloc0(wrln_max_line_size);
101   /* turn off echo */
102   noecho();             
103   /* make shure the cursor is visible */
104   curs_set(1);
105   /* print prompt string */
106   if( prompt )
107     waddstr(w, prompt);         
108   /* retrive y and x0 position */
109   getyx(w, y, x0);      
110   /* check the x1 value */
111   if( x1<=x0 || x1>COLS )
112     x1 = COLS;
113   width = x1-x0;
114   /* clear input area */
115   mvwhline(w, y, x0, ' ', width);       
117   if( history )
118     {
119       /* append the a new line to our history list */
120       *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
121       /* hlist points to the current item in the history list */
122       hlist =  g_list_last(*history);
123       hcurrent = hlist;
124     }
126   if( initial_value == (char *) -1 )
127     {
128       /* get previous history entry */
129       if( history && hlist->prev )
130         {
131           if( hlist==hcurrent )
132             {
133               /* save the current line */
134               strncpy(hlist->data, line, wrln_max_line_size);
135             }
136           /* get previous line */
137           hlist = hlist->prev;
138           strncpy(line, hlist->data, wrln_max_line_size);
139         }
140       cursor_move_to_eol();
141       drawline();
142     }
143   else if( initial_value )
144     {
145       /* copy the initial value to the line buffer */
146       strncpy(line, initial_value, wrln_max_line_size);
147       cursor_move_to_eol();
148       drawline();
149     }  
151   while( key!=13 && key!='\n' )
152     {
153       key = wgetch(w);
155       /* check if key is a function key */
156       for(i=0; i<63; i++)
157         if( key==KEY_F(i) )
158           {
159             key=KEY_F(1);
160             i=64;
161           }
163       switch (key)
164         {
165         case ERR:
166           /* ingnore errors */
167           break;
169         case KEY_RESIZE:
170           /* a resize event -> call an external callback function */
171           if( wrln_resize_callback )
172             wrln_resize_callback();
173           if( x1>COLS )
174             {
175               x1=COLS;
176               width = x1-x0;
177               cursor_move_to_eol();
178             }
179           /* make shure the cursor is visible */
180           curs_set(1);
181           break;
183         case TAB:
184           if( gcmp )
185             {
186               char *prefix = NULL;
187               GList *list;
188               
189               if(wrln_pre_completion_callback)
190                 wrln_pre_completion_callback(gcmp, line);
191               list = g_completion_complete(gcmp, line, &prefix);              
192               if( prefix )
193                 {
194                   int len = strlen(prefix);
195                   strncpy(line, prefix, len);
196                   cursor_move_to_eol();
197                   g_free(prefix);
198                 }
199               else
200                 screen_bell();
201               if( wrln_post_completion_callback )
202                 wrln_post_completion_callback(gcmp, line, list);
203             }
204           break;
206         case KEY_CTRL_G:
207           screen_bell();
208           g_free(line);
209           if( history )
210             {
211               g_free(hcurrent->data);
212               hcurrent->data = NULL;
213               *history = g_list_delete_link(*history, hcurrent);
214             }
215           return NULL;
216           
217         case KEY_LEFT:
218           cursor_move_left();
219           break;
220         case KEY_RIGHT: 
221           cursor_move_right();
222           break;
223         case KEY_HOME:
224         case KEY_CTRL_A:
225           cursor = 0;
226           start = 0;
227           break;
228         case KEY_END:
229         case KEY_CTRL_E:
230           cursor_move_to_eol();
231           break;
232         case KEY_CTRL_K:
233           line[cursor] = 0;
234           break;
235         case 127:
236         case KEY_BCKSPC:        /* handle backspace: copy all */
237         case KEY_BACKSPACE:     /* chars starting from curpos */
238           if( cursor > 0 )      /* - 1 from buf[n+1] to buf   */
239             {
240               for (i = cursor - 1; line[i] != 0; i++)
241                 line[i] = line[i + 1];
242               cursor_move_left();
243             }
244           break;
245         case KEY_DC:            /* handle delete key. As above */
246         case KEY_CTRL_D:
247           if( cursor <= strlen(line) - 1 ) 
248             {
249               for (i = cursor; line[i] != 0; i++)
250                 line[i] = line[i + 1];
251             }
252           break;
253         case KEY_UP:            
254           /* get previous history entry */
255           if( history && hlist->prev )
256             {
257               if( hlist==hcurrent )
258                 {
259                   /* save the current line */
260                   strncpy(hlist->data, line, wrln_max_line_size);
261                 }
262               /* get previous line */
263               hlist = hlist->prev;
264               strncpy(line, hlist->data, wrln_max_line_size);
265             }
266           //      if (cursor > strlen(line))
267           cursor_move_to_eol();
268           break;
269         case KEY_DOWN:  
270           /* get next history entry */
271           if( history && hlist->next )
272             {
273               /* get next line */
274               hlist = hlist->next;
275               strncpy(line, hlist->data, wrln_max_line_size);
276             }
277           cursor_move_to_eol();
278           break;
279           
280         case '\n':
281         case 13:
282         case KEY_IC:
283         case KEY_PPAGE:
284         case KEY_NPAGE:
285         case KEY_F(1):
286           /* ignore char */
287           break;
288         default:         
289           if (key >= 32)
290             {
291               if (strlen (line + cursor))       /* if the cursor is */
292                 {                               /* not at the last pos */
293                   char *tmp = 0;
294                   tmp = g_malloc0(strlen (line + cursor) + 1);
295                   strcpy (tmp, line + cursor);
296                   line[cursor] = key;
297                   line[cursor + 1] = 0;
298                   strcat (&line[cursor + 1], tmp);
299                   g_free(tmp);
300                   cursor_move_right();
301                 }
302               else
303                 {
304                   line[cursor + 1] = 0;
305                   line[cursor] = key;
306                   cursor_move_right();
307                 }
308             }
309         }
311       drawline();
312     }
314   /* update history */
315   if( history )
316     {
317       if( strlen(line) )
318         {
319           /* update the current history entry */
320           size_t size = strlen(line)+1;
321           hcurrent->data = g_realloc(hcurrent->data, size);
322           strncpy(hcurrent->data, line, size);
323         }
324       else
325         {
326           /* the line was empty - remove the current history entry */
327           g_free(hcurrent->data);
328           hcurrent->data = NULL;
329           *history = g_list_delete_link(*history, hcurrent);
330         }
332       while( g_list_length(*history) > wrln_max_history_length )
333         {
334           GList *first = g_list_first(*history);
336           /* remove the oldest history entry  */
337           g_free(first->data);
338           first->data = NULL;
339           *history = g_list_delete_link(*history, first);
340         }
341     }
342   
343   return g_realloc(line, strlen(line)+1);
345