Code

removed my_wgetch(), switch to wgetch()
[ncmpc.git] / src / wreadln.c
1 /*
2  * (c) 2004 by Kalle Wallin <kaw@linux.se>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * You should have received a copy of the GNU General Public License
14  * along with this program; if not, write to the Free Software
15  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  *
17  */
19 #include "wreadln.h"
20 #include "charset.h"
21 #include "config.h"
23 #include <stdlib.h>
24 #include <string.h>
25 #include <glib.h>
27 #define KEY_CTRL_A   1
28 #define KEY_CTRL_B   2
29 #define KEY_CTRL_C   3
30 #define KEY_CTRL_D   4
31 #define KEY_CTRL_E   5
32 #define KEY_CTRL_F   6
33 #define KEY_CTRL_G   7
34 #define KEY_CTRL_K   11
35 #define KEY_CTRL_N   14
36 #define KEY_CTRL_P   16
37 #define KEY_CTRL_U   21
38 #define KEY_CTRL_Z   26
39 #define KEY_BCKSPC   8
40 #define TAB          9
42 #define WRLN_MAX_LINE_SIZE 1024
43 #define WRLN_MAX_HISTORY_LENGTH 32
44  
45 guint wrln_max_line_size = WRLN_MAX_LINE_SIZE;
46 guint wrln_max_history_length = WRLN_MAX_HISTORY_LENGTH;
47 void *wrln_completion_callback_data = NULL;
48 wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL;
49 wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL;
51 extern void sigstop(void);
52 extern void screen_bell(void);
54 /* move the cursor one step to the right */
55 static inline void cursor_move_right(gint *cursor,
56                                      gint *start,
57                                      gint width,
58                                      gint x0,
59                                      gint x1,
60                                      gchar *line)
61 {
62         if (*cursor < (int)strlen(line) &&
63             *cursor < (int)wrln_max_line_size - 1) {
64                 (*cursor)++;
65                 if (*cursor + x0 >= x1 && *start < *cursor - width + 1)
66                         (*start)++;
67         }
68 }
70 /* move the cursor one step to the left */
71 static inline void cursor_move_left(gint *cursor,
72                                     gint *start)
73 {
74   if( *cursor > 0 )
75     {
76       if( *cursor==*start && *start > 0 )
77         (*start)--;
78       (*cursor)--;
79     }
80 }
82 /* move the cursor to the end of the line */
83 static inline void cursor_move_to_eol(gint *cursor,
84                                       gint *start,
85                                       gint width,
86                                       gint x0,
87                                       gint x1,
88                                       gchar *line)
89 {
90   *cursor = strlen(line);
91   if( *cursor+x0 >= x1 )
92     *start = *cursor-width+1;
93 }
95 /* draw line buffer and update cursor position */
96 static inline void drawline(gint cursor,
97                             gint start,
98                             gint width,
99                             gint x0,
100                             gint y,
101                             gboolean masked,
102                             gchar *line,
103                             WINDOW *w)
105   wmove(w, y, x0);
106   /* clear input area */
107   whline(w, ' ', width);
108   /* print visible part of the line buffer */
109   if(masked == TRUE) whline(w, '*', utf8_width(line) - start);
110   else waddnstr(w, line+start, width);
111   /* move the cursor to the correct position */
112   wmove(w, y, x0 + cursor-start);
113   /* tell ncurses to redraw the screen */
114   doupdate();
118 /* libcurses version */
120 static gchar *
121 _wreadln(WINDOW *w,
122          const gchar *prompt,
123          const gchar *initial_value,
124          gint x1,
125          GList **history,
126          GCompletion *gcmp,
127          gboolean masked)
129         GList *hlist = NULL, *hcurrent = NULL;
130         gchar *line;
131         gint x0, y, width;
132         gint cursor = 0, start = 0;
133         gint key = 0, i;
135         /* allocate a line buffer */
136         line = g_malloc0(wrln_max_line_size);
137         /* turn off echo */
138         noecho();
139         /* make shure the cursor is visible */
140         curs_set(1);
141         /* print prompt string */
142         if( prompt )
143                 waddstr(w, prompt);
144         /* retrive y and x0 position */
145         getyx(w, y, x0);
146         /* check the x1 value */
147         if( x1<=x0 || x1>COLS )
148                 x1 = COLS;
149         width = x1-x0;
150         /* clear input area */
151         mvwhline(w, y, x0, ' ', width);
153         if( history ) {
154                 /* append the a new line to our history list */
155                 *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
156                 /* hlist points to the current item in the history list */
157                 hlist =  g_list_last(*history);
158                 hcurrent = hlist;
159         }
161         if( initial_value == (char *) -1 ) {
162                 /* get previous history entry */
163                 if( history && hlist->prev )
164                         {
165                                 if( hlist==hcurrent )
166                                         {
167                                                 /* save the current line */
168                                                 g_strlcpy(hlist->data, line, wrln_max_line_size);
169                                         }
170                                 /* get previous line */
171                                 hlist = hlist->prev;
172                                 g_strlcpy(line, hlist->data, wrln_max_line_size);
173                         }
174                 cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
175                 drawline(cursor, start, width, x0, y, masked, line, w);
176         } else if( initial_value ) {
177                 /* copy the initial value to the line buffer */
178                 g_strlcpy(line, initial_value, wrln_max_line_size);
179                 cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
180                 drawline(cursor, start, width, x0, y, masked, line, w);
181         }
183         while( key!=13 && key!='\n' ) {
184                 key = wgetch(w);
186                 /* check if key is a function key */
187                 for(i=0; i<63; i++)
188                         if( key==KEY_F(i) ) {
189                                 key=KEY_F(1);
190                                 i=64;
191                         }
193                 switch (key) {
194 #ifdef HAVE_GETMOUSE
195                 case KEY_MOUSE: /* ignore mouse events */
196 #endif
197                 case ERR: /* ingnore errors */
198                         break;
200                 case TAB:
201                         if( gcmp ) {
202                                 char *prefix = NULL;
203                                 GList *list;
205                                 if(wrln_pre_completion_callback)
206                                         wrln_pre_completion_callback(gcmp, line,
207                                                                      wrln_completion_callback_data);
208                                 list = g_completion_complete(gcmp, line, &prefix);
209                                 if( prefix ) {
210                                         g_strlcpy(line, prefix, wrln_max_line_size);
211                                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
212                                         g_free(prefix);
213                                 }
214                                 else
215                                         screen_bell();
216                                 if( wrln_post_completion_callback )
217                                         wrln_post_completion_callback(gcmp, line, list,
218                                                                       wrln_completion_callback_data);
219                         }
220                         break;
222                 case KEY_CTRL_G:
223                         screen_bell();
224                         g_free(line);
225                         if( history ) {
226                                 g_free(hcurrent->data);
227                                 hcurrent->data = NULL;
228                                 *history = g_list_delete_link(*history, hcurrent);
229                         }
230                         return NULL;
232                 case KEY_LEFT:
233                 case KEY_CTRL_B:
234                         cursor_move_left(&cursor, &start);
235                         break;
236                 case KEY_RIGHT:
237                 case KEY_CTRL_F:
238                         cursor_move_right(&cursor, &start, width, x0, x1, line);
239                         break;
240                 case KEY_HOME:
241                 case KEY_CTRL_A:
242                         cursor = 0;
243                         start = 0;
244                         break;
245                 case KEY_END:
246                 case KEY_CTRL_E:
247                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
248                         break;
249                 case KEY_CTRL_K:
250                         line[cursor] = 0;
251                         break;
252                 case KEY_CTRL_U:
253                         cursor = utf8_width(line);
254                         for (i = 0;i < cursor; i++)
255                                 line[i] = '\0';
256                         cursor = 0;
257                         break;
258                 case 127:
259                 case KEY_BCKSPC:        /* handle backspace: copy all */
260                 case KEY_BACKSPACE:     /* chars starting from curpos */
261                         if( cursor > 0 ) {/* - 1 from buf[n+1] to buf   */
262                                 for (i = cursor - 1; line[i] != 0; i++)
263                                         line[i] = line[i + 1];
264                                 cursor_move_left(&cursor, &start);
265                         }
266                         break;
267                 case KEY_DC:            /* handle delete key. As above */
268                 case KEY_CTRL_D:
269                         if (cursor <= (gint)utf8_width(line) - 1) {
270                                 for (i = cursor; line[i] != 0; i++)
271                                         line[i] = line[i + 1];
272                         }
273                         break;
274                 case KEY_UP:
275                 case KEY_CTRL_P:
276                         /* get previous history entry */
277                         if( history && hlist->prev ) {
278                                 if( hlist==hcurrent )
279                                         {
280                                                 /* save the current line */
281                                                 g_strlcpy(hlist->data, line, wrln_max_line_size);
282                                         }
283                                 /* get previous line */
284                                 hlist = hlist->prev;
285                                 g_strlcpy(line, hlist->data, wrln_max_line_size);
286                         }
287                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
288                         break;
289                 case KEY_DOWN:
290                 case KEY_CTRL_N:
291                         /* get next history entry */
292                         if( history && hlist->next ) {
293                                 /* get next line */
294                                 hlist = hlist->next;
295                                 g_strlcpy(line, hlist->data, wrln_max_line_size);
296                         }
297                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
298                         break;
300                 case '\n':
301                 case 13:
302                 case KEY_IC:
303                 case KEY_PPAGE:
304                 case KEY_NPAGE:
305                 case KEY_F(1):
306                         /* ignore char */
307                         break;
308                 default:
309                         if (key >= 32) {
310                                 if (strlen (line + cursor)) { /* if the cursor is */
311                                         /* not at the last pos */
312                                         gchar *tmp = NULL;
313                                         gsize size = strlen(line + cursor) + 1;
315                                         tmp = g_malloc0(size);
316                                         g_strlcpy (tmp, line + cursor, size);
317                                         line[cursor] = key;
318                                         line[cursor + 1] = 0;
319                                         g_strlcat (&line[cursor + 1], tmp, size);
320                                         g_free(tmp);
321                                         cursor_move_right(&cursor, &start, width, x0, x1, line);
322                                 } else {
323                                         line[cursor + 1] = 0;
324                                         line[cursor] = key;
325                                         cursor_move_right(&cursor, &start, width, x0, x1, line);
326                                 }
327                         }
328                 }
330                 drawline(cursor, start, width, x0, y, masked, line, w);
331         }
333         /* update history */
334         if( history ) {
335                 if( strlen(line) ) {
336                         /* update the current history entry */
337                         size_t size = strlen(line)+1;
338                         hcurrent->data = g_realloc(hcurrent->data, size);
339                         g_strlcpy(hcurrent->data, line, size);
340                 } else {
341                         /* the line was empty - remove the current history entry */
342                         g_free(hcurrent->data);
343                         hcurrent->data = NULL;
344                         *history = g_list_delete_link(*history, hcurrent);
345                 }
347                 while( g_list_length(*history) > wrln_max_history_length ) {
348                         GList *first = g_list_first(*history);
350                         /* remove the oldest history entry  */
351                         g_free(first->data);
352                         first->data = NULL;
353                         *history = g_list_delete_link(*history, first);
354                 }
355         }
357         return g_realloc(line, strlen(line)+1);
360 gchar *
361 wreadln(WINDOW *w,
362         const gchar *prompt,
363         const gchar *initial_value,
364         gint x1,
365         GList **history,
366         GCompletion *gcmp)
368         return  _wreadln(w, prompt, initial_value, x1, history, gcmp, FALSE);
371 gchar *
372 wreadln_masked(WINDOW *w,
373                const gchar *prompt,
374                const gchar *initial_value,
375                gint x1,
376                GList **history,
377                GCompletion *gcmp)
379         return  _wreadln(w, prompt, initial_value, x1, history, gcmp, TRUE);