Code

fix function prototypes
[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 "config.h"
23 #include <stdlib.h>
24 #include <string.h>
25 #include <glib.h>
27 #ifdef USE_NCURSESW
28 #include <ncursesw/ncurses.h>
29 #else
30 #include <ncurses.h>
31 #endif
33 #include "wreadln.h"
35 #define KEY_CTRL_A   1
36 #define KEY_CTRL_C   3
37 #define KEY_CTRL_D   4 
38 #define KEY_CTRL_E   5
39 #define KEY_CTRL_G   7
40 #define KEY_CTRL_K   11
41 #define KEY_CTRL_U   21
42 #define KEY_CTRL_Z   26
43 #define KEY_BCKSPC   8
44 #define TAB          9
46 #define WRLN_MAX_LINE_SIZE 1024
47 #define WRLN_MAX_HISTORY_LENGTH 32
48  
49 guint wrln_max_line_size = WRLN_MAX_LINE_SIZE;
50 guint wrln_max_history_length = WRLN_MAX_HISTORY_LENGTH;
51 wrln_wgetch_fn_t wrln_wgetch = NULL;
52 void *wrln_completion_callback_data = NULL;
53 wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL;
54 wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL;
56 extern void sigstop(void);
57 extern void screen_bell(void);
58 extern size_t my_strlen(char *str);
60 #ifndef USE_NCURSESW
61 /* move the cursor one step to the right */
62 static inline void cursor_move_right(gint *cursor,
63                                      gint *start,
64                                      gint width,
65                                      gint x0,
66                                      gint x1,
67                                      gchar *line)
68 {
69         if (*cursor < strlen(line) && *cursor < wrln_max_line_size - 1) {
70                 (*cursor)++;
71                 if (*cursor + x0 >= x1 && *start < *cursor - width + 1)
72                         (*start)++;
73         }
74 }
76 /* move the cursor one step to the left */
77 static inline void cursor_move_left(gint *cursor,
78                                     gint *start)
79 {
80   if( *cursor > 0 )
81     {
82       if( *cursor==*start && *start > 0 )
83         (*start)--;
84       (*cursor)--;
85     }
86 }
88 /* move the cursor to the end of the line */
89 static inline void cursor_move_to_eol(gint *cursor,
90                                       gint *start,
91                                       gint width,
92                                       gint x0,
93                                       gint x1,
94                                       gchar *line)
95 {
96   *cursor = strlen(line);
97   if( *cursor+x0 >= x1 )
98     *start = *cursor-width+1;
99 }
101 /* draw line buffer and update cursor position */
102 static inline void drawline(gint cursor,
103                             gint start,
104                             gint width,
105                             gint x0,
106                             gint y,
107                             gboolean masked,
108                             gchar *line,
109                             WINDOW *w)
111   wmove(w, y, x0);
112   /* clear input area */
113   whline(w, ' ', width);
114   /* print visible part of the line buffer */
115   if(masked == TRUE) whline(w, '*', my_strlen(line)-start);
116   else waddnstr(w, line+start, width);
117   /* move the cursor to the correct position */
118   wmove(w, y, x0 + cursor-start);
119   /* tell ncurses to redraw the screen */
120   doupdate();
124 /* libcurses version */
126 static gchar *
127 _wreadln(WINDOW *w,
128          const gchar *prompt,
129          const gchar *initial_value,
130          gint x1,
131          GList **history,
132          GCompletion *gcmp,
133          gboolean masked)
135         GList *hlist = NULL, *hcurrent = NULL;
136         gchar *line;
137         gint x0, y, width;
138         gint cursor = 0, start = 0;
139         gint key = 0, i;
141         /* allocate a line buffer */
142         line = g_malloc0(wrln_max_line_size);
143         /* turn off echo */
144         noecho();
145         /* make shure the cursor is visible */
146         curs_set(1);
147         /* print prompt string */
148         if( prompt )
149                 waddstr(w, prompt);
150         /* retrive y and x0 position */
151         getyx(w, y, x0);
152         /* check the x1 value */
153         if( x1<=x0 || x1>COLS )
154                 x1 = COLS;
155         width = x1-x0;
156         /* clear input area */
157         mvwhline(w, y, x0, ' ', width);
159         if( history ) {
160                 /* append the a new line to our history list */
161                 *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
162                 /* hlist points to the current item in the history list */
163                 hlist =  g_list_last(*history);
164                 hcurrent = hlist;
165         }
167         if( initial_value == (char *) -1 ) {
168                 /* get previous history entry */
169                 if( history && hlist->prev )
170                         {
171                                 if( hlist==hcurrent )
172                                         {
173                                                 /* save the current line */
174                                                 g_strlcpy(hlist->data, line, wrln_max_line_size);
175                                         }
176                                 /* get previous line */
177                                 hlist = hlist->prev;
178                                 g_strlcpy(line, hlist->data, wrln_max_line_size);
179                         }
180                 cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
181                 drawline(cursor, start, width, x0, y, masked, line, w);
182         } else if( initial_value ) {
183                 /* copy the initial value to the line buffer */
184                 g_strlcpy(line, initial_value, wrln_max_line_size);
185                 cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
186                 drawline(cursor, start, width, x0, y, masked, line, w);
187         }
189         while( key!=13 && key!='\n' ) {
190                 if( wrln_wgetch )
191                         key = wrln_wgetch(w);
192                 else
193                         key = wgetch(w);
195                 /* check if key is a function key */
196                 for(i=0; i<63; i++)
197                         if( key==KEY_F(i) ) {
198                                 key=KEY_F(1);
199                                 i=64;
200                         }
202                 switch (key) {
203 #ifdef HAVE_GETMOUSE
204                 case KEY_MOUSE: /* ignore mouse events */
205 #endif
206                 case ERR: /* ingnore errors */
207                         break;
209                 case KEY_RESIZE:
210                         /* a resize event */
211                         if( x1>COLS ) {
212                                 x1=COLS;
213                                 width = x1-x0;
214                                 cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
215                         }
216                         /* make shure the cursor is visible */
217                         curs_set(1);
218                         break;
220                 case TAB:
221                         if( gcmp ) {
222                                 char *prefix = NULL;
223                                 GList *list;
225                                 if(wrln_pre_completion_callback)
226                                         wrln_pre_completion_callback(gcmp, line,
227                                                                      wrln_completion_callback_data);
228                                 list = g_completion_complete(gcmp, line, &prefix);
229                                 if( prefix ) {
230                                         g_strlcpy(line, prefix, wrln_max_line_size);
231                                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
232                                         g_free(prefix);
233                                 }
234                                 else
235                                         screen_bell();
236                                 if( wrln_post_completion_callback )
237                                         wrln_post_completion_callback(gcmp, line, list,
238                                                                       wrln_completion_callback_data);
239                         }
240                         break;
242                 case KEY_CTRL_G:
243                         screen_bell();
244                         g_free(line);
245                         if( history ) {
246                                 g_free(hcurrent->data);
247                                 hcurrent->data = NULL;
248                                 *history = g_list_delete_link(*history, hcurrent);
249                         }
250                         return NULL;
252                 case KEY_LEFT:
253                         cursor_move_left(&cursor, &start);
254                         break;
255                 case KEY_RIGHT:
256                         cursor_move_right(&cursor, &start, width, x0, x1, line);
257                         break;
258                 case KEY_HOME:
259                 case KEY_CTRL_A:
260                         cursor = 0;
261                         start = 0;
262                         break;
263                 case KEY_END:
264                 case KEY_CTRL_E:
265                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
266                         break;
267                 case KEY_CTRL_K:
268                         line[cursor] = 0;
269                         break;
270                 case KEY_CTRL_U:
271                         cursor = my_strlen(line);
272                         for (i = 0;i < cursor; i++)
273                                 line[i] = '\0';
274                         cursor = 0;
275                         break;
276                 case 127:
277                 case KEY_BCKSPC:        /* handle backspace: copy all */
278                 case KEY_BACKSPACE:     /* chars starting from curpos */
279                         if( cursor > 0 ) {/* - 1 from buf[n+1] to buf   */
280                                 for (i = cursor - 1; line[i] != 0; i++)
281                                         line[i] = line[i + 1];
282                                 cursor_move_left(&cursor, &start);
283                         }
284                         break;
285                 case KEY_DC:            /* handle delete key. As above */
286                 case KEY_CTRL_D:
287                         if (cursor <= my_strlen(line) - 1) {
288                                 for (i = cursor; line[i] != 0; i++)
289                                         line[i] = line[i + 1];
290                         }
291                         break;
292                 case KEY_UP:
293                         /* get previous history entry */
294                         if( history && hlist->prev ) {
295                                 if( hlist==hcurrent )
296                                         {
297                                                 /* save the current line */
298                                                 g_strlcpy(hlist->data, line, wrln_max_line_size);
299                                         }
300                                 /* get previous line */
301                                 hlist = hlist->prev;
302                                 g_strlcpy(line, hlist->data, wrln_max_line_size);
303                         }
304                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
305                         break;
306                 case KEY_DOWN:
307                         /* get next history entry */
308                         if( history && hlist->next ) {
309                                 /* get next line */
310                                 hlist = hlist->next;
311                                 g_strlcpy(line, hlist->data, wrln_max_line_size);
312                         }
313                         cursor_move_to_eol(&cursor, &start, width, x0, x1, line);
314                         break;
316                 case '\n':
317                 case 13:
318                 case KEY_IC:
319                 case KEY_PPAGE:
320                 case KEY_NPAGE:
321                 case KEY_F(1):
322                         /* ignore char */
323                         break;
324                 default:
325                         if (key >= 32) {
326                                 if (strlen (line + cursor)) { /* if the cursor is */
327                                         /* not at the last pos */
328                                         gchar *tmp = 0;
329                                         gsize size = strlen(line + cursor) + 1;
331                                         tmp = g_malloc0(size);
332                                         g_strlcpy (tmp, line + cursor, size);
333                                         line[cursor] = key;
334                                         line[cursor + 1] = 0;
335                                         g_strlcat (&line[cursor + 1], tmp, size);
336                                         g_free(tmp);
337                                         cursor_move_right(&cursor, &start, width, x0, x1, line);
338                                 } else {
339                                         line[cursor + 1] = 0;
340                                         line[cursor] = key;
341                                         cursor_move_right(&cursor, &start, width, x0, x1, line);
342                                 }
343                         }
344                 }
346                 drawline(cursor, start, width, x0, y, masked, line, w);
347         }
349         /* update history */
350         if( history ) {
351                 if( strlen(line) ) {
352                         /* update the current history entry */
353                         size_t size = strlen(line)+1;
354                         hcurrent->data = g_realloc(hcurrent->data, size);
355                         g_strlcpy(hcurrent->data, line, size);
356                 } else {
357                         /* the line was empty - remove the current history entry */
358                         g_free(hcurrent->data);
359                         hcurrent->data = NULL;
360                         *history = g_list_delete_link(*history, hcurrent);
361                 }
363                 while( g_list_length(*history) > wrln_max_history_length ) {
364                         GList *first = g_list_first(*history);
366                         /* remove the oldest history entry  */
367                         g_free(first->data);
368                         first->data = NULL;
369                         *history = g_list_delete_link(*history, first);
370                 }
371         }
373         return g_realloc(line, strlen(line)+1);
376 #else
378 /* move the cursor one step to the right */
379 static inline void cursor_move_right(gint *cursor,
380                                      gint *start,
381                                      gint width,
382                                      gint x0,
383                                      gint x1,
384                                      wchar_t *wline)
386   if( *cursor < wcslen(wline) && *cursor<wrln_max_line_size-1 )
387     {
388       (*cursor)++;
389       if( *cursor+x0 >= x1 && *start<*cursor-width+1)
390         (*start)++;
391     }
394 /* move the cursor one step to the left */
395 static inline void cursor_move_left(gint *cursor,
396                                     gint *start,
397                                     gint width,
398                                     gint x0,
399                                     gint x1,
400                                     wchar_t *line)
402   if( *cursor > 0 )
403     {
404       if( *cursor==*start && *start > 0 )
405         (*start)--;
406       (*cursor)--;
407     }
411 static inline void backspace(gint *cursor,
412                              gint *start,
413                              gint width,
414                              gint x0,
415                              gint x1,
416                              wchar_t *wline) 
418   int i;
419   if( *cursor > 0 )    
420     {
421       for (i = *cursor - 1; wline[i] != 0; i++)
422         wline[i] = wline[i + 1];
423       cursor_move_left(cursor, start, width, x0, x1, wline);
424     }
427 /* handle delete */
428 static inline void delete(gint *cursor,
429                           wchar_t *wline) 
431   int i;
432   if( *cursor <= wcslen(wline) - 1 ) 
433     {
434       for (i = *cursor; wline[i] != 0; i++)
435         wline[i] = wline[i + 1];
436     }
439 /* move the cursor to the end of the line */
440 static inline void cursor_move_to_eol(gint *cursor,
441                                       gint *start,
442                                       gint width,
443                                       gint x0,
444                                       gint x1,
445                                       wchar_t *line)
447   *cursor = wcslen(line);
448   if( *cursor+x0 >= x1 )
449     *start = *cursor-width+1;
452 /* draw line buffer and update cursor position */
453 static inline void drawline(gint cursor,
454                             gint start,
455                             gint width,
456                             gint x0,
457                             gint y,
458                             gboolean masked,
459                             wchar_t *line,
460                             WINDOW *w)
462   wmove(w, y, x0);
463   /* clear input area */
464   whline(w, ' ', width);
465   /* print visible part of the line buffer */
466   if(masked == TRUE) whline(w, '*', wcslen(line)-start);
467   else waddnwstr(w, line+start, width);
468   FILE *dbg = fopen ("dbg", "a+");
469   fprintf (dbg, "%i,%s---%i---", width, line, wcslen (line));
470   /* move the cursor to the correct position */
471   wmove(w, y, x0 + cursor-start);
472   /* tell ncurses to redraw the screen */
473   doupdate();
476 /* libcursesw version */ 
478 static gchar *
479 _wreadln(WINDOW *w,
480          const gchar *prompt,
481          const gchar *initial_value,
482          gint x1,
483          GList **history,
484          GCompletion *gcmp,
485          gboolean masked)
487         GList *hlist = NULL, *hcurrent = NULL;
488         wchar_t *wline;
489         gchar *mbline;
490         gint x0, x, y, width, start;
491         gint cursor;
492         wint_t wch;
493         gint key;
494         gint i;
496   /* initialize variables */
497   start = 0;
498   x = 0;
499   cursor = 0;
500   mbline = NULL;
502   /* allocate a line buffer */
503   wline = g_malloc0(wrln_max_line_size*sizeof(wchar_t));
504   /* turn off echo */
505   noecho();             
506   /* make shure the cursor is visible */
507   curs_set(1);
508   /* print prompt string */
509   if( prompt )
510     waddstr(w, prompt); 
511    /* retrive y and x0 position */
512   getyx(w, y, x0);
513   /* check the x1 value */
514   if( x1<=x0 || x1>COLS )
515     x1 = COLS;
516   width = x1-x0;
517   /* clear input area */
518   mvwhline(w, y, x0, ' ', width);
521   if( history )
522     {
523       /* append the a new line to our history list */
524       *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
525       /* hlist points to the current item in the history list */
526       hlist =  g_list_last(*history);
527       hcurrent = hlist;
528     }
529   if( initial_value == (char *) -1 )
530     {
531       /* get previous history entry */
532       if( history && hlist->prev )
533         {
534           if( hlist==hcurrent )
535             {
536               /* save the current line */
537               //g_strlcpy(hlist->data, line, wrln_max_line_size);
538             }
539           /* get previous line */
540           hlist = hlist->prev;
541           mbstowcs(wline, hlist->data, wrln_max_line_size);
542         }
543       cursor_move_to_eol(&cursor, &start, width, x0, x1, wline);
544       drawline(cursor, start, width, x0, y, masked, wline, w);
545     }
546   else if( initial_value )
547     {
548       /* copy the initial value to the line buffer */
549       mbstowcs(wline, initial_value, wrln_max_line_size);
550       cursor_move_to_eol(&cursor, &start, width, x0, x1, wline);
551       drawline(cursor, start, width, x0, y, masked, wline, w);
552     }  
554   wch=0;
555   key=0;
556   while( wch!=13 && wch!='\n' )
557     {
558       key = wget_wch(w, &wch);
560       if( key==KEY_CODE_YES )
561         {
562           /* function key */
563           switch(wch)
564             {
565             case KEY_HOME:
566               x=0;
567               cursor=0;
568               start=0;
569               break;
570             case KEY_END:
571               cursor_move_to_eol(&cursor, &start, width, x0, x1, wline);
572               break;
573             case KEY_LEFT:
574               cursor_move_left(&cursor, &start, width, x0, x1, wline);
575               break;
576             case KEY_RIGHT:
577               cursor_move_right(&cursor, &start, width, x0, x1, wline);
578               break;
579             case KEY_DC:
580               delete(&cursor, wline);
581               break;
582             case KEY_BCKSPC:
583             case KEY_BACKSPACE: 
584               backspace(&cursor, &start, width, x0, x1, wline);
585               break;
586             case KEY_UP:                
587               /* get previous history entry */
588               if( history && hlist->prev )
589                 {
590                   if( hlist==hcurrent )
591                     {
592                       /* save the current line */
593                       wcstombs(hlist->data, wline, wrln_max_line_size);
594                     }
595                   /* get previous line */
596                   hlist = hlist->prev;
597                   mbstowcs(wline, hlist->data, wrln_max_line_size);
598                 }
599               cursor_move_to_eol(&cursor, &start, width, x0, x1, wline);
600               break; 
601             case KEY_DOWN:      
602               /* get next history entry */
603               if( history && hlist->next )
604                 {
605                   /* get next line */
606                   hlist = hlist->next;
607                   mbstowcs(wline, hlist->data, wrln_max_line_size);
608                 }
609               cursor_move_to_eol(&cursor, &start, width, x0, x1, wline);
610               break;
611             case KEY_RESIZE:
612               /* resize event */
613               if( x1>COLS )
614                 {
615                   x1=COLS;
616                   width = x1-x0;
617                   cursor_move_to_eol(&cursor, &start, width, x0, x1, wline);
618                 }
619               /* make shure the cursor is visible */
620               curs_set(1);
621               break;
622             }
624         }
625       else if( key!=ERR )
626         {
627           switch(wch)
628             {
629             case KEY_CTRL_A:
630               x=0;
631               cursor=0;
632               start=0;
633               break;
634             case KEY_CTRL_C:
635               exit(EXIT_SUCCESS);
636               break;
637             case KEY_CTRL_D:
638               delete(&cursor, wline);
639               break;
640             case KEY_CTRL_E:
641               cursor_move_to_eol(&cursor, &start, width, x0, x1, wline);
642               break;
643             case TAB:
644               if( gcmp )
645                 {
646                   char *prefix = NULL;
647                   GList *list;
648                   
649                   i = wcstombs(NULL,wline,0)+1;
650                   mbline = g_malloc0(i);
651                   wcstombs(mbline, wline, i);
652                   
653                   if(wrln_pre_completion_callback)
654                     wrln_pre_completion_callback(gcmp, mbline, 
655                                                  wrln_completion_callback_data);
656                   list = g_completion_complete(gcmp, mbline, &prefix);        
657                   if( prefix )
658                     {
659                       mbstowcs(wline, prefix, wrln_max_line_size);
660                       cursor_move_to_eol(&cursor, &start, width, x0, x1, wline);
661                       g_free(prefix);
662                     }
663                   else
664                     screen_bell();
665                   if( wrln_post_completion_callback )
666                     wrln_post_completion_callback(gcmp, mbline, list,
667                                                   wrln_completion_callback_data);
668                   
669                   g_free(mbline);
670                 }
671               break;
672             case KEY_CTRL_G:
673               screen_bell();
674               g_free(wline);
675               if( history )
676                 {
677                   g_free(hcurrent->data);
678                   hcurrent->data = NULL;
679                   *history = g_list_delete_link(*history, hcurrent);
680                 }
681               return NULL;
682             case KEY_CTRL_K:
683               wline[cursor] = 0;
684               break;
685             case KEY_CTRL_U:
686               cursor = wcslen(wline);
687               for (i = 0;i < cursor; i++)
688                 wline[i] = '\0';
689               cursor = 0;
690               break;
691             case KEY_CTRL_Z:
692               sigstop();
693               break;
694             case 127:
695               backspace(&cursor, &start, width, x0, x1, wline);
696               break;
697             case '\n':
698             case 13:
699               /* ignore char */
700               break;
701             default:
702               if( (wcslen(wline+cursor)) )
703                 {
704                   /* the cursor is not at the last pos */
705                   wchar_t *tmp = NULL;
706                   gsize len = (wcslen(wline+cursor)+1);
707                   tmp = g_malloc0(len*sizeof(wchar_t));
708                   wmemcpy(tmp, wline+cursor, len);
709                   wline[cursor] = wch;
710                   wline[cursor+1] = 0;
711                   wcscat(&wline[cursor+1], tmp);
712                   g_free(tmp);
713                   cursor_move_right(&cursor, &start, width, x0, x1, wline);
714                 }
715               else
716                 {
717                   FILE *ff = fopen ("curspr", "a+");
718                   fprintf (ff, "%i", cursor);
719                   wline[cursor] = wch;
720                   wline[cursor+1] = 0;
721                   cursor_move_right(&cursor, &start, width, x0, x1, wline);
722                 }
723             }
724         }
725       drawline(cursor, start, width, x0, y, masked, wline, w);
726     }
727   i = wcstombs(NULL,wline,0)+1;
728   mbline = g_malloc0(i);
729   wcstombs(mbline, wline, i);
731   /* update history */
732   if( history )
733     {
734       if( strlen(mbline) )
735         {
736           /* update the current history entry */
737           size_t size = strlen(mbline)+1;
738           hcurrent->data = g_realloc(hcurrent->data, size);
739           g_strlcpy(hcurrent->data, mbline, size);
740         }
741       else
742         {
743           /* the line was empty - remove the current history entry */
744           g_free(hcurrent->data);
745           hcurrent->data = NULL;
746           *history = g_list_delete_link(*history, hcurrent);
747         }
749       while( g_list_length(*history) > wrln_max_history_length )
750         {
751           GList *first = g_list_first(*history);
753           /* remove the oldest history entry  */
754           g_free(first->data);
755           first->data = NULL;
756           *history = g_list_delete_link(*history, first);
757         }
758     }
759   return mbline;
761  
762 #endif
764 gchar *
765 wreadln(WINDOW *w,
766         const gchar *prompt,
767         const gchar *initial_value,
768         gint x1,
769         GList **history,
770         GCompletion *gcmp)
772         return  _wreadln(w, prompt, initial_value, x1, history, gcmp, FALSE);
775 gchar *
776 wreadln_masked(WINDOW *w,
777                const gchar *prompt,
778                const gchar *initial_value,
779                gint x1,
780                GList **history,
781                GCompletion *gcmp)
783         return  _wreadln(w, prompt, initial_value, x1, history, gcmp, TRUE);