view UI/EquipmentAmountControl.cs @ 165:2c17714ad71b

Fixes #295: Index out of bounds when changing unit size and clicking in equipment list * Remove forced refresh of equipment on unit size change * Revert r620 and remove code for unlistening and relistening
author IBBoard <dev@ibboard.co.uk>
date Wed, 14 Jul 2010 20:02:20 +0000
parents e00332dc1618
children
line wrap: on
line source

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using log4net;
using IBBoard.CustomMath;
using IBBoard.Lang;
using IBBoard.Limits;
using IBBoard.Windows.Forms;
using IBBoard.Windows.Forms.I18N;
using IBBoard.Windows.Forms.Util;
using IBBoard.WarFoundry.API;
using IBBoard.WarFoundry.API.Objects;
using IBBoard.WarFoundry.API.Util;

namespace IBBoard.WarFoundry.GUI.WinForms.UI
{
	public partial class EquipmentAmountControl : UserControl
	{
		private ILog log = LogManager.GetLogger(typeof(EquipmentAmountControl));
		private Unit unit;
		private UnitEquipmentItem equip;
		public event EventHandler ValueChanged;

		public EquipmentAmountControl()
		{
			InitializeComponent();
			ControlTranslator.TranslateControl(this);
		}

		public void SetUnit(Unit equipUnit)
		{
			unit = equipUnit;
			log.Debug("Set unit to: " + (unit == null ? "null" : unit.Name));
		}

		public void SetUnitEquipmentItem(UnitEquipmentItem unitEquipment)
		{
			equip = unitEquipment;
			log.Debug("Set equipment to: " + (equip == null ? "null" : equip.Name));
			SetWidgetValues();
		}

		private void OnValueChanged()
		{
			if (ValueChanged != null)
			{
				ValueChanged(this, new EventArgs());
			}
		}

		private void SetWidgetValues()
		{
			if (equip != null)
			{
				IgnoreWidgets();

				Enabled = true;

				log.Debug("Equipment update");
				bool equipIsRatioLimit = UnitEquipmentUtil.IsEquipmentRatioLimited(unit, equip);
				log.Debug("Equipment is ratio? " + (equipIsRatioLimit ? "yes" : "no"));
				double maxPercent = RoundPercentage(UnitEquipmentUtil.GetMaxEquipmentPercentage(unit, equip));
				maxPercent = Math.Max(0, maxPercent);
				log.Debug("Equipment max percentage: " + maxPercent);
				double minPercent = RoundPercentage(UnitEquipmentUtil.GetMinEquipmentPercentage(unit, equip));
				minPercent = Math.Max(0, minPercent);
				log.Debug("Equipment min percentage: " + minPercent);
				int maxNumber = UnitEquipmentUtil.GetMaxEquipmentCount(unit, equip);
				maxNumber = Math.Max(0, maxNumber);
				log.Debug("Equipment max count: " + maxNumber);
				int minNumber = UnitEquipmentUtil.GetMinEquipmentCount(unit, equip);
				minNumber = Math.Max(0, minNumber);
				log.Debug("Equipment min count: " + minNumber);

				SetUpDownControlMinMaxes(minPercent, maxPercent, minNumber, maxNumber);

				if (equipIsRatioLimit)
				{
					SetEquipmentAmountsFromPercentage(minPercent);
				}
				else
				{
					SetEquipmentAmountsFromNumber(minNumber);
				}

				rbEquipAll.Enabled = equipIsRatioLimit && maxPercent == 100;
				rbEquipAll.Checked = equipIsRatioLimit && minPercent == 100;
				log.Debug("rbEquipAll: " + (rbEquipAll.Enabled ? "enabled":"disabled") + " " + (rbEquipAll.Checked ? "checked" : "unchecked"));
				percentage.Enabled = equipIsRatioLimit && minPercent != 100;
				rbPercentage.Enabled = percentage.Enabled;
				rbPercentage.Checked = equipIsRatioLimit && !rbEquipAll.Checked;
				log.Debug("rbPercentage: " + (rbPercentage.Enabled ? "enabled" : "disabled") + " " + (rbPercentage.Checked ? "checked" : "unchecked"));
				numeric.Enabled = !equipIsRatioLimit || minPercent != 100;
				rbNumeric.Enabled = numeric.Enabled;
				rbNumeric.Checked = !equipIsRatioLimit;
				log.Debug("rbNumeric: " + (rbNumeric.Enabled ? "enabled" : "disabled") + " " + (rbNumeric.Checked ? "checked" : "unchecked"));
				SetUnitEquipmentItemAmount();

				ListenToWidgets();
			}
			else
			{
				log.Debug("Null equipment - no widget update");
				Enabled = false;
			}
		}

		private void IgnoreWidgets()
		{
			percentage.ValueChanged -= percentage_ValueChanged;
			numeric.ValueChanged -= numeric_ValueChanged;
			rbEquipAll.CheckedChanged -= rbEquipAll_CheckedChanged;
			rbNumeric.CheckedChanged -= radioCheckedChanged;
			rbPercentage.CheckedChanged -= radioCheckedChanged;
		}

		private void ListenToWidgets()
		{
			percentage.ValueChanged += percentage_ValueChanged;
			numeric.ValueChanged += numeric_ValueChanged;
			rbEquipAll.CheckedChanged += rbEquipAll_CheckedChanged;
			rbNumeric.CheckedChanged += radioCheckedChanged;
			rbPercentage.CheckedChanged += radioCheckedChanged;
		}

		private void SetUpDownControlMinMaxes(double minPercent, double maxPercent, int minNumber, int maxNumber)
		{
			SetUpDownControlMinMax(percentage, minPercent, maxPercent);
			SetUpDownControlMinMax(numeric, (decimal) minNumber, (decimal) maxNumber);
		}

		private void SetUpDownControlMinMax(NumericUpDown upDownControl, double min, double max)
		{
			SetUpDownControlMinMax(upDownControl, (decimal)min, (decimal)max);
		}

		private void SetUpDownControlMinMax(NumericUpDown upDownControl, decimal min, decimal max)
		{
			log.Debug("Set "+upDownControl.Name+" min and max to: "+min+", "+max);
			upDownControl.Minimum = min;
			upDownControl.Maximum = max;
		}

		private void rbEquipAll_CheckedChanged(object sender, EventArgs e)
		{
			bool equipAll = rbEquipAll.Checked;
			numeric.Enabled = !equipAll;
			percentage.Enabled = !equipAll;

			if (equipAll)
			{
				numeric.Value = unit.Size;
				percentage.Value = 100;
			}

			radioCheckedChanged(sender, e);
		}

		private void percentage_ValueChanged(object sender, EventArgs e)
		{
			PerformPercentageValueChanged();
		}

		private void PerformPercentageValueChanged()
		{
			log.Debug("Percentage value changed");
			SetNumericValueFromPercentage();
			rbEquipAll.Checked = (percentage.Value == 100 && !rbNumeric.Checked);
			OnValueChanged();
		}

		private void SetNumericValueFromPercentage()
		{
			numeric.ValueChanged -= numeric_ValueChanged;
			numeric.Value = CalculateNumericValueFromPercentage(percentage.Value);
			numeric.ValueChanged += numeric_ValueChanged;
		}

		private decimal CalculateNumericValueFromPercentage(decimal percent)
		{
			decimal calcedAmount = (decimal) IBBoard.CustomMath.IBBMath.Round((double)(unit.Size * (percent / 100)), equip.RoundNumberUp);
			log.Debug("Numeric value calculated from percentage: "+percent+"% -> "+calcedAmount);
			return Math.Min(Math.Max(calcedAmount, numeric.Minimum), numeric.Maximum);
		}

		private void numeric_ValueChanged(object sender, EventArgs e)
		{
			log.Debug("Numeric value changed");
			SetPercentageValueFromNumeric();
			OnValueChanged();
		}

		private void SetPercentageValueFromNumeric()
		{
			int number = (int)numeric.Value;
			percentage.ValueChanged -= percentage_ValueChanged;
			percentage.Value = CalcualtePercentageValueFromNumber(number);
			percentage.ValueChanged += percentage_ValueChanged;
		}

		private decimal CalcualtePercentageValueFromNumber(int number)
		{
			decimal calcedAmount =  (decimal) RoundPercentage(IBBoard.CustomMath.IBBMath.Percentage(number, unit.Size));
			log.Debug("Numeric value calculated from percentage: " + number + " -> " + calcedAmount+"%");
			return Math.Min(Math.Max(calcedAmount, percentage.Minimum), percentage.Maximum);
		}

		private double RoundPercentage(double percent)
		{
			return Math.Round(percent, 1);
		}

		public double EquipmentAmount
		{
			get
			{
				double val = 0;

				if (rbNumeric.Checked)
				{
					val = (double) numeric.Value;
				}
				else if (rbPercentage.Checked)
				{
					val = (double) percentage.Value;
				}
				else if (rbEquipAll.Checked)
				{
					val = 100;
				}
				else
				{
					val = 0;
				}

				return val;
			}
		}

		public bool IsRatioEquipmentAmount
		{
			get
			{
				return !rbNumeric.Checked;
			}
		}

		private void SetUnitEquipmentItemAmount()
		{
			double equipAmountNum = UnitEquipmentUtil.GetEquipmentAmount(unit, equip);

			if (equipAmountNum > 0)
			{
				bool isRatio = UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, equip);

				if (isRatio)
				{
					SetEquipmentAmountsFromPercentage(equipAmountNum);

					if (equipAmountNum == 100)
					{
						rbEquipAll.Checked = true;
					}
					else
					{
						rbPercentage.Checked = true;
					}
				}
				else
				{
					int equipAmount = (int) equipAmountNum;
					SetEquipmentAmountsFromNumber(equipAmount);
					rbNumeric.Checked = true;
				}
			}
		}

		private void SetEquipmentAmountsFromPercentage(double equipAmount)
		{
			log.Debug("Set equipment amount from percentage: " + equipAmount);
			decimal decEquipAmount = (decimal) equipAmount;

			if (decEquipAmount > percentage.Maximum)
			{
				string percentageTooLarge = Translation.GetTranslation("equipPercentageTooLarge", "the current percentage ({0}%) was larger than the maximum for the equipment item ({1}%) - the maximum value will be used instead", equipAmount, percentage.Maximum);
				string percentageTooLargeTitle = Translation.GetTranslation("equipPercentageTooLargeTitle", "equipment percentage too large");
				MessageBox.Show(ParentForm, percentageTooLarge, percentageTooLargeTitle);
				decEquipAmount = percentage.Maximum;
				log.Debug("Limited equipment amount to " + decEquipAmount);
				OnValueChanged();
			}
			else if (decEquipAmount < percentage.Minimum)
			{
				string percentageTooSmall = Translation.GetTranslation("equipPercentageTooSmall", "the current percentage ({0}%) was smaller than the minimum for the equipment item ({1}%) - the minimum value will be used instead", equipAmount, percentage.Minimum);
				string percentageTooSmallTitle = Translation.GetTranslation("equipPercentageTooSmallTitle", "equipment percentage too small");
				MessageBox.Show(ParentForm, percentageTooSmall, percentageTooSmallTitle);
				decEquipAmount = percentage.Minimum;
				log.Debug("Limited equipment amount to " + decEquipAmount);
				OnValueChanged();
			}

			numeric.Value = CalculateNumericValueFromPercentage(decEquipAmount);
			percentage.Value = decEquipAmount;
		}

		private void SetEquipmentAmountsFromNumber(int equipAmount)
		{
			log.Debug("Set equipment percentage from amount: " + equipAmount);

			if (equipAmount > numeric.Maximum)
			{
				string amountTooLarge = Translation.GetTranslation("equipNumberTooLarge", "the current amount ({0}) was larger than the maximum for the equipment item ({1}) - the maximum value will be used instead", equipAmount, numeric.Maximum);
				string amountTooLargeTitle = Translation.GetTranslation("equipNumberTooLargeTitle", "equipment amount too large");
				MessageBox.Show(ParentForm, amountTooLarge, amountTooLargeTitle);
				equipAmount = (int)numeric.Maximum;
				log.Debug("Limited equipment amount to " + equipAmount);
				OnValueChanged();
			}
			else if (equipAmount < numeric.Minimum)
			{
				string amountTooSmall = Translation.GetTranslation("equipNumberTooSmall", "the current amount ({0}) was smaller than the minimum for the equipment item ({1}) - the minimum value will be used instead", equipAmount, numeric.Minimum);
				string amountTooSmallTitle = Translation.GetTranslation("equipNumberTooSmallTitle", "equipment amount too small");
				MessageBox.Show(ParentForm, amountTooSmall, amountTooSmallTitle);
				equipAmount = (int) numeric.Minimum;
				log.Debug("Limited equipment amount to " + equipAmount);
				OnValueChanged();
			}

			percentage.Value = CalcualtePercentageValueFromNumber(equipAmount);
			numeric.Value = equipAmount;
		}

		private void radioCheckedChanged(object sender, EventArgs e)
		{
			OnValueChanged();
		}

		protected override bool ProcessDialogKey(Keys keyData)
		{
			bool processed = false;

			if (keyData == Keys.Enter)
			{
				if (numeric.Focused)
				{
					processed = ForceUpDownControlUpdate(numeric);
				}
				else if (percentage.Focused)
				{
					processed = ForceUpDownControlUpdate(percentage);
				}
			}

			if (!processed)
			{
				processed = base.ProcessDialogKey(keyData);
			}

			return processed;
		}

		private bool ForceUpDownControlUpdate(NumericUpDown control)
		{
			ControlValueChangedChecker checker = new ControlValueChangedChecker();
			control.ValueChanged += checker.ValueChanged;
			decimal val = control.Value;
			control.ValueChanged -= checker.ValueChanged;
			return checker.valueChanged;
		}
	}
}