Code

translation updates, template and german. another little build fix
[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 wrln_gcmp_pre_cb_t wrln_pre_completion_callback = NULL;
52 wrln_gcmp_post_cb_t wrln_post_completion_callback = NULL;
54 extern void sigstop(void);
55 extern void screen_bell(void);
56 extern size_t my_strlen(char *str);
59 #ifndef USE_NCURSESW
60 /* libcurses version */
62 gchar *
63 wreadln(WINDOW *w, 
64         gchar *prompt, 
65         gchar *initial_value,
66         gint x1, 
67         GList **history, 
68         GCompletion *gcmp)
69 {
70   GList *hlist = NULL, *hcurrent = NULL;
71   gchar *line;
72   gint x0, y, width;            
73   gint cursor = 0, start = 0;           
74   gint key = 0, i;
76   /* move the cursor one step to the right */
77   void cursor_move_right(void) {
78     if( cursor < my_strlen(line) && cursor<wrln_max_line_size-1 )
79       {
80         cursor++;
81         if( cursor+x0 >= x1 && start<cursor-width+1)
82           start++;
83       }
84   }
85   /* move the cursor one step to the left */
86   void cursor_move_left(void) {
87     if( cursor > 0 )
88       {
89         if( cursor==start && start > 0 )
90           start--;
91         cursor--;
92       }
93   }
94  /* move the cursor to the end of the line */
95   void cursor_move_to_eol(void) {
96     cursor = my_strlen(line);
97     if( cursor+x0 >= x1 )
98       start = cursor-width+1;
99   }
100   /* draw line buffer and update cursor position */
101   void drawline() {
102     wmove(w, y, x0);
103     /* clear input area */
104     whline(w, ' ', width);
105     /* print visible part of the line buffer */
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();
111   }
114   /* allocate a line buffer */
115   line = g_malloc0(wrln_max_line_size);
116   /* turn off echo */
117   noecho();             
118   /* make shure the cursor is visible */
119   curs_set(1);
120   /* print prompt string */
121   if( prompt )
122     waddstr(w, prompt);         
123   /* retrive y and x0 position */
124   getyx(w, y, x0);      
125   /* check the x1 value */
126   if( x1<=x0 || x1>COLS )
127     x1 = COLS;
128   width = x1-x0;
129   /* clear input area */
130   mvwhline(w, y, x0, ' ', width);       
132   if( history )
133     {
134       /* append the a new line to our history list */
135       *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
136       /* hlist points to the current item in the history list */
137       hlist =  g_list_last(*history);
138       hcurrent = hlist;
139     }
141   if( initial_value == (char *) -1 )
142     {
143       /* get previous history entry */
144       if( history && hlist->prev )
145         {
146           if( hlist==hcurrent )
147             {
148               /* save the current line */
149               g_strlcpy(hlist->data, line, wrln_max_line_size);
150             }
151           /* get previous line */
152           hlist = hlist->prev;
153           g_strlcpy(line, hlist->data, wrln_max_line_size);
154         }
155       cursor_move_to_eol();
156       drawline();
157     }
158   else if( initial_value )
159     {
160       /* copy the initial value to the line buffer */
161       g_strlcpy(line, initial_value, wrln_max_line_size);
162       cursor_move_to_eol();
163       drawline();
164     }  
166   while( key!=13 && key!='\n' )
167     {
168       if( wrln_wgetch )
169         key = wrln_wgetch(w);
170       else
171         key = wgetch(w);
173       /* check if key is a function key */
174       for(i=0; i<63; i++)
175         if( key==KEY_F(i) )
176           {
177             key=KEY_F(1);
178             i=64;
179           }
181       switch (key)
182         {
183 #ifdef HAVE_GETMOUSE
184         case KEY_MOUSE: /* ignore mouse events */
185 #endif
186         case ERR: /* ingnore errors */
187           break;
189         case KEY_RESIZE:
190           /* a resize event */
191           if( x1>COLS )
192             {
193               x1=COLS;
194               width = x1-x0;
195               cursor_move_to_eol();
196             }
197           /* make shure the cursor is visible */
198           curs_set(1);
199           break;
201         case TAB:
202           if( gcmp )
203             {
204               char *prefix = NULL;
205               GList *list;
206               
207               if(wrln_pre_completion_callback)
208                 wrln_pre_completion_callback(gcmp, line);
209               list = g_completion_complete(gcmp, line, &prefix);              
210               if( prefix )
211                 {
212                   g_strlcpy(line, prefix, wrln_max_line_size);
213                   cursor_move_to_eol();
214                   g_free(prefix);
215                 }
216               else
217                 screen_bell();
218               if( wrln_post_completion_callback )
219                 wrln_post_completion_callback(gcmp, line, list);
220             }
221           break;
223         case KEY_CTRL_G:
224           screen_bell();
225           g_free(line);
226           if( history )
227             {
228               g_free(hcurrent->data);
229               hcurrent->data = NULL;
230               *history = g_list_delete_link(*history, hcurrent);
231             }
232           return NULL;
233           
234         case KEY_LEFT:
235           cursor_move_left();
236           break;
237         case KEY_RIGHT: 
238           cursor_move_right();
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();
248           break;
249         case KEY_CTRL_K:
250           line[cursor] = 0;
251           break;
252         case 127:
253         case KEY_BCKSPC:        /* handle backspace: copy all */
254         case KEY_BACKSPACE:     /* chars starting from curpos */
255           if( cursor > 0 )      /* - 1 from buf[n+1] to buf   */
256             {
257               for (i = cursor - 1; line[i] != 0; i++)
258                 line[i] = line[i + 1];
259               cursor_move_left();
260             }
261           break;
262         case KEY_DC:            /* handle delete key. As above */
263         case KEY_CTRL_D:
264           if( cursor <= my_strlen(line) - 1 ) 
265             {
266               for (i = cursor; line[i] != 0; i++)
267                 line[i] = line[i + 1];
268             }
269           break;
270         case KEY_UP:            
271           /* get previous history entry */
272           if( history && hlist->prev )
273             {
274               if( hlist==hcurrent )
275                 {
276                   /* save the current line */
277                   g_strlcpy(hlist->data, line, wrln_max_line_size);
278                 }
279               /* get previous line */
280               hlist = hlist->prev;
281               g_strlcpy(line, hlist->data, wrln_max_line_size);
282             }
283           cursor_move_to_eol();
284           break;
285         case KEY_DOWN:  
286           /* get next history entry */
287           if( history && hlist->next )
288             {
289               /* get next line */
290               hlist = hlist->next;
291               g_strlcpy(line, hlist->data, wrln_max_line_size);
292             }
293           cursor_move_to_eol();
294           break;
295           
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             {
307               if (strlen (line + cursor))       /* if the cursor is */
308                 {                               /* not at the last pos */
309                   gchar *tmp = 0;
310                   gsize size = strlen(line + cursor) + 1;
312                   tmp = g_malloc0(size);
313                   g_strlcpy (tmp, line + cursor, size);
314                   line[cursor] = key;
315                   line[cursor + 1] = 0;
316                   g_strlcat (&line[cursor + 1], tmp, size);
317                   g_free(tmp);
318                   cursor_move_right();
319                 }
320               else
321                 {
322                   line[cursor + 1] = 0;
323                   line[cursor] = key;
324                   cursor_move_right();
325                 }
326             }
327         }
329       drawline();
330     }
332   /* update history */
333   if( history )
334     {
335       if( strlen(line) )
336         {
337           /* update the current history entry */
338           size_t size = strlen(line)+1;
339           hcurrent->data = g_realloc(hcurrent->data, size);
340           g_strlcpy(hcurrent->data, line, size);
341         }
342       else
343         {
344           /* the line was empty - remove the current history entry */
345           g_free(hcurrent->data);
346           hcurrent->data = NULL;
347           *history = g_list_delete_link(*history, hcurrent);
348         }
350       while( g_list_length(*history) > wrln_max_history_length )
351         {
352           GList *first = g_list_first(*history);
354           /* remove the oldest history entry  */
355           g_free(first->data);
356           first->data = NULL;
357           *history = g_list_delete_link(*history, first);
358         }
359     }
360   
361   return g_realloc(line, strlen(line)+1);
364 #else
365 /* libcursesw version */ 
367 gchar *
368 wreadln(WINDOW *w, 
369         gchar *prompt, 
370         gchar *initial_value,
371         gint x1, 
372         GList **history, 
373         GCompletion *gcmp)
375   GList *hlist = NULL, *hcurrent = NULL;
376   wchar_t *wline;
377   gchar *mbline;
378   gint x0, x, y, width, start;
379   gint cursor;
380   wint_t wch;
381   gint key;
382   gint i;
384   /* move the cursor to the beginning of the line */
385   void cursor_move_home(void) {
386     x=0;
387     cursor=0;
388     start=0;
389   }
390   /* move the cursor to the end of the line */
391   void cursor_move_to_eol(void) {
392     cursor = wcslen(wline);
393     //x=wcswidth(wline,cursor);
394     if( cursor+x0 >= x1 )
395       start = cursor-width+1;
396   }
397   /* move the cursor one step to the left */
398   void cursor_move_left(void) {
399     if( cursor > 0 )
400       {
401         if( cursor==start && start > 0 )
402           start--;
403         //x-=wcwidth(wline[cursor]); 
404         cursor--;
405       }
406   }
407   /* move the cursor one step to the right */
408   void cursor_move_right(void) {
409     if( cursor < wcslen(wline) && cursor<wrln_max_line_size-1 )
410       {
411         //x +=wcwidth(wline[cursor]); 
412         cursor++;
413         if( cursor+x0 >= x1 && start<cursor-width+1)
414           start++;
415       }
416   }
417   /* handle backspace */
418   void backspace() {
419     if( cursor > 0 )    
420       {
421         for (i = cursor - 1; wline[i] != 0; i++)
422           wline[i] = wline[i + 1];
423         cursor_move_left();
424       }
425   }
426   /* handle delete */
427   void delete() {
428     if( cursor <= wcslen(wline) - 1 ) 
429       {
430         for (i = cursor; wline[i] != 0; i++)
431           wline[i] = wline[i + 1];
432       }
433   }
434   /* draw line buffer and update cursor position */
435   void drawline() {
436     wmove(w, y, x0);
437     /* clear input area */
438     whline(w, ' ', width);
439     /* print visible part of the line buffer */
440     waddnwstr(w, wline+start, width);
441     /* move the cursor to the correct position */
442     wmove(w, y, x0 + cursor-start);
443     /* tell ncurses to redraw the screen */
444     doupdate();
445   }
447   /* initialize variables */
448   start = 0;
449   x = 0;
450   cursor = 0;
451   mbline = NULL;
453   /* allocate a line buffer */
454   wline = g_malloc0(wrln_max_line_size*sizeof(wchar_t));
455   /* turn off echo */
456   noecho();             
457   /* make shure the cursor is visible */
458   curs_set(1);
459   /* print prompt string */
460   if( prompt )
461     waddstr(w, prompt); 
462    /* retrive y and x0 position */
463   getyx(w, y, x0);
464   /* check the x1 value */
465   if( x1<=x0 || x1>COLS )
466     x1 = COLS;
467   width = x1-x0;
468   /* clear input area */
469   mvwhline(w, y, x0, ' ', width);
472   if( history )
473     {
474       /* append the a new line to our history list */
475       *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
476       /* hlist points to the current item in the history list */
477       hlist =  g_list_last(*history);
478       hcurrent = hlist;
479     }
480   if( initial_value == (char *) -1 )
481     {
482       /* get previous history entry */
483       if( history && hlist->prev )
484         {
485           if( hlist==hcurrent )
486             {
487               /* save the current line */
488               //g_strlcpy(hlist->data, line, wrln_max_line_size);
489             }
490           /* get previous line */
491           hlist = hlist->prev;
492           mbstowcs(wline, hlist->data, wrln_max_line_size);
493         }
494       cursor_move_to_eol();
495       drawline();
496     }
497   else if( initial_value )
498     {
499       /* copy the initial value to the line buffer */
500       mbstowcs(wline, initial_value, wrln_max_line_size);
501       cursor_move_to_eol();
502       drawline();
503     }  
505   wch=0;
506   key=0;
507   while( wch!=13 && wch!='\n' )
508     {
509       key = wget_wch(w, &wch);
511       if( key==KEY_CODE_YES )
512         {
513           /* function key */
514           switch(wch)
515             {
516             case KEY_HOME:
517               cursor_move_home();
518               break;
519             case KEY_END:
520               cursor_move_to_eol();
521               break;
522             case KEY_LEFT:
523               cursor_move_left();
524               break;
525             case KEY_RIGHT:
526               cursor_move_right();
527               break;
528             case KEY_DC:
529               delete();
530               break;
531             case KEY_BCKSPC:
532             case KEY_BACKSPACE: 
533               backspace();
534               break;
535             case KEY_UP:                
536               /* get previous history entry */
537               if( history && hlist->prev )
538                 {
539                   if( hlist==hcurrent )
540                     {
541                       /* save the current line */
542                       wcstombs(hlist->data, wline, wrln_max_line_size);
543                     }
544                   /* get previous line */
545                   hlist = hlist->prev;
546                   mbstowcs(wline, hlist->data, wrln_max_line_size);
547                 }
548               cursor_move_to_eol();
549               break; 
550             case KEY_DOWN:      
551               /* get next history entry */
552               if( history && hlist->next )
553                 {
554                   /* get next line */
555                   hlist = hlist->next;
556                   mbstowcs(wline, hlist->data, wrln_max_line_size);
557                 }
558               cursor_move_to_eol();
559               break;
560             case KEY_RESIZE:
561               /* resize event */
562               if( x1>COLS )
563                 {
564                   x1=COLS;
565                   width = x1-x0;
566                   cursor_move_to_eol();
567                 }
568               /* make shure the cursor is visible */
569               curs_set(1);
570               break;
571             }
573         }
574       else if( key!=ERR )
575         {
576           switch(wch)
577             {
578             case KEY_CTRL_A:
579               cursor_move_home();
580               break;
581             case KEY_CTRL_C:
582               exit(EXIT_SUCCESS);
583               break;
584             case KEY_CTRL_D:
585               delete();
586               break;
587             case KEY_CTRL_E:
588               cursor_move_to_eol();
589               break;
590             case TAB:
591               if( gcmp )
592                 {
593                   char *prefix = NULL;
594                   GList *list;
595                   
596                   i = wcstombs(NULL,wline,0)+1;
597                   mbline = g_malloc0(i);
598                   wcstombs(mbline, wline, i);
599                   
600                   if(wrln_pre_completion_callback)
601                     wrln_pre_completion_callback(gcmp, mbline);
602                   list = g_completion_complete(gcmp, mbline, &prefix);        
603                   if( prefix )
604                     {
605                       mbstowcs(wline, prefix, wrln_max_line_size);
606                       cursor_move_to_eol();
607                       g_free(prefix);
608                     }
609                   else
610                     screen_bell();
611                   if( wrln_post_completion_callback )
612                     wrln_post_completion_callback(gcmp, mbline, list);
613                   
614                   g_free(mbline);
615                 }
616               break;
617             case KEY_CTRL_G:
618               screen_bell();
619               g_free(wline);
620               if( history )
621                 {
622                   g_free(hcurrent->data);
623                   hcurrent->data = NULL;
624                   *history = g_list_delete_link(*history, hcurrent);
625                 }
626               return NULL;
627             case KEY_CTRL_K:
628               wline[cursor] = 0;
629               break;
630             case KEY_CTRL_Z:
631               sigstop();
632               break;
633             case 127:
634               backspace();
635               break;
636             case '\n':
637             case 13:
638               /* ignore char */
639               break;
640             default:
641               if( (wcslen(wline+cursor)) )
642                 {
643                   /* the cursor is not at the last pos */
644                   wchar_t *tmp = NULL;
645                   gsize len = (wcslen(wline+cursor)+1);
646                   tmp = g_malloc0(len*sizeof(wchar_t));
647                   wmemcpy(tmp, wline+cursor, len);
648                   wline[cursor] = wch;
649                   wline[cursor+1] = 0;
650                   wcscat(&wline[cursor+1], tmp);
651                   g_free(tmp);
652                   cursor_move_right();
653                 }
654               else
655                 {
656                   wline[cursor] = wch;
657                   wline[cursor+1] = 0;
658                   cursor_move_right();
659                 }
660             }
661         }
662       drawline();
663     }
665   i = wcstombs(NULL,wline,0)+1;
666   mbline = g_malloc0(i);
667   wcstombs(mbline, wline, i);
669   /* update history */
670   if( history )
671     {
672       if( strlen(mbline) )
673         {
674           /* update the current history entry */
675           size_t size = strlen(mbline)+1;
676           hcurrent->data = g_realloc(hcurrent->data, size);
677           g_strlcpy(hcurrent->data, mbline, size);
678         }
679       else
680         {
681           /* the line was empty - remove the current history entry */
682           g_free(hcurrent->data);
683           hcurrent->data = NULL;
684           *history = g_list_delete_link(*history, hcurrent);
685         }
687       while( g_list_length(*history) > wrln_max_history_length )
688         {
689           GList *first = g_list_first(*history);
691           /* remove the oldest history entry  */
692           g_free(first->data);
693           first->data = NULL;
694           *history = g_list_delete_link(*history, first);
695         }
696     }
697   return mbline;
699  
700 #endif