Code

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