Prep experimental build for streamers.
Prep experimental build for streamers.

--- a/Documents/CHANGES.txt
+++ b/Documents/CHANGES.txt
@@ -1,6 +1,19 @@
-1.0.18.1
+1.0.19.1, 09-11-15
+    Added: Key binding editor accessible under 'Settings' on the Build Engineer.
+    Added: Added current vessel name readout. (antplant)
+    Added: 'Relative Radial Velocity' and 'Time To Rendezvous' readouts. (itwtx)
+    Added: Readout help strings. (harryyoung)
     Changed: The 'Torque' value in the editor is now precise to two decimal places.
-    
+    Changed: Time formatting reference (Kerbin/Earth) is now based on the in-game setting.
+    Changed: Eccentric Anomaly, Mean Anomaly and Mean Anomaly At Epoc now display in degrees rather than radians.
+    Fixed: Optimised time formatting. (itwtx)
+    Fixed: TimeToAtmosphere checks that the Apoapsis is outside atmosphere. (Kerbas-ad-astra)
+    Fixed: Issue with stage priority flow. Caused Rapier calculations to fail if LF and O are drawn from different tanks. (Padishar)
+    Fixed: Issue with angle to prograde/retrograde calculations on highly inclined orbits.
+    Fixed: Editor input locks not being reset when a scene change is forced (e.g. via Kerbal Construction Time).
+    Fixed: Roll axis readout now shows the correct sign.
+    Removed: Time Formatter readout as it's not required anymore.
+
 1.0.18.0
     Added: Orbital readouts - "Speed at Periapsis" and "Speed at Apoapsis". (Padishar)
     Added: Manoeuvre readouts - "Post-burn Apoapsis" and "Post-burn Periapsis". (Padishar)

--- a/KerbalEngineer/Editor/BuildAdvanced.cs
+++ b/KerbalEngineer/Editor/BuildAdvanced.cs
@@ -1,7 +1,5 @@
 // 
-//     Kerbal Engineer Redux
-// 
-//     Copyright (C) 2014 CYBUTEK
+//     Copyright (C) 2015 CYBUTEK
 // 
 //     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
@@ -19,23 +17,26 @@
 
 namespace KerbalEngineer.Editor
 {
-    #region Using Directives
     using System;
     using Extensions;
     using Flight;
     using Helpers;
+    using KeyBinding;
     using Settings;
     using UIControls;
     using UnityEngine;
     using VesselSimulator;
 
-    #endregion
-
     [KSPAddon(KSPAddon.Startup.EditorAny, false)]
     public class BuildAdvanced : MonoBehaviour
     {
-        #region Fields
-        public static float Altitude = 0.0f;
+        public static float Altitude;
+
+        private static Rect compactModeRect = new Rect(0.0f, 5.0f, 0.0f, 20.0f);
+        private static Stage stage;
+        private static int stagesCount;
+        private static int stagesLength;
+        private static string title;
 
         private GUIStyle areaSettingStyle;
         private GUIStyle areaStyle;
@@ -64,9 +65,7 @@
         private GUIStyle titleStyle;
         private bool visible = true;
         private GUIStyle windowStyle;
-        #endregion
-
-        #region Properties
+
         /// <summary>
         ///     Gets the current instance if started or returns null.
         /// </summary>
@@ -146,14 +145,6 @@
                 visible = value;
             }
         }
-        #endregion
-
-        #region Methods
-        private static Rect compactModeRect = new Rect(0.0f, 5.0f, 0.0f, 20.0f);
-        private static Stage stage;
-        private static int stagesCount;
-        private static int stagesLength;
-        private static string title;
 
         protected void Awake()
         {
@@ -179,6 +170,8 @@
         /// </summary>
         protected void OnDestroy()
         {
+            Logger.Log("BuildAdvanced->OnDestroy");
+
             try
             {
                 SettingHandler handler = new SettingHandler();
@@ -198,6 +191,8 @@
             {
                 Logger.Exception(ex, "BuildAdvanced.OnDestroy()");
             }
+
+            EditorLock(false);
         }
 
         protected void OnGUI()
@@ -552,6 +547,14 @@
             GUILayout.EndHorizontal();
 
             GUILayout.BeginHorizontal();
+            GUILayout.Label("Key Bindings:", settingStyle);
+            if (GUILayout.Button("EDIT KEY BINDINGS", buttonStyle, GUILayout.Width(200.0f * GuiDisplaySize.Offset)))
+            {
+                KeyBinder.Show();
+            }
+            GUILayout.EndHorizontal();
+
+            GUILayout.BeginHorizontal();
             GUILayout.Label("GUI Size: " + GuiDisplaySize.Increment, settingStyle);
             if (GUILayout.Button("<", buttonStyle, GUILayout.Width(100.0f * GuiDisplaySize.Offset)))
             {
@@ -566,6 +569,7 @@
             GUILayout.Label("Minimum delay between simulations: " + SimManager.minSimTime.TotalMilliseconds + "ms", settingStyle);
             GUI.skin = HighLogic.Skin;
             SimManager.minSimTime = TimeSpan.FromMilliseconds(GUILayout.HorizontalSlider((float)SimManager.minSimTime.TotalMilliseconds, 0, 2000.0f));
+
             GUI.skin = null;
         }
 
@@ -706,7 +710,7 @@
                 fontSize = (int)(11 * GuiDisplaySize.Offset),
                 fontStyle = FontStyle.Bold,
                 alignment = TextAnchor.MiddleCenter,
-                stretchWidth = true,
+                stretchWidth = true
             };
 
             infoStyle = new GUIStyle(HighLogic.Skin.label)
@@ -884,6 +888,5 @@
                 Logger.Exception(ex, "BuildAdvanced.Window()");
             }
         }
-        #endregion
     }
 }

--- a/KerbalEngineer/EngineerGlobals.cs
+++ b/KerbalEngineer/EngineerGlobals.cs
@@ -25,11 +25,12 @@
         /// <summary>
         ///     Current version of the Kerbal Engineer assembly.
         /// </summary>
-        public const string ASSEMBLY_VERSION = "1.0.18.1";
+        public const string ASSEMBLY_VERSION = "1.0.19.1";
 
         private static string assemblyFile;
         private static string assemblyName;
         private static string assemblyPath;
+        private static string settingsPath;
 
         /// <summary>
         ///     Gets the Kerbal Engineer assembly's path including the file name.
@@ -63,5 +64,20 @@
                 return assemblyPath ?? (assemblyPath = AssemblyFile.Replace(new FileInfo(AssemblyFile).Name, ""));
             }
         }
+
+        /// <summary>
+        ///     Gets the settings directory path.
+        /// </summary>
+        public static string SettingsPath
+        {
+            get
+            {
+                if (string.IsNullOrEmpty(settingsPath))
+                {
+                    settingsPath = Path.Combine(AssemblyPath, "Settings");
+                }
+                return settingsPath;
+            }
+        }
     }
 }

--- a/KerbalEngineer/Extensions/OrbitExtensions.cs
+++ b/KerbalEngineer/Extensions/OrbitExtensions.cs
@@ -61,12 +61,15 @@
                 return 0.0;
             }
 
-            var angle = AngleHelper.GetAngleBetweenVectors(orbit.getRelativePositionAtUT(universalTime),
-                                                           Vector3d.Exclude(orbit.GetOrbitNormal(), orbit.referenceBody.orbit.getRelativePositionAtUT(universalTime)));
+            Vector3d orbitVector = orbit.getRelativePositionAtUT(universalTime);
+            orbitVector.z = 0.0;
 
-            angle = AngleHelper.Clamp360(angle - 90.0);
+            Vector3d bodyVector = orbit.referenceBody.orbit.getOrbitalVelocityAtUT(universalTime);
+            bodyVector.z = 0.0;
 
-            return orbit.inclination > 90.0 ? angle : 360.0 - angle;
+            double angle = AngleHelper.GetAngleBetweenVectors(bodyVector, orbitVector);
+
+            return AngleHelper.Clamp360(orbit.inclination < 90.0 ? angle : 360.0 - angle);
         }
 
         public static double GetAngleToRetrograde(this Orbit orbit)
@@ -81,12 +84,15 @@
                 return 0.0;
             }
 
-            var angle = AngleHelper.GetAngleBetweenVectors(orbit.getRelativePositionAtUT(universalTime),
-                                                           Vector3d.Exclude(orbit.GetOrbitNormal(), orbit.referenceBody.orbit.getRelativePositionAtUT(universalTime)));
+            Vector3d orbitVector = orbit.getRelativePositionAtUT(universalTime);
+            orbitVector.z = 0.0;
 
-            angle = AngleHelper.Clamp360(angle + 90.0);
+            Vector3d bodyVector = orbit.referenceBody.orbit.getOrbitalVelocityAtUT(universalTime);
+            bodyVector.z = 0.0;
 
-            return orbit.inclination > 90.0 ? angle : 360.0 - angle;
+            double angle = AngleHelper.GetAngleBetweenVectors(-bodyVector, orbitVector);
+
+            return AngleHelper.Clamp360(orbit.inclination < 90.0 ? angle : 360.0 - angle);
         }
 
         public static double GetAngleToTrueAnomaly(this Orbit orbit, double trueAnomaly)

--- a/KerbalEngineer/Flight/DisplayStack.cs
+++ b/KerbalEngineer/Flight/DisplayStack.cs
@@ -33,6 +33,7 @@
 
 namespace KerbalEngineer.Flight
 {
+    using KeyBinding;
     using Upgradeables;
 
     /// <summary>

--- a/KerbalEngineer/Flight/Readouts/Miscellaneous/TimeReference.cs
+++ /dev/null
@@ -1,66 +1,1 @@
-// 
-//     Kerbal Engineer Redux
-// 
-//     Copyright (C) 2014 CYBUTEK
-// 
-//     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 3 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, see <http://www.gnu.org/licenses/>.
-// 
 
-#region Using Directives
-
-using System;
-
-using KerbalEngineer.Flight.Sections;
-using KerbalEngineer.Helpers;
-
-using UnityEngine;
-
-#endregion
-
-namespace KerbalEngineer.Flight.Readouts.Miscellaneous
-{
-    public class TimeReference : ReadoutModule
-    {
-        #region Constructors
-
-        public TimeReference()
-        {
-            this.Name = "Time Reference Adjuster";
-            this.Category = ReadoutCategory.GetCategory("Miscellaneous");
-            this.HelpString = "Shows a control that will allow you to select if days and years are calculated in Kerbin Time (6 hours/day) or Earth Time (24 hours/day)";
-            this.IsDefault = false;
-        }
-
-        #endregion
-
-        #region Methods: public
-
-        public override void Draw(SectionModule section)
-        {
-            GUILayout.BeginHorizontal();
-            GUILayout.Label("Time Ref.: " + TimeFormatter.Reference, this.NameStyle);
-            if (GUILayout.Button("Earth", this.ButtonStyle))
-            {
-                TimeFormatter.SetReference();
-            }
-            if (GUILayout.Button("Kerbin", this.ButtonStyle))
-            {
-                TimeFormatter.SetReference(PSystemManager.Instance.localBodies.Find(body => body.bodyName.Equals("Kerbin")));
-            }
-            GUILayout.EndHorizontal();
-        }
-
-        #endregion
-    }
-}

--- a/KerbalEngineer/Flight/Readouts/Orbital/EccentricAnomaly.cs
+++ b/KerbalEngineer/Flight/Readouts/Orbital/EccentricAnomaly.cs
@@ -1,7 +1,7 @@
 // 
 //     Kerbal Engineer Redux
 // 
-//     Copyright (C) 2014 CYBUTEK
+//     Copyright (C) 2015 CYBUTEK
 // 
 //     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
@@ -17,38 +17,25 @@
 //     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 // 
 
-#region Using Directives
-
-using System;
-
-using KerbalEngineer.Extensions;
-using KerbalEngineer.Flight.Sections;
-
-#endregion
-
 namespace KerbalEngineer.Flight.Readouts.Orbital
 {
+    using Extensions;
+    using Helpers;
+    using Sections;
+
     public class EccentricAnomaly : ReadoutModule
     {
-        #region Constructors
-
         public EccentricAnomaly()
         {
-            this.Name = "Eccentric Anomaly";
-            this.Category = ReadoutCategory.GetCategory("Orbital");
-            this.HelpString = String.Empty;
-            this.IsDefault = false;
+            Name = "Eccentric Anomaly";
+            Category = ReadoutCategory.GetCategory("Orbital");
+            HelpString = string.Empty;
+            IsDefault = false;
         }
-
-        #endregion
-
-        #region Methods: public
 
         public override void Draw(SectionModule section)
         {
-            this.DrawLine(FlightGlobals.ship_orbit.eccentricAnomaly.ToAngle(), section.IsHud);
+            DrawLine((FlightGlobals.ship_orbit.eccentricAnomaly * Units.RAD_TO_DEG).ToAngle(), section.IsHud);
         }
-
-        #endregion
     }
 }

--- a/KerbalEngineer/Flight/Readouts/Orbital/MeanAnomaly.cs
+++ b/KerbalEngineer/Flight/Readouts/Orbital/MeanAnomaly.cs
@@ -1,7 +1,7 @@
 // 
 //     Kerbal Engineer Redux
 // 
-//     Copyright (C) 2014 CYBUTEK
+//     Copyright (C) 2015 CYBUTEK
 // 
 //     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
@@ -17,38 +17,25 @@
 //     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 // 
 
-#region Using Directives
-
-using System;
-
-using KerbalEngineer.Extensions;
-using KerbalEngineer.Flight.Sections;
-
-#endregion
-
 namespace KerbalEngineer.Flight.Readouts.Orbital
 {
+    using Extensions;
+    using Helpers;
+    using Sections;
+
     public class MeanAnomaly : ReadoutModule
     {
-        #region Constructors
-
         public MeanAnomaly()
         {
-            this.Name = "Mean Anomaly";
-            this.Category = ReadoutCategory.GetCategory("Orbital");
-            this.HelpString = String.Empty;
-            this.IsDefault = false;
+            Name = "Mean Anomaly";
+            Category = ReadoutCategory.GetCategory("Orbital");
+            HelpString = string.Empty;
+            IsDefault = false;
         }
-
-        #endregion
-
-        #region Methods: public
 
         public override void Draw(SectionModule section)
         {
-            this.DrawLine(FlightGlobals.ship_orbit.meanAnomaly.ToAngle(), section.IsHud);
+            DrawLine((FlightGlobals.ship_orbit.meanAnomaly * Units.RAD_TO_DEG).ToAngle(), section.IsHud);
         }
-
-        #endregion
     }
 }

--- a/KerbalEngineer/Flight/Readouts/Orbital/MeanAnomalyAtEpoc.cs
+++ b/KerbalEngineer/Flight/Readouts/Orbital/MeanAnomalyAtEpoc.cs
@@ -1,7 +1,7 @@
 // 
 //     Kerbal Engineer Redux
 // 
-//     Copyright (C) 2014 CYBUTEK
+//     Copyright (C) 2015 CYBUTEK
 // 
 //     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
@@ -17,38 +17,25 @@
 //     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 // 
 
-#region Using Directives
-
-using System;
-
-using KerbalEngineer.Extensions;
-using KerbalEngineer.Flight.Sections;
-
-#endregion
-
 namespace KerbalEngineer.Flight.Readouts.Orbital
 {
+    using Extensions;
+    using Helpers;
+    using Sections;
+
     public class MeanAnomalyAtEpoc : ReadoutModule
     {
-        #region Constructors
-
         public MeanAnomalyAtEpoc()
         {
-            this.Name = "Mean Anomaly at Epoc";
-            this.Category = ReadoutCategory.GetCategory("Orbital");
-            this.HelpString = String.Empty;
-            this.IsDefault = false;
+            Name = "Mean Anomaly at Epoc";
+            Category = ReadoutCategory.GetCategory("Orbital");
+            HelpString = string.Empty;
+            IsDefault = false;
         }
-
-        #endregion
-
-        #region Methods: public
 
         public override void Draw(SectionModule section)
         {
-            this.DrawLine(FlightGlobals.ship_orbit.meanAnomalyAtEpoch.ToAngle(), section.IsHud);
+            DrawLine((FlightGlobals.ship_orbit.meanAnomalyAtEpoch * Units.RAD_TO_DEG).ToAngle(), section.IsHud);
         }
-
-        #endregion
     }
 }

--- a/KerbalEngineer/Flight/Readouts/ReadoutLibrary.cs
+++ b/KerbalEngineer/Flight/Readouts/ReadoutLibrary.cs
@@ -190,7 +190,6 @@
                 readouts.Add(new Separator());
                 readouts.Add(new GuiSizeAdjustor());
                 readouts.Add(new SimulationDelay());
-                readouts.Add(new TimeReference());
                 readouts.Add(new VectoredThrustToggle());
                 readouts.Add(new SystemTime());
 

--- a/KerbalEngineer/Flight/Readouts/Vessel/AttitudeProcessor.cs
+++ b/KerbalEngineer/Flight/Readouts/Vessel/AttitudeProcessor.cs
@@ -110,8 +110,8 @@
                 ? 360.0f - this.surfaceRotation.eulerAngles.x
                 : -this.surfaceRotation.eulerAngles.x;
             this.roll = this.surfaceRotation.eulerAngles.z > 180.0f
-                ? this.surfaceRotation.eulerAngles.z - 360.0f
-                : this.surfaceRotation.eulerAngles.z;
+                ? 360.0f - this.surfaceRotation.eulerAngles.z
+                : -this.surfaceRotation.eulerAngles.z;
 
             this.headingRate = this.heading - this.previousHeading;
             this.pitchRate = this.pitch - this.previousPitch;

--- a/KerbalEngineer/Helpers/TimeFormatter.cs
+++ b/KerbalEngineer/Helpers/TimeFormatter.cs
@@ -1,7 +1,5 @@
 // 
-//     Kerbal Engineer Redux
-// 
-//     Copyright (C) 2014 CYBUTEK
+//     Copyright (C) 2015 CYBUTEK
 // 
 //     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
@@ -17,139 +15,46 @@
 //     along with this program.  If not, see <http://www.gnu.org/licenses/>.
 // 
 
-#region Using Directives
-
-using System;
-
-using KerbalEngineer.Settings;
-
-#endregion
-
 namespace KerbalEngineer.Helpers
 {
     public static class TimeFormatter
     {
-        #region Constructors
-
-        static TimeFormatter()
-        {
-            SetReference(false);
-            Load();
-        }
-
-        #endregion
-
-        #region Properties
-
-        public static string Reference { get; set; }
-
-        public static double SecondsPerDay { get; set; }
-
-        public static double SecondsPerHour { get; set; }
-
-        public static double SecondsPerMinute { get; set; }
-
-        public static double SecondsPerYear { get; set; }
-
-        #endregion
-
-        #region Methods: public
-
         public static string ConvertToString(double seconds, string format = "F1")
         {
-            var years = 0;
-            var days = 0;
-            var hours = 0;
-            var minutes = 0;
+            int years = 0;
+            int days = 0;
+            int hours = 0;
+            int minutes = 0;
 
-            if (seconds > 0)
+            if (seconds > 0.0)
             {
-                years = (int)(seconds / SecondsPerYear);
-                seconds -= years * SecondsPerYear;
+                years = (int)(seconds / KSPUtil.Year);
+                seconds -= years * KSPUtil.Year;
 
-                days = (int)(seconds / SecondsPerDay);
-                seconds -= days * SecondsPerDay;
+                days = (int)(seconds / KSPUtil.Day);
+                seconds -= days * KSPUtil.Day;
 
-                hours = (int)(seconds / SecondsPerHour);
-                seconds -= hours * SecondsPerHour;
+                hours = (int)(seconds / 3600.0);
+                seconds -= hours * 3600.0;
 
-                minutes = (int)(seconds / SecondsPerMinute);
-                seconds -= minutes * SecondsPerMinute;
+                minutes = (int)(seconds / 60.0);
+                seconds -= minutes * 60.0;
             }
 
             if (years > 0)
             {
-                return String.Format("{0}y {1}d {2}h {3}m {4}s", years, days, hours, minutes, seconds.ToString(format));
+                return string.Format("{0}y {1}d {2}h {3}m {4}s", years, days, hours, minutes, seconds.ToString(format));
             }
             if (days > 0)
             {
-                return String.Format("{0}d {1}h {2}m {3}s", days, hours, minutes, seconds.ToString(format));
+                return string.Format("{0}d {1}h {2}m {3}s", days, hours, minutes, seconds.ToString(format));
             }
             if (hours > 0)
             {
-                return String.Format("{0}h {1}m {2}s", hours, minutes, seconds.ToString(format));
+                return string.Format("{0}h {1}m {2}s", hours, minutes, seconds.ToString(format));
             }
 
-            return minutes > 0 ? String.Format("{0}m {1}s", minutes, seconds.ToString(format)) : String.Format("{0}s", seconds.ToString(format));
+            return minutes > 0 ? string.Format("{0}m {1}s", minutes, seconds.ToString(format)) : string.Format("{0}s", seconds.ToString(format));
         }
-
-        public static void Load()
-        {
-            var handler = SettingHandler.Load("TimeFormatter.xml");
-            SecondsPerMinute = handler.Get("SecondsPerMinute", SecondsPerMinute);
-            SecondsPerHour = handler.Get("SecondsPerHour", SecondsPerHour);
-            SecondsPerDay = handler.Get("SecondsPerDay", SecondsPerDay);
-            SecondsPerYear = handler.Get("SecondsPerYear", SecondsPerYear);
-            Reference = handler.Get("Reference", Reference);
-        }
-
-        public static void Save()
-        {
-            var handler = SettingHandler.Load("TimeFormatter.xml");
-            handler.Set("SecondsPerMinute", SecondsPerMinute);
-            handler.Set("SecondsPerHour", SecondsPerHour);
-            handler.Set("SecondsPerDay", SecondsPerDay);
-            handler.Set("SecondsPerYear", SecondsPerYear);
-            handler.Set("Reference", Reference);
-            handler.Save("TimeFormatter.xml");
-        }
-
-        public static void SetReference(bool save = true)
-        {
-            const double minute = 60.0;
-            const double hour = minute * 60.0;
-            const double day = hour * 24.0;
-            const double year = day * 365.0;
-            SetReference(minute, hour, day, year, "Earth", save);
-        }
-
-        public static void SetReference(CelestialBody body, bool save = true)
-        {
-            SetReference(SecondsPerMinute, SecondsPerHour, body.rotationPeriod, body.orbit.period, body.bodyName, save);
-        }
-
-        public static void SetReference(double minute, double hour, double day, double year, string reference, bool save = true)
-        {
-            SecondsPerMinute = minute;
-            SecondsPerHour = hour;
-            SecondsPerDay = day;
-            SecondsPerYear = year;
-            Reference = reference;
-
-            if (save)
-            {
-                Save();
-            }
-        }
-
-        public new static string ToString()
-        {
-            return String.Format("SecondsPerMinute: {0}", SecondsPerMinute) + Environment.NewLine +
-                   String.Format("SecondsPerHour: {0}", SecondsPerHour) + Environment.NewLine +
-                   String.Format("SecondsPerDay: {0}", SecondsPerDay) + Environment.NewLine +
-                   String.Format("SecondsPerYear: {0}", SecondsPerYear) + Environment.NewLine;
-        }
-
-        #endregion
     }
 }

--- a/KerbalEngineer/Helpers/Units.cs
+++ b/KerbalEngineer/Helpers/Units.cs
@@ -24,6 +24,8 @@
     public static class Units
     {
         public const double GRAVITY = 9.80665;
+        public const double RAD_TO_DEG = 180.0 / Math.PI;
+        public const double DEG_TO_RAD = Math.PI / 180.0;
 
         public static string Concat(int value1, int value2)
         {
@@ -91,7 +93,7 @@
             int min = (int)Math.Floor(rem * 60);
             rem -= ((double)min / 60);
             int sec = (int)Math.Floor(rem * 3600);
-            return String.Format("{0:0}° {1:00}' {2:00}\"", deg, min, sec);
+            return string.Format("{0:0}° {1:00}' {2:00}\"", deg, min, sec);
         }
 
         public static string ToDistance(double value, int decimals = 1)
@@ -130,13 +132,13 @@
 
         public static string ToForce(double value)
         {
-            return value.ToString((value < 100000.0) ? (value < 10000.0) ? (value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0") + "kN";
+            return value.ToString((value < 100000.0) ? (value < 10000.0) ? (value < 100.0) ? (Math.Abs(value) < double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0") + "kN";
         }
 
         public static string ToForce(double value1, double value2)
         {
-            string format1 = (value1 < 100000.0) ? (value1 < 10000.0) ? (value1 < 100.0) ? (Math.Abs(value1) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0";
-            string format2 = (value2 < 100000.0) ? (value2 < 10000.0) ? (value2 < 100.0) ? (Math.Abs(value2) < Double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0";
+            string format1 = (value1 < 100000.0) ? (value1 < 10000.0) ? (value1 < 100.0) ? (Math.Abs(value1) < double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0";
+            string format2 = (value2 < 100000.0) ? (value2 < 10000.0) ? (value2 < 100.0) ? (Math.Abs(value2) < double.Epsilon) ? "N0" : "N3" : "N2" : "N1" : "N0";
             return value1.ToString(format1) + " / " + value2.ToString(format2) + "kN";
         }
 
@@ -205,7 +207,7 @@
 
         public static string ToTorque(double value)
         {
-            return value.ToString((value < 100.0) ? (Math.Abs(value) < Double.Epsilon) ? "N0" : "N2" : "N0") + "kNm";
+            return value.ToString((value < 100.0) ? (Math.Abs(value) < double.Epsilon) ? "N0" : "N2" : "N0") + "kNm";
         }
     }
 }

--- /dev/null
+++ b/KerbalEngineer/Helpers/XmlHelper.cs
@@ -1,1 +1,67 @@
+namespace KerbalEngineer.Helpers
+{
+    using System;
+    using System.IO;
+    using System.Text;
+    using System.Xml.Serialization;
 
+    public static class XmlHelper
+    {
+        /// <summary>
+        ///     Loads an object from disk.
+        /// </summary>
+        public static T LoadObject<T>(string path)
+        {
+            T obj = default(T);
+
+            if (File.Exists(path))
+            {
+                try
+                {
+                    using (StreamReader stream = new StreamReader(path, Encoding.UTF8))
+                    {
+                        obj = (T)new XmlSerializer(typeof(T)).Deserialize(stream);
+                    }
+                }
+                catch (Exception ex)
+                {
+                    Logger.Exception(ex);
+                }
+            }
+
+            return obj;
+        }
+
+        /// <summary>
+        ///     Loads and object from disk.
+        /// </summary>
+        public static bool LoadObject<T>(string path, out T obj)
+        {
+            obj = LoadObject<T>(path);
+            return (obj != null);
+        }
+
+        /// <summary>
+        ///     Saves an object to disk.
+        /// </summary>
+        public static void SaveObject<T>(string path, T obj)
+        {
+            if (obj == null || string.IsNullOrEmpty(path))
+            {
+                return;
+            }
+
+            try
+            {
+                using (StreamWriter stream = new StreamWriter(path, false, Encoding.UTF8))
+                {
+                    new XmlSerializer(typeof(T)).Serialize(stream, obj);
+                }
+            }
+            catch (Exception ex)
+            {
+                Logger.Exception(ex);
+            }
+        }
+    }
+}

--- a/KerbalEngineer/KerbalEngineer.csproj
+++ b/KerbalEngineer/KerbalEngineer.csproj
@@ -53,7 +53,6 @@
     <Compile Include="Flight\Presets\Preset.cs" />
     <Compile Include="Flight\Readouts\Miscellaneous\SystemTime.cs" />
     <Compile Include="Flight\Readouts\Miscellaneous\VectoredThrustToggle.cs" />
-    <Compile Include="Flight\Readouts\Miscellaneous\TimeReference.cs" />
     <Compile Include="Flight\Readouts\Miscellaneous\Separator.cs" />
     <Compile Include="Flight\Readouts\Miscellaneous\GuiSizeAdjustor.cs" />
     <Compile Include="Flight\Readouts\Orbital\AngleToEquatorialDescendingNode.cs" />
@@ -149,8 +148,10 @@
     <Compile Include="Helpers\TextureHelper.cs" />
     <Compile Include="Helpers\Units.cs" />
     <Compile Include="Helpers\TimeFormatter.cs" />
-    <Compile Include="KeyBinder.cs" />
+    <Compile Include="Helpers\XmlHelper.cs" />
+    <Compile Include="KeyBinding\KeyBinder.cs" />
     <Compile Include="Control\ControlCentre.cs" />
+    <Compile Include="KeyBinding\KeyBindingsObject.cs" />
     <Compile Include="UIControls\DropDown.cs" />
     <Compile Include="Logger.cs" />
     <Compile Include="EngineerGlobals.cs" />
@@ -222,6 +223,7 @@
     <Compile Include="Settings\SettingHandler.cs" />
     <Compile Include="Settings\SettingItem.cs" />
     <Compile Include="TapeDriveAnimator.cs" />
+    <Compile Include="KeyBinding\KeyBindPopup.cs" />
     <Compile Include="UIControls\WindowObject.cs" />
     <Compile Include="VesselSimulator\AttachNodeSim.cs" />
     <Compile Include="VesselSimulator\EngineSim.cs" />

--- a/KerbalEngineer/KeyBinder.cs
+++ /dev/null
@@ -1,35 +1,1 @@
-// 
-//     Kerbal Engineer Redux
-// 
-//     Copyright (C) 2014 CYBUTEK
-// 
-//     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 3 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, see <http://www.gnu.org/licenses/>.
-// 
 
-using UnityEngine;
-
-namespace KerbalEngineer
-{
-    public class KeyBinder
-    {
-        public static KeyCode EditorShowHide { get; set; }
-
-        public static KeyCode FlightShowHide { get; set; }
-
-        static KeyBinder()
-        {
-            EditorShowHide = FlightShowHide = KeyCode.Backslash;
-        }
-    }
-}

--- /dev/null
+++ b/KerbalEngineer/KeyBinding/KeyBindPopup.cs
@@ -1,1 +1,243 @@
-
+// 
+//     Kerbal Engineer Redux
+// 
+//     Copyright (C) 2015 CYBUTEK
+// 
+//     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 3 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, see <http://www.gnu.org/licenses/>.
+// 
+
+namespace KerbalEngineer.KeyBinding
+{
+    using System;
+    using Extensions;
+    using UnityEngine;
+
+    public class KeyBindPopup : MonoBehaviour
+    {
+        private const string LOCK_ID = "KER_KeyBindPopup";
+        private static Rect position = new Rect(Screen.width, Screen.height, 250.0f, 0.0f);
+        private static bool hasCentred;
+        private static KeyBindPopup instance;
+        private readonly Array availableBindings = Enum.GetValues(typeof(KeyCode));
+
+        /// <summary>
+        ///     Gets the delegate to be invoked when accepted button is clicked.
+        /// </summary>
+        public Action<KeyCode> AcceptClicked { get; private set; }
+
+        /// <summary>
+        ///     Gets the name of the binding to change.
+        /// </summary>
+        public string Name { get; private set; }
+
+        /// <summary>
+        ///     Gets the selected binding.
+        /// </summary>
+        public KeyCode Binding { get; private set; }
+
+        /// <summary>
+        ///     Gets whether a key bind popup is already open.
+        /// </summary>
+        public static bool IsOpen
+        {
+            get
+            {
+                return (instance != null);
+            }
+        }
+
+        /// <summary>
+        ///     Gets and sets the input lock state.
+        /// </summary>
+        public bool InputLock
+        {
+            get
+            {
+                return InputLockManager.GetControlLock(LOCK_ID) != ControlTypes.None;
+            }
+            set
+            {
+                if (value)
+                {
+                    InputLockManager.SetControlLock(ControlTypes.All, LOCK_ID);
+                }
+                else
+                {
+                    InputLockManager.SetControlLock(ControlTypes.None, LOCK_ID);
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Shows a key bind popup allowing the user to select a key for binding.
+        /// </summary>
+        public static void Show(string name, KeyCode currentBinding, Action<KeyCode> acceptClicked)
+        {
+            if (instance == null)
+            {
+                instance = new GameObject("SelectKeyBind").AddComponent<KeyBindPopup>();
+            }
+
+            instance.Name = name;
+            instance.Binding = currentBinding;
+            instance.AcceptClicked = acceptClicked;
+        }
+
+        /// <summary>
+        ///     Handles the accept button click event.
+        /// </summary>
+        public void OnAccept()
+        {
+            if (AcceptClicked != null)
+            {
+                AcceptClicked.Invoke(Binding);
+            }
+            Destroy(gameObject);
+        }
+
+        /// <summary>
+        ///     Handles the cancel button click event.
+        /// </summary>
+        public void OnCancel()
+        {
+            Destroy(gameObject);
+        }
+
+        /// <summary>
+        ///     Called by unity when the component is created.
+        /// </summary>
+        protected virtual void Awake()
+        {
+            if (instance == null)
+            {
+                instance = this;
+            }
+            else if (instance != this)
+            {
+                OnCancel();
+            }
+        }
+
+        /// <summary>
+        ///     Called by unity when the component is destroyed.
+        /// </summary>
+        protected virtual void OnDestroy()
+        {
+            InputLock = false;
+        }
+
+        /// <summary>
+        ///     Called by unity each frame to render the GUI.
+        /// </summary>
+        protected virtual void OnGUI()
+        {
+            position = GUILayout.Window(GetInstanceID(), position, RenderWindow, "Select Key Bind", HighLogic.Skin.window).ClampToScreen();
+            CentreWindow();
+        }
+
+        /// <summary>
+        ///     Called by unity every frame.
+        /// </summary>
+        protected virtual void Update()
+        {
+            CentreWindow();
+            UpdateBinding();
+            UpdateInputLock();
+        }
+
+        /// <summary>
+        ///     Centres the window on the screen.
+        /// </summary>
+        private static void CentreWindow()
+        {
+            if (hasCentred == false && position.width > 0.0f && position.height > 0.0f)
+            {
+                hasCentred = true;
+                position.center = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f);
+            }
+        }
+
+        /// <summary>
+        ///     Renders the window content.
+        /// </summary>
+        private void RenderWindow(int id)
+        {
+            GUILayout.Label("Press the desired key to change it.");
+
+            // Binding labels.
+            GUILayout.BeginVertical(HighLogic.Skin.textArea);
+            GUILayout.Label("Key Bind: " + Name);
+            GUILayout.Label("Selected: " + Binding);
+            GUILayout.EndVertical();
+
+            // Window buttons.
+            GUILayout.BeginHorizontal();
+            if (GUILayout.Button("Cancel", HighLogic.Skin.button))
+            {
+                OnCancel();
+            }
+
+            if (GUILayout.Button("Accept", HighLogic.Skin.button))
+            {
+                OnAccept();
+            }
+            GUILayout.EndHorizontal();
+
+            // Make the window to be draggable.
+            GUI.DragWindow();
+        }
+
+        /// <summary>
+        ///     Updates the binding selected by the user.
+        /// </summary>
+        private void UpdateBinding()
+        {
+            for (int i = 0; i < availableBindings.Length; ++i)
+            {
+                KeyCode keyCode = (KeyCode)availableBindings.GetValue(i);
+
+                if (keyCode == KeyCode.Mouse0)
+                {
+                    continue;
+                }
+
+                if (Input.GetKeyDown(keyCode))
+                {
+                    if (Input.GetKeyDown(keyCode))
+                    {
+                        Binding = keyCode;
+                    }
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Updates the input lock.
+        /// </summary>
+        private void UpdateInputLock()
+        {
+            bool mouseOver = position.MouseIsOver();
+            bool inputLock = InputLock;
+
+            if (mouseOver && inputLock == false)
+            {
+                InputLock = true;
+            }
+            else if (mouseOver == false && inputLock)
+            {
+                InputLock = false;
+            }
+        }
+    }
+}

--- /dev/null
+++ b/KerbalEngineer/KeyBinding/KeyBinder.cs
@@ -1,1 +1,255 @@
-
+// 
+//     Kerbal Engineer Redux
+// 
+//     Copyright (C) 2015 CYBUTEK
+// 
+//     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 3 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, see <http://www.gnu.org/licenses/>.
+// 
+
+namespace KerbalEngineer.KeyBinding
+{
+    using System;
+    using System.IO;
+    using Extensions;
+    using Helpers;
+    using UnityEngine;
+
+    public class KeyBinder : MonoBehaviour
+    {
+        private const string LOCK_ID = "KER_KeyBinder";
+        private static readonly string filePath = Path.Combine(EngineerGlobals.SettingsPath, "KeyBinds.xml");
+        private static KeyBindingsObject bindings;
+        private static Rect position = new Rect(Screen.width, Screen.height, 500.0f, 0.0f);
+        private static bool hasCentred;
+
+        static KeyBinder()
+        {
+            Load();
+        }
+
+        /// <summary>
+        ///     Gets whether the key binder window is open.
+        /// </summary>
+        public static bool IsOpen { get; private set; }
+
+        /// <summary>
+        ///     Gets and sets the key bindings object.
+        /// </summary>
+        public static KeyBindingsObject Bindings
+        {
+            get
+            {
+                if (bindings == null)
+                {
+                    bindings = new KeyBindingsObject();
+                }
+                return bindings;
+            }
+            private set
+            {
+                if (value != null)
+                {
+                    bindings = value;
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Gets and sets the editor show/hide binding.
+        /// </summary>
+        public static KeyCode EditorShowHide
+        {
+            get
+            {
+                return Bindings.EditorShowHide;
+            }
+            set
+            {
+                Bindings.EditorShowHide = value;
+                Save();
+            }
+        }
+
+        /// <summary>
+        ///     Gets and sets the flight show/hide binding.
+        /// </summary>
+        public static KeyCode FlightShowHide
+        {
+            get
+            {
+                return Bindings.FlightShowHide;
+            }
+            set
+            {
+                Bindings.FlightShowHide = value;
+                Save();
+            }
+        }
+
+        /// <summary>
+        ///     Gets and sets the input lock state.
+        /// </summary>
+        public bool InputLock
+        {
+            get
+            {
+                return InputLockManager.GetControlLock(LOCK_ID) != ControlTypes.None;
+            }
+            set
+            {
+                if (value)
+                {
+                    InputLockManager.SetControlLock(ControlTypes.All, LOCK_ID);
+                }
+                else
+                {
+                    InputLockManager.SetControlLock(ControlTypes.None, LOCK_ID);
+                }
+            }
+        }
+
+        /// <summary>
+        ///     Loads the key bindings from disk.
+        /// </summary>
+        public static void Load()
+        {
+            Bindings = XmlHelper.LoadObject<KeyBindingsObject>(filePath);
+        }
+
+        /// <summary>
+        ///     Saves the key bindings to disk.
+        /// </summary>
+        public static void Save()
+        {
+            XmlHelper.SaveObject(filePath, Bindings);
+        }
+
+        /// <summary>
+        ///     Shows the key binding window.
+        /// </summary>
+        public static void Show()
+        {
+            if (IsOpen)
+            {
+                return;
+            }
+
+            new GameObject("KeyBinder").AddComponent<KeyBinder>();
+        }
+
+        /// <summary>
+        ///     Called by unity when component is created.
+        /// </summary>
+        protected virtual void Awake()
+        {
+            if (IsOpen)
+            {
+                Destroy(gameObject);
+            }
+            else
+            {
+                IsOpen = true;
+                position.height = 0.0f;
+            }
+        }
+
+        /// <summary>
+        ///     Called by unity when component is destroyed.
+        /// </summary>
+        protected virtual void OnDestroy()
+        {
+            IsOpen = false;
+            InputLock = false;
+        }
+
+        /// <summary>
+        ///     Called by unity to draw the GUI.
+        /// </summary>
+        protected virtual void OnGUI()
+        {
+            position = GUILayout.Window(GetInstanceID(), position, RenderWindow, "Kerbal Engineer Redux - Key Bindings", HighLogic.Skin.window).ClampToScreen();
+            CentreWindow();
+        }
+
+        /// <summary>
+        ///     Called by unity every frame.
+        /// </summary>
+        protected virtual void Update()
+        {
+            UpdateInputLock();
+        }
+
+        /// <summary>
+        ///     Renders a key bind option.
+        /// </summary>
+        private static void RenderKeyBind(string name, KeyCode currentBinding, Action<KeyCode> acceptClicked)
+        {
+            GUILayout.BeginHorizontal();
+            GUILayout.Label(name);
+            if (GUILayout.Button(currentBinding.ToString(), HighLogic.Skin.button, GUILayout.Width(100.0f)))
+            {
+                KeyBindPopup.Show(name, currentBinding, acceptClicked);
+            }
+            GUILayout.EndHorizontal();
+        }
+
+        /// <summary>
+        ///     Centres the window on screen.
+        /// </summary>
+        private void CentreWindow()
+        {
+            if (hasCentred == false && position.width > 0.0f && position.height > 0.0f)
+            {
+                hasCentred = true;
+                position.center = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f);
+            }
+        }
+
+        /// <summary>
+        ///     Renders the GUI window contents.
+        /// </summary>
+        private void RenderWindow(int id)
+        {
+            GUILayout.BeginVertical(HighLogic.Skin.textArea);
+            RenderKeyBind("Editor Show/Hide", EditorShowHide, binding => EditorShowHide = binding);
+            RenderKeyBind("Flight Show/Hide", FlightShowHide, binding => FlightShowHide = binding);
+            GUILayout.EndVertical();
+
+            if (GUILayout.Button("Close", HighLogic.Skin.button))
+            {
+                Destroy(gameObject);
+            }
+
+            GUI.DragWindow();
+        }
+
+        /// <summary>
+        ///     Updates the input lock.
+        /// </summary>
+        private void UpdateInputLock()
+        {
+            bool mouseOver = position.MouseIsOver();
+            bool inputLock = InputLock;
+
+            if (mouseOver && inputLock == false)
+            {
+                InputLock = true;
+            }
+            else if (mouseOver == false && inputLock)
+            {
+                InputLock = false;
+            }
+        }
+    }
+}

--- /dev/null
+++ b/KerbalEngineer/KeyBinding/KeyBindingsObject.cs
@@ -1,1 +1,31 @@
+// 
+//     Kerbal Engineer Redux
+// 
+//     Copyright (C) 2015 CYBUTEK
+// 
+//     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 3 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, see <http://www.gnu.org/licenses/>.
+// 
 
+namespace KerbalEngineer.KeyBinding
+{
+    using System.Xml.Serialization;
+    using UnityEngine;
+
+    public class KeyBindingsObject
+    {
+        public KeyCode EditorShowHide { get; set; }
+
+        public KeyCode FlightShowHide { get; set; }
+    }
+}

--- a/KerbalEngineer/VesselSimulator/EngineSim.cs
+++ b/KerbalEngineer/VesselSimulator/EngineSim.cs
@@ -327,6 +327,7 @@
                         break;
 
                     case ResourceFlowMode.ALL_VESSEL:
+                    case ResourceFlowMode.ALL_VESSEL_BAlANCE:
                         for (int i = 0; i < allParts.Count; i++)
                         {
                             PartSim aPartSim = allParts[i];
@@ -338,6 +339,7 @@
                         break;
 
                     case ResourceFlowMode.STAGE_PRIORITY_FLOW:
+                    case ResourceFlowMode.STAGE_PRIORITY_FLOW_BALANCE:
 
                         foreach (HashSet<PartSim> stagePartSet in stagePartSets.Values)
                         {
@@ -386,6 +388,8 @@
                         break;
 
                     case ResourceFlowMode.STACK_PRIORITY_SEARCH:
+                    case ResourceFlowMode.STAGE_STACK_FLOW:
+                    case ResourceFlowMode.STAGE_STACK_FLOW_BALANCE:
                         visited.Clear();
 
                         if (SimManager.logOutput)

 Binary files a/Output/KerbalEngineer/KerbalEngineer.dll and b/Output/KerbalEngineer/KerbalEngineer.dll differ
--- a/Output/KerbalEngineer/KerbalEngineer.version
+++ b/Output/KerbalEngineer/KerbalEngineer.version
@@ -5,14 +5,14 @@
 	{
 		"MAJOR":1,
 		"MINOR":0,
-		"PATCH":18,
-		"BUILD":0
+		"PATCH":19,
+		"BUILD":1
 	},
 	"KSP_VERSION":
 	{
 		"MAJOR":1,
 		"MINOR":0,
-		"PATCH":4
+		"PATCH":5
 	}
 }
 

 Binary files a/Output/KerbalEngineer/Textures/Thumbs.db and /dev/null differ