Code

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