Code

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