mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-12-22 16:36:03 +01:00
a4ffaf239f
- The value of the spinner control is rounded to the nearest, while the minimum is rounded up and the maximum down (or, in some cases, min and max are not rounded at all). In most cases this is fine, but if the current value (in bytes) is equal to one of these ranges, by rounding this way we might end up with a value that is outside of the control's ranges (causing ArgumentOutOfRange exception). Signed-off-by: Mihaela Stoica <mihaela.stoica@citrix.com>
252 lines
8.6 KiB
C#
252 lines
8.6 KiB
C#
/* Copyright (c) Citrix Systems Inc.
|
||
* All rights reserved.
|
||
*
|
||
* Redistribution and use in source and binary forms,
|
||
* with or without modification, are permitted provided
|
||
* that the following conditions are met:
|
||
*
|
||
* * Redistributions of source code must retain the above
|
||
* copyright notice, this list of conditions and the
|
||
* following disclaimer.
|
||
* * Redistributions in binary form must reproduce the above
|
||
* copyright notice, this list of conditions and the
|
||
* following disclaimer in the documentation and/or other
|
||
* materials provided with the distribution.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||
* SUCH DAMAGE.
|
||
*/
|
||
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel;
|
||
using System.Data;
|
||
using System.Drawing;
|
||
using System.Text;
|
||
using System.Windows.Forms;
|
||
|
||
namespace XenAdmin.Controls.Ballooning
|
||
{
|
||
public partial class MemorySpinner : UserControl
|
||
{
|
||
public event EventHandler SpinnerValueChanged;
|
||
private double valueMB;
|
||
private string previousUnitsValue;
|
||
private bool initializing= true;
|
||
|
||
public MemorySpinner()
|
||
{
|
||
InitializeComponent();
|
||
previousUnitsValue = Messages.VAL_GIGB;
|
||
}
|
||
|
||
public void Initialize(string name, Image icon, double amount, double static_max)
|
||
{
|
||
amount = Util.CorrectRoundingErrors(amount);
|
||
|
||
if(static_max <= Util.BINARY_GIGA)
|
||
{
|
||
Units = Messages.VAL_MEGB;
|
||
}
|
||
else
|
||
{
|
||
Units = Messages.VAL_GIGB;
|
||
}
|
||
ChangeSpinnerSettings();
|
||
previousUnitsValue = Units;
|
||
Initialize(name, icon, amount, RoundingBehaviour.None);
|
||
}
|
||
|
||
public void Initialize(string name, Image icon, double amount, RoundingBehaviour rounding)
|
||
{
|
||
NameLabel.Text = name;
|
||
if (icon != null && iconBox.Image == null) // without this line, setting iconBox.Image causes another repaint, hence an infinite loop
|
||
iconBox.Image = icon;
|
||
ValueMB = Util.ToMB(amount, rounding);
|
||
setSpinnerValueDisplay(amount);
|
||
initializing = false;
|
||
}
|
||
|
||
[Browsable(false)]
|
||
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
|
||
public string Units
|
||
{
|
||
get
|
||
{
|
||
return SpinnerUnits.Text;
|
||
}
|
||
set
|
||
{
|
||
SpinnerUnits.Text = value;
|
||
}
|
||
}
|
||
|
||
[Browsable(false)]
|
||
public double Value
|
||
{
|
||
get
|
||
{
|
||
return ValueMB * Util.BINARY_MEGA;
|
||
}
|
||
}
|
||
|
||
double ValueMB
|
||
{
|
||
get
|
||
{
|
||
return valueMB;
|
||
}
|
||
|
||
set
|
||
{
|
||
valueMB = value;
|
||
}
|
||
}
|
||
|
||
private void setSpinnerValueDisplay(double value)
|
||
{
|
||
decimal newValue;
|
||
if (Units == "GB")
|
||
{
|
||
newValue = (decimal)Util.ToGB(value, 1, RoundingBehaviour.Nearest);
|
||
}
|
||
else
|
||
{
|
||
newValue = (long)Util.ToMB(value, RoundingBehaviour.Nearest);
|
||
}
|
||
if (newValue < Spinner.Minimum)
|
||
newValue = Spinner.Minimum;
|
||
if (newValue > Spinner.Maximum)
|
||
newValue = Spinner.Maximum;
|
||
Spinner.Value = newValue;
|
||
}
|
||
|
||
public static void CalcMBRanges(double minBytes, double maxBytes, out double minMB, out double maxMB)
|
||
{
|
||
// Round ranges inwards to avoid bugs like CA-34487 and CA-34996
|
||
minMB = Util.ToMB(minBytes, RoundingBehaviour.Up);
|
||
maxMB = Util.ToMB(maxBytes, RoundingBehaviour.Down);
|
||
if (minMB > maxMB) // just in case...
|
||
{
|
||
minMB = Util.ToMB(minBytes, RoundingBehaviour.None);
|
||
maxMB = Util.ToMB(maxBytes, RoundingBehaviour.None);
|
||
}
|
||
}
|
||
|
||
public static void CalcGBRanges(double minBytes, double maxBytes, out double minGB, out double maxGB)
|
||
{
|
||
// Round ranges inwards to avoid bugs like CA-34487 and CA-34996
|
||
minGB = Util.ToGB(minBytes, 1, RoundingBehaviour.Up);
|
||
maxGB = Util.ToGB(maxBytes, 1, RoundingBehaviour.Down);
|
||
if (minGB > maxGB) // just in case...
|
||
{
|
||
minGB = Util.ToGB(minBytes, 1, RoundingBehaviour.None);
|
||
maxGB = Util.ToGB(maxBytes, 1, RoundingBehaviour.None);
|
||
}
|
||
}
|
||
|
||
public void SetRange(double min, double max)
|
||
{
|
||
if (min > max)
|
||
return; // Can happen when we are adjusting several simultaneously: can cause a stack overflow
|
||
|
||
double spinnerMin, spinnerMax;
|
||
if (Units == "MB")
|
||
{
|
||
CalcMBRanges(min, max, out spinnerMin, out spinnerMax);
|
||
}
|
||
else
|
||
{
|
||
CalcGBRanges(min, max, out spinnerMin, out spinnerMax);
|
||
}
|
||
Spinner.Minimum = (decimal)spinnerMin;
|
||
Spinner.Maximum = (decimal)spinnerMax;
|
||
}
|
||
|
||
[Browsable(false)]
|
||
public double Increment
|
||
{
|
||
get
|
||
{
|
||
return (double)Spinner.Increment;
|
||
}
|
||
set
|
||
{
|
||
if (Units == "MB")
|
||
{
|
||
Spinner.Increment = (decimal)value / Util.BINARY_MEGA;
|
||
}
|
||
else
|
||
{
|
||
// When the units are GB, we simply want the numbers to increase by 1 if the spinner value is greater than 10 GB
|
||
// and by 0.1 if smaller than 10 GB, this being the reason we ignore the given value.
|
||
if (valueMB * Util.BINARY_MEGA < 10 * Util.BINARY_GIGA)
|
||
{
|
||
Spinner.Increment = 0.1M;
|
||
}
|
||
else
|
||
{
|
||
Spinner.Increment = 1;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void Spinner_ValueChanged(object sender, EventArgs e)
|
||
{
|
||
// We do not want to modify the ValueMB if the user does not modify anything in the Spinner.Value.
|
||
// When the Memory Settings dialog is intiliazing and the units change because the new value is > 1 GB,
|
||
// we do not want any changes to be applied to ValueMB.
|
||
if (initializing)
|
||
return;
|
||
|
||
if (Units == "GB")
|
||
{
|
||
ValueMB = (double)Spinner.Value * Util.BINARY_KILO;
|
||
}
|
||
else
|
||
{
|
||
ValueMB = (double)Spinner.Value;
|
||
}
|
||
|
||
if (SpinnerValueChanged != null)
|
||
SpinnerValueChanged(this, e);
|
||
}
|
||
|
||
private void Spinner_Leave(object sender, EventArgs e)
|
||
{
|
||
if (sender is NumericUpDown)
|
||
((Control)sender).Text = ((NumericUpDown)sender).Value.ToString();
|
||
}
|
||
|
||
private void ChangeSpinnerSettings()
|
||
{
|
||
if (Units == previousUnitsValue)
|
||
return;
|
||
|
||
if (Units == "GB")
|
||
{
|
||
SetRange((double)Spinner.Minimum * Util.BINARY_MEGA, (double)Spinner.Maximum * Util.BINARY_MEGA);
|
||
Spinner.DecimalPlaces = 1;
|
||
}
|
||
else
|
||
{
|
||
SetRange((double)Spinner.Minimum * Util.BINARY_GIGA, (double)Spinner.Maximum * Util.BINARY_GIGA);
|
||
Spinner.DecimalPlaces = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|