Code

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