Code

wreadln: convert public globals to local constants
[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 "screen_utils.h"
22 #include "config.h"
24 #include <stdlib.h>
25 #include <string.h>
26 #include <glib.h>
28 #define KEY_CTRL_A   1
29 #define KEY_CTRL_B   2
30 #define KEY_CTRL_C   3
31 #define KEY_CTRL_D   4
32 #define KEY_CTRL_E   5
33 #define KEY_CTRL_F   6
34 #define KEY_CTRL_G   7
35 #define KEY_CTRL_K   11
36 #define KEY_CTRL_N   14
37 #define KEY_CTRL_P   16
38 #define KEY_CTRL_U   21
39 #define KEY_CTRL_Z   26
40 #define KEY_BCKSPC   8
41 #define TAB          9
43 /** max size allocated for a line */
44 static const size_t wrln_max_line_size = 1024;
46 /** max items stored in the history list */
47 static const guint wrln_max_history_length = 32;
49 void *wrln_completion_callback_data = NULL;
50 wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL;
51 wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL;
53 /* move the cursor one step to the right */
54 static inline void cursor_move_right(gint *cursor,
55                                      gint *start,
56                                      gint width,
57                                      gint x0,
58                                      gint x1,
59                                      gchar *line)
60 {
61         if (*cursor < (int)strlen(line) &&
62             *cursor < (int)wrln_max_line_size - 1) {
63                 (*cursor)++;
64                 if (*cursor + x0 >= x1 && *start < *cursor - width + 1)
65                         (*start)++;
66         }
67 }
69 /* move the cursor one step to the left */
70 static inline void cursor_move_left(gint *cursor,
71                                     gint *start)
72 {
73         if (*cursor > 0) {
74                 if (*cursor == *start && *start > 0)
75                         (*start)--;
76                 (*cursor)--;
77         }
78 }
80 /* move the cursor to the end of the line */
81 static inline void cursor_move_to_eol(gint *cursor,
82                                       gint *start,
83                                       gint width,
84                                       gint x0,
85                                       gint x1,
86                                       gchar *line)
87 {
88         *cursor = strlen(line);
89         if (*cursor + x0 >= x1)
90                 *start = *cursor - width + 1;
91 }
93 /* draw line buffer and update cursor position */
94 static inline void drawline(gint cursor,
95                             gint start,
96                             gint width,
97                             gint x0,
98                             gint y,
99                             gboolean masked,
100                             gchar *line,
101                             WINDOW *w)
103         wmove(w, y, x0);
104         /* clear input area */
105         whline(w, ' ', width);
106         /* print visible part of the line buffer */
107         if(masked == TRUE)
108                 whline(w, '*', utf8_width(line) - start);
109         else
110                 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();
117 /* libcurses version */
119 static gchar *
120 _wreadln(WINDOW *w,
121          const gchar *prompt,
122          const gchar *initial_value,
123          gint x1,
124          GList **history,
125          GCompletion *gcmp,
126          gboolean masked)
128         GList *hlist = NULL, *hcurrent = NULL;
129         gchar *line;
130         gint x0, y, width;
131         gint cursor = 0, start = 0;
132         gint key = 0, i;
134         /* allocate a line buffer */
135         line = g_malloc0(wrln_max_line_size);
136         /* turn off echo */
137         noecho();
138         /* make shure the cursor is visible */
139         curs_set(1);
140         /* print prompt string */
141         if (prompt)
142                 waddstr(w, prompt);
143         /* retrive y and x0 position */
144         getyx(w, y, x0);
145         /* check the x1 value */
146         if (x1 <= x0 || x1 > COLS)
147                 x1 = COLS;
148         width = x1 - x0;
149         /* clear input area */
150         mvwhline(w, y, x0, ' ', width);
152         if (history) {
153                 /* append the a new line to our history list */
154                 *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
155                 /* hlist points to the current item in the history list */
156                 hlist = g_list_last(*history);
157                 hcurrent = hlist;
158         }
160         if (initial_value == (char *)-1) {
161                 /* get previous history entry */
162                 if (history && hlist->prev) {
163                         if (hlist == hcurrent)
164                                 /* save the current line */
165                                 g_strlcpy(hlist->data, line, wrln_max_line_size);
167                         /* get previous line */
168                         hlist = hlist->prev;
169                         g_strlcpy(line, hlist->data, wrln_max_line_size);
170                 }
171                 cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
172                 drawline(cursor, start, width, x0, y, masked, line, w);
173         } else if (initial_value) {
174                 /* copy the initial value to the line buffer */
175                 g_strlcpy(line, initial_value, wrln_max_line_size);
176                 cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
177                 drawline(cursor, start, width, x0, y, masked, line, w);
178         }
180         while (key != 13 && key != '\n') {
181                 key = wgetch(w);
183                 /* check if key is a function key */
184                 for (i = 0; i < 63; i++)
185                         if (key == KEY_F(i)) {
186                                 key = KEY_F(1);
187                                 i = 64;
188                         }
190                 switch (key) {
191 #ifdef HAVE_GETMOUSE
192                 case KEY_MOUSE: /* ignore mouse events */
193 #endif
194                 case ERR: /* ingnore errors */
195                         break;
197                 case TAB:
198                         if (gcmp) {
199                                 char *prefix = NULL;
200                                 GList *list;
202                                 if (wrln_pre_completion_callback)
203                                         wrln_pre_completion_callback(gcmp, line,
204                                                                      wrln_completion_callback_data);
205                                 list = g_completion_complete(gcmp, line, &prefix);
206                                 if (prefix) {
207                                         g_strlcpy(line, prefix, wrln_max_line_size);
208                                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
209                                         g_free(prefix);
210                                 } else
211                                         screen_bell();
213                                 if (wrln_post_completion_callback)
214                                         wrln_post_completion_callback(gcmp, line, list,
215                                                                       wrln_completion_callback_data);
216                         }
217                         break;
219                 case KEY_CTRL_G:
220                         screen_bell();
221                         g_free(line);
222                         if (history) {
223                                 g_free(hcurrent->data);
224                                 hcurrent->data = NULL;
225                                 *history = g_list_delete_link(*history, hcurrent);
226                         }
227                         return NULL;
229                 case KEY_LEFT:
230                 case KEY_CTRL_B:
231                         cursor_move_left(&cursor, &start);
232                         break;
233                 case KEY_RIGHT:
234                 case KEY_CTRL_F:
235                         cursor_move_right(&cursor, &start, width, x0, x1, line);
236                         break;
237                 case KEY_HOME:
238                 case KEY_CTRL_A:
239                         cursor = 0;
240                         start = 0;
241                         break;
242                 case KEY_END:
243                 case KEY_CTRL_E:
244                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
245                         break;
246                 case KEY_CTRL_K:
247                         line[cursor] = 0;
248                         break;
249                 case KEY_CTRL_U:
250                         cursor = utf8_width(line);
251                         for (i = 0;i < cursor; i++)
252                                 line[i] = '\0';
253                         cursor = 0;
254                         break;
255                 case 127:
256                 case KEY_BCKSPC:        /* handle backspace: copy all */
257                 case KEY_BACKSPACE:     /* chars starting from curpos */
258                         if( cursor > 0 ) {/* - 1 from buf[n+1] to buf   */
259                                 for (i = cursor - 1; line[i] != 0; i++)
260                                         line[i] = line[i + 1];
261                                 cursor_move_left(&cursor, &start);
262                         }
263                         break;
264                 case KEY_DC:            /* handle delete key. As above */
265                 case KEY_CTRL_D:
266                         if (cursor <= (gint)utf8_width(line) - 1) {
267                                 for (i = cursor; line[i] != 0; i++)
268                                         line[i] = line[i + 1];
269                         }
270                         break;
271                 case KEY_UP:
272                 case KEY_CTRL_P:
273                         /* get previous history entry */
274                         if (history && hlist->prev) {
275                                 if (hlist == hcurrent)
276                                         /* save the current line */
277                                         g_strlcpy(hlist->data, line, wrln_max_line_size);
279                                 /* get previous line */
280                                 hlist = hlist->prev;
281                                 g_strlcpy(line, hlist->data, wrln_max_line_size);
282                         }
283                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
284                         break;
285                 case KEY_DOWN:
286                 case KEY_CTRL_N:
287                         /* get next history entry */
288                         if (history && hlist->next) {
289                                 /* get next line */
290                                 hlist = hlist->next;
291                                 g_strlcpy(line, hlist->data, wrln_max_line_size);
292                         }
293                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
294                         break;
296                 case '\n':
297                 case 13:
298                 case KEY_IC:
299                 case KEY_PPAGE:
300                 case KEY_NPAGE:
301                 case KEY_F(1):
302                         /* ignore char */
303                         break;
304                 default:
305                         if (key >= 32) {
306                                 if (strlen (line + cursor)) { /* if the cursor is */
307                                         /* not at the last pos */
308                                         gchar *tmp = NULL;
309                                         gsize size = strlen(line + cursor) + 1;
311                                         tmp = g_malloc0(size);
312                                         g_strlcpy (tmp, line + cursor, size);
313                                         line[cursor] = key;
314                                         line[cursor + 1] = 0;
315                                         g_strlcat (&line[cursor + 1], tmp, size);
316                                         g_free(tmp);
317                                         cursor_move_right(&cursor, &start, width, x0, x1, line);
318                                 } else {
319                                         line[cursor + 1] = 0;
320                                         line[cursor] = key;
321                                         cursor_move_right(&cursor, &start, width, x0, x1, line);
322                                 }
323                         }
324                 }
326                 drawline(cursor, start, width, x0, y, masked, line, w);
327         }
329         /* update history */
330         if (history) {
331                 if (strlen(line)) {
332                         /* update the current history entry */
333                         size_t size = strlen(line)+1;
334                         hcurrent->data = g_realloc(hcurrent->data, size);
335                         g_strlcpy(hcurrent->data, line, size);
336                 } else {
337                         /* the line was empty - remove the current history entry */
338                         g_free(hcurrent->data);
339                         hcurrent->data = NULL;
340                         *history = g_list_delete_link(*history, hcurrent);
341                 }
343                 while (g_list_length(*history) > wrln_max_history_length) {
344                         GList *first = g_list_first(*history);
346                         /* remove the oldest history entry  */
347                         g_free(first->data);
348                         first->data = NULL;
349                         *history = g_list_delete_link(*history, first);
350                 }
351         }
353         return g_realloc(line, strlen(line)+1);
356 gchar *
357 wreadln(WINDOW *w,
358         const gchar *prompt,
359         const gchar *initial_value,
360         gint x1,
361         GList **history,
362         GCompletion *gcmp)
364         return  _wreadln(w, prompt, initial_value, x1, history, gcmp, FALSE);
367 gchar *
368 wreadln_masked(WINDOW *w,
369                const gchar *prompt,
370                const gchar *initial_value,
371                gint x1,
372                GList **history,
373                GCompletion *gcmp)
375         return  _wreadln(w, prompt, initial_value, x1, history, gcmp, TRUE);