Code

700769bd158175ebb1bd7644526fe3abccd1b776
[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 unsigned int wrln_max_line_size = WRLN_MAX_LINE_SIZE;
41 unsigned int 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 char *
49 wreadln(WINDOW *w, 
50         char *prompt, 
51         char *initial_value,
52         int x1, 
53         GList **history, 
54         GCompletion *gcmp)
55 {
56   GList *hlist = NULL, *hcurrent = NULL;
57   char *line;
58   int x0, y, width;             
59   int cursor = 0, start = 0;            
60   int 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               strncpy(hlist->data, line, wrln_max_line_size);
136             }
137           /* get previous line */
138           hlist = hlist->prev;
139           strncpy(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       strncpy(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         case KEY_MOUSE: /* ignore mouse events */
170         case ERR: /* ingnore errors */
171           break;
173         case KEY_RESIZE:
174           /* a resize event */
175           if( x1>COLS )
176             {
177               x1=COLS;
178               width = x1-x0;
179               cursor_move_to_eol();
180             }
181           /* make shure the cursor is visible */
182           curs_set(1);
183           break;
185         case TAB:
186           if( gcmp )
187             {
188               char *prefix = NULL;
189               GList *list;
190               
191               if(wrln_pre_completion_callback)
192                 wrln_pre_completion_callback(gcmp, line);
193               list = g_completion_complete(gcmp, line, &prefix);              
194               if( prefix )
195                 {
196                   int len = strlen(prefix);
197                   strncpy(line, prefix, len);
198                   cursor_move_to_eol();
199                   g_free(prefix);
200                 }
201               else
202                 screen_bell();
203               if( wrln_post_completion_callback )
204                 wrln_post_completion_callback(gcmp, line, list);
205             }
206           break;
208         case KEY_CTRL_G:
209           screen_bell();
210           g_free(line);
211           if( history )
212             {
213               g_free(hcurrent->data);
214               hcurrent->data = NULL;
215               *history = g_list_delete_link(*history, hcurrent);
216             }
217           return NULL;
218           
219         case KEY_LEFT:
220           cursor_move_left();
221           break;
222         case KEY_RIGHT: 
223           cursor_move_right();
224           break;
225         case KEY_HOME:
226         case KEY_CTRL_A:
227           cursor = 0;
228           start = 0;
229           break;
230         case KEY_END:
231         case KEY_CTRL_E:
232           cursor_move_to_eol();
233           break;
234         case KEY_CTRL_K:
235           line[cursor] = 0;
236           break;
237         case 127:
238         case KEY_BCKSPC:        /* handle backspace: copy all */
239         case KEY_BACKSPACE:     /* chars starting from curpos */
240           if( cursor > 0 )      /* - 1 from buf[n+1] to buf   */
241             {
242               for (i = cursor - 1; line[i] != 0; i++)
243                 line[i] = line[i + 1];
244               cursor_move_left();
245             }
246           break;
247         case KEY_DC:            /* handle delete key. As above */
248         case KEY_CTRL_D:
249           if( cursor <= strlen(line) - 1 ) 
250             {
251               for (i = cursor; line[i] != 0; i++)
252                 line[i] = line[i + 1];
253             }
254           break;
255         case KEY_UP:            
256           /* get previous history entry */
257           if( history && hlist->prev )
258             {
259               if( hlist==hcurrent )
260                 {
261                   /* save the current line */
262                   strncpy(hlist->data, line, wrln_max_line_size);
263                 }
264               /* get previous line */
265               hlist = hlist->prev;
266               strncpy(line, hlist->data, wrln_max_line_size);
267             }
268           //      if (cursor > strlen(line))
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               strncpy(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                   char *tmp = 0;
296                   tmp = g_malloc0(strlen (line + cursor) + 1);
297                   strcpy (tmp, line + cursor);
298                   line[cursor] = key;
299                   line[cursor + 1] = 0;
300                   strcat (&line[cursor + 1], tmp);
301                   g_free(tmp);
302                   cursor_move_right();
303                 }
304               else
305                 {
306                   line[cursor + 1] = 0;
307                   line[cursor] = key;
308                   cursor_move_right();
309                 }
310             }
311         }
313       drawline();
314     }
316   /* update history */
317   if( history )
318     {
319       if( strlen(line) )
320         {
321           /* update the current history entry */
322           size_t size = strlen(line)+1;
323           hcurrent->data = g_realloc(hcurrent->data, size);
324           strncpy(hcurrent->data, line, size);
325         }
326       else
327         {
328           /* the line was empty - remove the current history entry */
329           g_free(hcurrent->data);
330           hcurrent->data = NULL;
331           *history = g_list_delete_link(*history, hcurrent);
332         }
334       while( g_list_length(*history) > wrln_max_history_length )
335         {
336           GList *first = g_list_first(*history);
338           /* remove the oldest history entry  */
339           g_free(first->data);
340           first->data = NULL;
341           *history = g_list_delete_link(*history, first);
342         }
343     }
344   
345   return g_realloc(line, strlen(line)+1);
347