Code

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