From e76e311607a7893b3ffc6e1907e8c28d8983d2d1 Mon Sep 17 00:00:00 2001
From: Kalle Wallin <kaw@linux.se>
Date: Sun, 6 Jun 2004 16:40:16 +0000
Subject: Added wreadln.c, wreadln.h a simple line editor

git-svn-id: https://svn.musicpd.org/ncmpc/trunk@1351 09075e82-0dd4-0310-85a5-a0d7c8717e4f
---
 ChangeLog          |   6 +
 src/Makefile.am    |   5 +-
 src/main.c         |  11 +-
 src/screen.c       |  38 ++++--
 src/screen.h       |   1 +
 src/screen_utils.c |  44 ++++---
 src/wreadln.c      | 339 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/wreadln.h      |  25 ++++
 8 files changed, 434 insertions(+), 35 deletions(-)
 create mode 100644 src/wreadln.c
 create mode 100644 src/wreadln.h

diff --git a/ChangeLog b/ChangeLog
index e37371848..fcd582aea 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2004-06-06 Kalle Wallin <kaw@linux.se>
 	* i18n: ncmpc is now bilingual (sv)
+	* Moved all locale initialization code to main.c
+	* main.c: added bind_textdomain_codeset() 	
+	* Added wreadln.c, wreadln.h a simple line editor
+	* screen_utils.c: use wreadln() instead of curses wgetstr()
+	* Added a history for the find command!
+
 
 2004-06-05 Kalle Wallin <kaw@linux.se>
 	* Added initial i18n support
diff --git a/src/Makefile.am b/src/Makefile.am
index 59a7f4a70..ff4fb7aa9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,12 +11,13 @@ AM_CPPFLAGS = $(GLIB_CFLAGS) -DLOCALE_DIR=\""$(datadir)/locale"\" -DSYSCONFDIR=\
 
 ncmpc_headers = libmpdclient.h mpc.h options.h conf.h command.h screen.h \
                 screen_utils.h screen_play.h screen_file.h screen_search.h \
-	        screen_help.h list_window.h colors.h support.h ncmpc.h
+	        screen_help.h list_window.h colors.h support.h wreadln.h \
+                ncmpc.h
 
 ncmpc_SOURCES = libmpdclient.c main.c mpc.c options.c conf.c command.c \
 		screen.c screen_utils.c screen_play.c screen_file.c \
 		screen_search.c screen_help.c screen_keydef.c \
-                list_window.c colors.c support.c $(ncmpc_headers)
+                list_window.c colors.c support.c wreadln.c $(ncmpc_headers)
 
 
 
diff --git a/src/main.c b/src/main.c
index 1a3573e7a..176bcf8a0 100644
--- a/src/main.c
+++ b/src/main.c
@@ -69,13 +69,12 @@ main(int argc, const char *argv[])
   gboolean connected;
   const char *charset = NULL;
 
-  /* initialize charset */
 #ifdef HAVE_LOCALE_H
-  if( setlocale(LC_CTYPE,"") == NULL )
-    {
-      g_printerr("setlocale() - failed!\n");
-      exit(EXIT_FAILURE);
-    }
+  /* time and date formatting */
+  setlocale(LC_TIME,"");
+  /* charset */
+  setlocale(LC_CTYPE,"");
+  /* initialize charset conversions */
   charset_init(g_get_charset(&charset));
   D(printf("charset: %s\n", charset));
 #endif
diff --git a/src/screen.c b/src/screen.c
index 4eac178b7..7522ad227 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -1,5 +1,7 @@
 /* 
- * (c) 2004 by Kalle Wallin (kaw@linux.se)
+ * $Id$
+ *
+ * (c) 2004 by Kalle Wallin <kaw@linux.se>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -32,6 +34,7 @@
 #include "command.h"
 #include "options.h"
 #include "colors.h"
+#include "wreadln.h"
 #include "screen.h"
 #include "screen_play.h"
 #include "screen_file.h"
@@ -39,7 +42,7 @@
 #include "screen_search.h"
 #include "screen_utils.h"
 
-#undef  ENABLE_STATUS_LINE_CLOCK
+#define ENABLE_STATUS_LINE_CLOCK
 #define ENABLE_SCROLLING
 
 #define DEFAULT_CROSSFADE_TIME 10
@@ -294,15 +297,17 @@ paint_status_window(mpd_client_t *c)
 	  snprintf(screen->buf, screen->buf_size,  " [%d kbps]", status->bitRate );
 	}
     }
+#ifdef ENABLE_STATUS_LINE_CLOCK
   else
     {
       time_t timep;
 
       time(&timep);
       /* Note: setlocale(LC_TIME,"") should be used first */
-      //strftime(screen->buf, screen->buf_size, "%X ",  localtime(&timep));
-      strftime(screen->buf, screen->buf_size, " %k:%M",  localtime(&timep));
+      //strftime(screen->buf, screen->buf_size, "%x  - %X ",localtime(&timep));
+      strftime(screen->buf, screen->buf_size, "%X ",localtime(&timep));
     }
+#endif
 
   /* display song */
   if( (IS_PLAYING(status->state) || IS_PAUSED(status->state)) &&  song )
@@ -333,7 +338,20 @@ paint_status_window(mpd_client_t *c)
   wnoutrefresh(w);
 }
 
-
+GList *
+screen_free_string_list(GList *list)
+{
+  GList *l = g_list_first(list);
+  
+  while(l)
+    {
+      g_free(l->data);
+      l->data = NULL;
+      l=l->next;
+    }
+  g_list_free(list);
+  return NULL;
+}
 
 int
 screen_exit(void)
@@ -356,9 +374,10 @@ screen_exit(void)
 	  list=list->next;
 	}
       g_list_free(screen->screen_list);
-
+      screen_free_string_list(screen->find_history);
       g_free(screen->buf);
       g_free(screen->findbuf);
+      
       g_free(screen);
       screen = NULL;
     }
@@ -472,9 +491,9 @@ screen_init(void)
   noecho();    
   /* set cursor invisible */     
   curs_set(0);     
-  /* return from getch() without blocking */
-  //  nodelay(stdscr, TRUE); 
+  /* enable extra keys */
   keypad(stdscr, TRUE);  
+  /* return from getch() without blocking */
   timeout(SCREEN_TIMEOUT);
 
   if( COLS<SCREEN_MIN_COLS || LINES<SCREEN_MIN_ROWS )
@@ -575,6 +594,9 @@ screen_init(void)
 
   mode_fn = get_screen_playlist();
 
+  /* initialize wreadln */
+  wrln_resize_callback = screen_resize;
+
   return 0;
 }
 
diff --git a/src/screen.h b/src/screen.h
index 75852d36e..cb4abc83d 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -53,6 +53,7 @@ typedef struct
   size_t buf_size;
 
   char *findbuf;
+  GList *find_history;
 
   int painted;
 
diff --git a/src/screen_utils.c b/src/screen_utils.c
index 080b118b1..b14b2a0ee 100644
--- a/src/screen_utils.c
+++ b/src/screen_utils.c
@@ -1,5 +1,7 @@
 /* 
- * (c) 2004 by Kalle Wallin (kaw@linux.se)
+ * $Id$
+ *
+ * (c) 2004 by Kalle Wallin <kaw@linux.se>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -31,6 +33,7 @@
 #include "options.h"
 #include "list_window.h"
 #include "colors.h"
+#include "wreadln.h"
 #include "screen.h"
 
 #define FIND_PROMPT  _("Find: ")
@@ -63,31 +66,28 @@ screen_getch(WINDOW *w, char *prompt)
   return key;
 }
 
-
 char *
-screen_getstr(WINDOW *w, char *prompt)
+screen_readln(WINDOW *w, 
+	      char *prompt, 
+	      char *value,
+	      GList **history,
+	      GCompletion *gcmp)
 {
-  char buf[256], *line = NULL;
-  int prompt_len = strlen(prompt);
+  char *line = NULL;
 
-  colors_use(w, COLOR_STATUS_ALERT);
-  wclear(w);  
-  wmove(w, 0, 0);
-  waddstr(w, prompt);
-  wmove(w, 0, prompt_len);
-  
-  echo();
+  wmove(w, 0,0);
   curs_set(1);
-
-  if( wgetnstr(w, buf, 256) == OK )
-    line = g_strdup(buf);
-
-  noecho();
+  line = wreadln(w, prompt, value, COLS, history, gcmp);
   curs_set(0);
-
   return line;
 }
 
+char *
+screen_getstr(WINDOW *w, char *prompt)
+{
+  return screen_readln(w, prompt, NULL, NULL, NULL);
+}
+
 
 /* query user for a string and find it in a list window */
 int 
@@ -121,7 +121,13 @@ screen_find(screen_t *screen,
     case CMD_LIST_FIND_NEXT:
     case CMD_LIST_RFIND_NEXT:
       if( !screen->findbuf )
-	screen->findbuf=screen_getstr(screen->status_window.w, prompt);
+	screen->findbuf=screen_readln(screen->status_window.w,
+				      prompt,
+				      (char *) -1, //NULL,
+				      &screen->find_history,
+				      NULL);
+      if( !screen->findbuf || !screen->findbuf[0] )
+	return 1; 
       if( reversed )
 	retval = list_window_rfind(lw, 
 				   callback_fn,
diff --git a/src/wreadln.c b/src/wreadln.c
new file mode 100644
index 000000000..6ac94b7dc
--- /dev/null
+++ b/src/wreadln.c
@@ -0,0 +1,339 @@
+/* 
+ * $Id$
+ *
+ * (c) 2004 by Kalle Wallin <kaw@linux.se>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <ncurses.h>
+#include <glib.h>
+
+#include "wreadln.h"
+
+#define KEY_CTRL_A   1
+#define KEY_CTRL_D   4 
+#define KEY_CTRL_E   5
+#define KEY_CTRL_G   7
+#define KEY_CTRL_K   11
+#define KEY_BCKSPC   8
+#define TAB          9
+
+#define WRLN_MAX_LINE_SIZE 1024
+#define WRLN_MAX_HISTORY_LENGTH 32
+ 
+unsigned int wrln_max_line_size = WRLN_MAX_LINE_SIZE;
+unsigned int wrln_max_history_length = WRLN_MAX_HISTORY_LENGTH;
+GVoidFunc wrln_resize_callback = NULL;
+
+
+char *
+wreadln(WINDOW *w, 
+	char *prompt, 
+	char *initial_value,
+	int x1, 
+	GList **history, 
+	GCompletion *gcmp)
+{
+  GList *hlist = NULL, *hcurrent = NULL;
+  char *line;
+  int x0, y, width;		
+  int cursor = 0, start = 0;		
+  int key = 0, i;
+
+  /* move the cursor one step to the right */
+  void cursor_move_right(void) {
+    if( cursor < strlen(line) && cursor<wrln_max_line_size-1 )
+      {
+	cursor++;
+	if( cursor+x0 >= x1 && start<cursor-width+1)
+	  start++;
+      }
+  }
+  /* move the cursor one step to the left */
+  void cursor_move_left(void) {
+    if( cursor > 0 )
+      {
+	if( cursor==start && start > 0 )
+	  start--;
+	cursor--;
+      }
+  }
+ /* move the cursor to the end of the line */
+  void cursor_move_to_eol(void) {
+    cursor = strlen(line);
+    if( cursor+x0 >= x1 )
+      start = cursor-width+1;
+  }
+  /* draw line buffer and update cursor position */
+  void drawline() {
+    wmove(w, y, x0);
+    /* clear input area */
+    whline(w, ' ', width);
+    /* print visible part of the line buffer */
+    waddnstr(w, line+start, width);
+    /* move the cursor to the correct position */
+    wmove(w, y, x0 + cursor-start);
+    /* tell ncurses to redraw the screen */
+    doupdate();
+  }
+
+
+  /* allocate a line buffer */
+  line = g_malloc0(wrln_max_line_size);
+  /* turn off echo */
+  noecho();		
+  /* make shure the cursor is visible */
+  curs_set(1);
+  /* print prompt string */
+  if( prompt )
+    waddstr(w, prompt);		
+  /* retrive y and x0 position */
+  getyx(w, y, x0);	
+  /* check the x1 value */
+  if( x1<=x0 || x1>COLS )
+    x1 = COLS;
+  width = x1-x0;
+  /* clear input area */
+  mvwhline(w, y, x0, ' ', width);	
+
+  if( history )
+    {
+      /* append the a new line to our history list */
+      *history = g_list_append(*history, g_malloc0(wrln_max_line_size));
+      /* hlist points to the current item in the history list */
+      hlist =  g_list_last(*history);
+      hcurrent = hlist;
+    }
+
+  if( initial_value == (char *) -1 )
+    {
+      /* get previous history entry */
+      if( history && hlist->prev )
+	{
+	  if( hlist==hcurrent )
+	    {
+	      /* save the current line */
+	      strncpy(hlist->data, line, wrln_max_line_size);
+	    }
+	  /* get previous line */
+	  hlist = hlist->prev;
+	  strncpy(line, hlist->data, wrln_max_line_size);
+	}
+      cursor_move_to_eol();
+      drawline();
+    }
+  else if( initial_value )
+    {
+      /* copy the initial value to the line buffer */
+      strncpy(line, initial_value, wrln_max_line_size);
+      cursor_move_to_eol();
+      drawline();
+    }  
+
+  while( key!=13 && key!='\n' )
+    {
+      key = wgetch(w);
+
+      /* check if key is a function key */
+      for(i=0; i<63; i++)
+	if( key==KEY_F(i) )
+	  {
+	    key=KEY_F(1);
+	    i=64;
+	  }
+
+      switch (key)
+	{
+	case ERR:
+	  /* ingnore errors */
+	  break;
+
+	case KEY_RESIZE:
+	  /* a resize event -> call an external callback function */
+	  if( wrln_resize_callback )
+	    wrln_resize_callback();
+	  if( x1>COLS )
+	    {
+	      x1=COLS;
+	      width = x1-x0;
+	      cursor_move_to_eol();
+	    }
+	  /* make shure the cursor is visible */
+	  curs_set(1);
+	  break;
+
+	case TAB:
+	  if( gcmp && strlen(line))
+	    {
+	      char *prefix = NULL;
+	      GList *list;
+	      
+	      list = g_completion_complete(gcmp, line, &prefix);
+	      if( prefix )
+		{
+		  int len = strlen(prefix);
+		  strncpy(line, prefix, len);
+		  cursor_move_to_eol();
+		  g_free(prefix);
+		}
+	      else
+		beep();
+	    }
+	  break;
+
+	case KEY_CTRL_G:
+	  beep();
+	  g_free(line);
+	  if( history )
+	    {
+	      g_free(hcurrent->data);
+	      hcurrent->data = NULL;
+	      *history = g_list_delete_link(*history, hcurrent);
+	    }
+	  return NULL;
+	  
+	case KEY_LEFT:
+	  cursor_move_left();
+	  break;
+	case KEY_RIGHT:	
+	  cursor_move_right();
+	  break;
+	case KEY_HOME:
+	case KEY_CTRL_A:
+	  cursor = 0;
+	  start = 0;
+	  break;
+	case KEY_END:
+	case KEY_CTRL_E:
+	  cursor_move_to_eol();
+	  break;
+	case KEY_CTRL_K:
+	  line[cursor] = 0;
+	  break;
+	case 127:
+	case KEY_BCKSPC:	/* handle backspace: copy all */
+	case KEY_BACKSPACE:	/* chars starting from curpos */
+	  if( cursor > 0 )	/* - 1 from buf[n+1] to buf   */
+	    {
+	      for (i = cursor - 1; line[i] != 0; i++)
+		line[i] = line[i + 1];
+	      cursor_move_left();
+	    }
+	  break;
+	case KEY_DC:		/* handle delete key. As above */
+	case KEY_CTRL_D:
+	  if( cursor <= strlen(line) - 1 ) 
+	    {
+	      for (i = cursor; line[i] != 0; i++)
+		line[i] = line[i + 1];
+	    }
+	  break;
+	case KEY_UP:		
+	  /* get previous history entry */
+	  if( history && hlist->prev )
+	    {
+	      if( hlist==hcurrent )
+		{
+		  /* save the current line */
+		  strncpy(hlist->data, line, wrln_max_line_size);
+		}
+	      /* get previous line */
+	      hlist = hlist->prev;
+	      strncpy(line, hlist->data, wrln_max_line_size);
+	    }
+	  //	  if (cursor > strlen(line))
+	  cursor_move_to_eol();
+	  break;
+	case KEY_DOWN:	
+	  /* get next history entry */
+	  if( history && hlist->next )
+	    {
+	      /* get next line */
+	      hlist = hlist->next;
+	      strncpy(line, hlist->data, wrln_max_line_size);
+	    }
+	  cursor_move_to_eol();
+	  break;
+	  
+	case '\n':
+	case 13:
+	case KEY_IC:
+	case KEY_PPAGE:
+	case KEY_NPAGE:
+	case KEY_F(1):
+	  /* ignore char */
+	  break;
+	default:	 
+	  if (key >= 32)
+	    {
+	      if (strlen (line + cursor))	/* if the cursor is */
+		{		                /* not at the last pos */
+		  char *tmp = 0;
+		  tmp = g_malloc0(strlen (line + cursor) + 1);
+		  strcpy (tmp, line + cursor);
+		  line[cursor] = key;
+		  line[cursor + 1] = 0;
+		  strcat (&line[cursor + 1], tmp);
+		  g_free(tmp);
+		  cursor_move_right();
+		}
+	      else
+		{
+		  line[cursor + 1] = 0;
+		  line[cursor] = key;
+		  cursor_move_right();
+		}
+	    }
+	}
+
+      drawline();
+    }
+
+  /* update history */
+  if( history )
+    {
+      if( strlen(line) )
+	{
+	  /* update the current history entry */
+	  size_t size = strlen(line)+1;
+	  hcurrent->data = g_realloc(hcurrent->data, size);
+	  strncpy(hcurrent->data, line, size);
+	}
+      else
+	{
+	  /* the line was empty - remove the current history entry */
+	  g_free(hcurrent->data);
+	  hcurrent->data = NULL;
+	  *history = g_list_delete_link(*history, hcurrent);
+	}
+
+      while( g_list_length(*history) > wrln_max_history_length )
+	{
+	  GList *first = g_list_first(*history);
+
+	  /* remove the oldest history entry  */
+	  g_free(first->data);
+	  first->data = NULL;
+	  *history = g_list_delete_link(*history, first);
+	}
+    }
+  
+  return g_realloc(line, strlen(line)+1);
+}
+ 
+
diff --git a/src/wreadln.h b/src/wreadln.h
new file mode 100644
index 000000000..270d73fb1
--- /dev/null
+++ b/src/wreadln.h
@@ -0,0 +1,25 @@
+#ifndef WREADLN_H
+#define WREADLN_H
+
+/* max size allocated for a line */
+extern unsigned int wrln_max_line_size;
+
+/* max items stored in the history list */
+extern unsigned int wrln_max_history_length;
+
+/* a callback function for KEY_RESIZE */
+extern GVoidFunc wrln_resize_callback;
+
+/* Note, wreadln calls curs_set() and noecho(), to enable cursor and 
+ * disable echo. wreadln will not restore these settings when exiting! */
+char *wreadln(WINDOW *w,           /* the curses window to use */
+	      char *prompt,        /* the prompt string or NULL */
+	      char *initial_value, /* initial value or NULL for a empty line
+				    * (char *) -1 => get value from history */
+	      int x1,              /* the maximum x position or 0 */
+	      GList **history,     /* a pointer to a history list or NULL */ 
+	      GCompletion *gcmp    /* a GCompletion structure or NULL */
+	      );
+
+
+#endif
-- 
cgit v1.2.3