Code

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