diff --git a/gtk2_ardour/panner.cc b/gtk2_ardour/panner.cc
index 975f1e6..e1ce802 100644
--- a/gtk2_ardour/panner.cc
+++ b/gtk2_ardour/panner.cc
@@ -19,9 +19,12 @@
 
 #include <iostream>
 
+#include "ardour/panner.h"
 #include "panner.h"
+#include "i18n.h"
 
 using namespace std;
+using namespace Gtk;
 
 static const int triangle_size = 5;
 
@@ -34,7 +37,7 @@ null_label_callback (char* buf, unsigned int bufsize)
 }
 
 
-PannerBar::PannerBar (Gtk::Adjustment& adj, boost::shared_ptr<PBD::Controllable> c)
+PannerBar::PannerBar (Adjustment& adj, boost::shared_ptr<PBD::Controllable> c)
 	: BarController (adj, c, sigc::ptr_fun (null_label_callback))
 {
 	set_style (BarController::Line);
@@ -121,3 +124,66 @@ PannerBar::button_release (GdkEventButton* ev)
 	return BarController::button_release (ev);
 }
 
+bool
+PannerBar::entry_input (double *new_value)
+{
+	Entry* e = dynamic_cast<Entry*> (&spinner);
+	string const text = e->get_text ();
+
+	string digits;
+	string letters;
+
+	string const L = _("L");
+	string const C = _("C");
+	string const R = _("R");
+
+	for (string::size_type i = 0; i < text.length(); ++i) {
+		if (isdigit (text[i])) {
+			digits += text[i];
+		} else if (text[i] != '%') {
+			letters += text[i];
+		}
+	}
+
+	if (letters.empty()) {
+		/* no letter specified, so take any number as a percentage where
+		 * 0 is left and 100 right */
+		*new_value = digits.empty() ? 0.5 : (atoi (digits.c_str()) / 100.0);
+	} else {
+		/* letter given, so value is a percentage to the extreme
+		 * (e.g. 100L is full left, 1L is slightly left */
+		if (letters[0] == L[0] || letters[0] == tolower (L[0])) {
+			*new_value = digits.empty() ? 0 : (0.5 - atoi (digits.c_str()) / 200.0);
+		} else if (letters[0] == R[0] || letters[0] == tolower (R[0])) {
+			*new_value = digits.empty() ? 1 : 0.5 + atoi (digits.c_str()) / 200.0;
+		} else if (letters[0] == C[0] || letters[0] == tolower (C[0])) {
+			*new_value = 0.5;
+		}
+	}
+
+	return true;
+}
+
+bool
+PannerBar::entry_output ()
+{
+	Entry* e = dynamic_cast<Entry*> (&spinner);
+	double const v = spinner.get_adjustment()->get_value();
+	if (ARDOUR::Panner::equivalent (v, 0.5)) {
+		e->set_text (_("C"));
+	} else if (ARDOUR::Panner::equivalent (v, 0)) {
+		e->set_text (_("L"));
+	} else if (ARDOUR::Panner::equivalent (v, 1)) {
+		e->set_text (_("R"));
+	} else if (v < 0.5) {
+		std::stringstream s;
+		s << _("L") << ((0.5 - v) * 200) << "%";
+		e->set_text (s.str ());
+	} else if (v > 0.5) {
+		std::stringstream s;
+		s << _("R") << ((v -0.5) * 200) << "%";
+		e->set_text (s.str ());
+	}
+
+	return true;
+}
diff --git a/gtk2_ardour/panner.h b/gtk2_ardour/panner.h
index 21f984a..2103966 100644
--- a/gtk2_ardour/panner.h
+++ b/gtk2_ardour/panner.h
@@ -33,6 +33,8 @@ class PannerBar : public Gtkmm2ext::BarController
 	bool expose (GdkEventExpose*);
 	bool button_press (GdkEventButton*);
 	bool button_release (GdkEventButton*);
+	bool entry_input (double *);
+	bool entry_output ();
 };
 
 #endif /* __gtk_ardour_panner_h__ */
diff --git a/gtk2_ardour/panner_ui.cc b/gtk2_ardour/panner_ui.cc
index 3ffec0a..208ff6e 100644
--- a/gtk2_ardour/panner_ui.cc
+++ b/gtk2_ardour/panner_ui.cc
@@ -697,23 +697,6 @@ PannerUI::update_pan_bars (bool only_if_aplay)
 }
 
 void
-PannerUI::pan_printer (char *buf, uint32_t len, Adjustment* adj)
-{
-	float val = adj->get_value();
-
-	if (val == 0.0f) {
-		snprintf (buf, len, X_("L"));
-	} else if (val == 1.0f) {
-		snprintf (buf, len, X_("R"));
-	} else if (Panner::equivalent (val, 0.5f)) {
-		snprintf (buf, len, X_("C"));
-	} else {
-		/* don't print anything */
-		buf[0] = '\0';
-	}
-}
-
-void
 PannerUI::update_pan_sensitive () 
 {
 	bool sensitive = !(_io->panner()->automation_state() & Play);
diff --git a/gtk2_ardour/panner_ui.h b/gtk2_ardour/panner_ui.h
index fa0b7ce..602164d 100644
--- a/gtk2_ardour/panner_ui.h
+++ b/gtk2_ardour/panner_ui.h
@@ -119,7 +119,6 @@ class PannerUI : public Gtk::HBox
 
 	void pan_adjustment_changed (uint32_t which);
 	void pan_value_changed (uint32_t which);
-	void pan_printer (char* buf, uint32_t, Gtk::Adjustment*);
 	void update_pan_bars (bool only_if_aplay);
 	void update_pan_linkage ();
 	void update_pan_state ();
diff --git a/libs/gtkmm2ext/barcontroller.cc b/libs/gtkmm2ext/barcontroller.cc
index 90954e5..53414d8 100644
--- a/libs/gtkmm2ext/barcontroller.cc
+++ b/libs/gtkmm2ext/barcontroller.cc
@@ -76,6 +76,8 @@ BarController::BarController (Gtk::Adjustment& adj,
 
 	spinner.signal_activate().connect (mem_fun (*this, &BarController::entry_activated));
 	spinner.signal_focus_out_event().connect (mem_fun (*this, &BarController::entry_focus_out));
+	spinner.signal_input().connect (mem_fun (*this, &BarController::entry_input));
+	spinner.signal_output().connect (mem_fun (*this, &BarController::entry_output));
 	spinner.set_digits (3);
 
 	add (darea);
@@ -467,3 +469,17 @@ BarController::set_sensitive (bool yn)
 	Frame::set_sensitive (yn);
 	darea.set_sensitive (yn);
 }
+
+bool
+BarController::entry_input (double* v)
+{
+	return false;
+}
+
+bool
+BarController::entry_output ()
+{
+	return false;
+}
+
+	
diff --git a/libs/gtkmm2ext/gtkmm2ext/barcontroller.h b/libs/gtkmm2ext/gtkmm2ext/barcontroller.h
index 2c0ca94..a01007f 100644
--- a/libs/gtkmm2ext/gtkmm2ext/barcontroller.h
+++ b/libs/gtkmm2ext/gtkmm2ext/barcontroller.h
@@ -55,8 +55,6 @@ class BarController : public Gtk::Frame
 
 	void set_sensitive (bool yn);
 
-	Gtk::SpinButton& get_spin_button() { return spinner; }
-
 	sigc::signal<void> StartGesture;
 	sigc::signal<void> StopGesture;
 
@@ -90,6 +88,8 @@ class BarController : public Gtk::Frame
 	virtual bool expose (GdkEventExpose *);
 	virtual bool scroll (GdkEventScroll *);
 	virtual bool entry_focus_out (GdkEventFocus*);
+	virtual bool entry_input (double *);
+	virtual bool entry_output ();
 
 	gint mouse_control (double x, GdkWindow* w, double scaling);
 
