Code

930cb5429f85ad51462f0f295c1ceeab2b80e4d5
[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 "config.h"
27 #include "wreadln.h"
29 #define KEY_CTRL_A   1
30 #define KEY_CTRL_D   4 
31 #define KEY_CTRL_E   5
32 #define KEY_CTRL_G   7
33 #define KEY_CTRL_K   11
34 #define KEY_BCKSPC   8
35 #define TAB          9
37 #define WRLN_MAX_LINE_SIZE 1024
38 #define WRLN_MAX_HISTORY_LENGTH 32
39  
40 guint wrln_max_line_size = WRLN_MAX_LINE_SIZE;
41 guint wrln_max_history_length = WRLN_MAX_HISTORY_LENGTH;
42 wrln_wgetch_fn_t wrln_wgetch = NULL;
43 wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL;
44 wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL;
46 extern void screen_bell(void);
48 gchar *
49 wreadln(WINDOW *w, 
50         gchar *prompt, 
51         gchar *initial_value,
52         gint x1, 
53         GList **history, 
54         GCompletion *gcmp)
55 {
56   GList *hlist = NULL, *hcurrent = NULL;
57   gchar *line;
58   gint x0, y, width;            
59   gint cursor = 0, start = 0;           
60   gint key = 0, i;
62   /* move the cursor one step to the right */
63   void cursor_move_right(void) {
64     if( cursor < strlen(line) && cursor<wrln_max_line_size-1 )
65       {
66         cursor++;
67         if( cursor+x0 >= x1 && start<cursor-width+1)
68           start++;
69       }
70   }
71   /* move the cursor one step to the left */
72   void cursor_move_left(void) {
73     if( cursor > 0 )
74       {
75         if( cursor==start && start > 0 )
76           start--;
77         cursor--;
78       }
79   }
80  /* move the cursor to the end of the line */
81   void cursor_move_to_eol(void) {
82     cursor = strlen(line);
83     if( cursor+x0 >= x1 )
84       start = cursor-width+1;
85   }
86   /* draw line buffer and update cursor position */
87   void drawline() {
88     wmove(w, y, x0);
89     /* clear input area */
90     whline(w, ' ', width);
91     /* print visible part of the line buffer */
92     waddnstr(w, line+start, width);
93     /* move the cursor to the correct position */
94     wmove(w, y, x0 + cursor-start);
95     /* tell ncurses to redraw the screen */
96     doupdate();
97   }
100   /* allocate a line buffer */
101   line = g_malloc0(wrln_max_line_size);
102   /* turn off echo */
103   noecho();             
104   /* make shure the cursor is visible */
105   curs_set(1);
106   /* print prompt string */
107   if( prompt )
108     waddstr(w, prompt);         
109   /* retrive y and x0 position */
110   getyx(w, y, x0);      
111   /* check the x1 value */
112   if( x1<=x0 || x1>COLS )
113     x1 = COLS;
114   width = x1-x0;
115   /* clear input area */
116   mvwhline(w, y, x0, ' ', width);       
118   if( history )
119     {
120       /* append the a new line to our history list */
121       *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
122       /* hlist points to the current item in the history list */
123       hlist =  g_list_last(*history);
124       hcurrent = hlist;
125     }
127   if( initial_value == (char *) -1 )
128     {
129       /* get previous history entry */
130       if( history && hlist->prev )
131         {
132           if( hlist==hcurrent )
133             {
134               /* save the current line */
135               g_strlcpy(hlist->data, line, wrln_max_line_size);
136             }
137           /* get previous line */
138           hlist = hlist->prev;
139           g_strlcpy(line, hlist->data, wrln_max_line_size);
140         }
141       cursor_move_to_eol();
142       drawline();
143     }
144   else if( initial_value )
145     {
146       /* copy the initial value to the line buffer */
147       g_strlcpy(line, initial_value, wrln_max_line_size);
148       cursor_move_to_eol();
149       drawline();
150     }  
152   while( key!=13 && key!='\n' )
153     {
154       if( wrln_wgetch )
155         key = wrln_wgetch(w);
156       else
157         key = wgetch(w);
159       /* check if key is a function key */
160       for(i=0; i<63; i++)
161         if( key==KEY_F(i) )
162           {
163             key=KEY_F(1);
164             i=64;
165           }
167       switch (key)
168         {
169 #ifdef HAVE_GETMOUSE
170         case KEY_MOUSE: /* ignore mouse events */
171 #endif
172         case ERR: /* ingnore errors */
173           break;
175         case KEY_RESIZE:
176           /* a resize event */
177           if( x1>COLS )
178             {
179               x1=COLS;
180               width = x1-x0;
181               cursor_move_to_eol();
182             }
183           /* make shure the cursor is visible */
184           curs_set(1);
185           break;
187         case TAB:
188           if( gcmp )
189             {
190               char *prefix = NULL;
191               GList *list;
192               
193               if(wrln_pre_completion_callback)
194                 wrln_pre_completion_callback(gcmp, line);
195               list = g_completion_complete(gcmp, line, &prefix);              
196               if( prefix )
197                 {
198                   g_strlcpy(line, prefix, wrln_max_line_size);
199                   cursor_move_to_eol();
200                   g_free(prefix);
201                 }
202               else
203                 screen_bell();
204               if( wrln_post_completion_callback )
205                 wrln_post_completion_callback(gcmp, line, list);
206             }
207           break;
209         case KEY_CTRL_G:
210           screen_bell();
211           g_free(line);
212           if( history )
213             {
214               g_free(hcurrent->data);
215               hcurrent->data = NULL;
216               *history = g_list_delete_link(*history, hcurrent);
217             }
218           return NULL;
219           
220         case KEY_LEFT:
221           cursor_move_left();
222           break;
223         case KEY_RIGHT: 
224           cursor_move_right();
225           break;
226         case KEY_HOME:
227         case KEY_CTRL_A:
228           cursor = 0;
229           start = 0;
230           break;
231         case KEY_END:
232         case KEY_CTRL_E:
233           cursor_move_to_eol();
234           break;
235         case KEY_CTRL_K:
236           line[cursor] = 0;
237           break;
238         case 127:
239         case KEY_BCKSPC:        /* handle backspace: copy all */
240         case KEY_BACKSPACE:     /* chars starting from curpos */
241           if( cursor > 0 )      /* - 1 from buf[n+1] to buf   */
242             {
243               for (i = cursor - 1; line[i] != 0; i++)
244                 line[i] = line[i + 1];
245               cursor_move_left();
246             }
247           break;
248         case KEY_DC:            /* handle delete key. As above */
249         case KEY_CTRL_D:
250           if( cursor <= strlen(line) - 1 ) 
251             {
252               for (i = cursor; line[i] != 0; i++)
253                 line[i] = line[i + 1];
254             }
255           break;
256         case KEY_UP:            
257           /* get previous history entry */
258           if( history && hlist->prev )
259             {
260               if( hlist==hcurrent )
261                 {
262                   /* save the current line */
263                   g_strlcpy(hlist->data, line, wrln_max_line_size);
264                 }
265               /* get previous line */
266               hlist = hlist->prev;
267               g_strlcpy(line, hlist->data, wrln_max_line_size);
268             }
269           cursor_move_to_eol();
270           break;
271         case KEY_DOWN:  
272           /* get next history entry */
273           if( history && hlist->next )
274             {
275               /* get next line */
276               hlist = hlist->next;
277               g_strlcpy(line, hlist->data, wrln_max_line_size);
278             }
279           cursor_move_to_eol();
280           break;
281           
282         case '\n':
283         case 13:
284         case KEY_IC:
285         case KEY_PPAGE:
286         case KEY_NPAGE:
287         case KEY_F(1):
288           /* ignore char */
289           break;
290         default:         
291           if (key >= 32)
292             {
293               if (strlen (line + cursor))       /* if the cursor is */
294                 {                               /* not at the last pos */
295                   gchar *tmp = 0;
296                   gsize size = strlen(line + cursor) + 1;
298                   tmp = g_malloc0(size);
299                   g_strlcpy (tmp, line + cursor, size);
300                   line[cursor] = key;
301                   line[cursor + 1] = 0;
302                   g_strlcat (&line[cursor + 1], tmp, size);
303                   g_free(tmp);
304                   cursor_move_right();
305                 }
306               else
307                 {
308                   line[cursor + 1] = 0;
309                   line[cursor] = key;
310                   cursor_move_right();
311                 }
312             }
313         }
315       drawline();
316     }
318   /* update history */
319   if( history )
320     {
321       if( strlen(line) )
322         {
323           /* update the current history entry */
324           size_t size = strlen(line)+1;
325           hcurrent->data = g_realloc(hcurrent->data, size);
326           g_strlcpy(hcurrent->data, line, size);
327         }
328       else
329         {
330           /* the line was empty - remove the current history entry */
331           g_free(hcurrent->data);
332           hcurrent->data = NULL;
333           *history = g_list_delete_link(*history, hcurrent);
334         }
336       while( g_list_length(*history) > wrln_max_history_length )
337         {
338           GList *first = g_list_first(*history);
340           /* remove the oldest history entry  */
341           g_free(first->data);
342           first->data = NULL;
343           *history = g_list_delete_link(*history, first);
344         }
345     }
346   
347   return g_realloc(line, strlen(line)+1);
349