Code

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