UltimateZoneBuilder/Source/Plugins/BuilderEffects/Controls/NumericUpDownEx.cs
MaxED 3a35b7603a Fixed, Map Analysis mode, "Check stuck things" check: rewritten parts of the flags checking logic to allow more accurate flag checks.
Fixed, Map Analysis mode: fixed a crash when trying to dissolve an invalid sector when one of it's linedefs referenced it on the both sides.
Fixed, Sectors mode: fixed incorrect undo description when deleting sectors.
Internal: joined declaration and assignment of some more variables.
2015-12-28 15:01:53 +00:00

425 lines
11 KiB
C#

// ============================================================================ '
// NumericUpDownEx - v.1.3 '
// ============================================================================ '
// Author: Claudio Nicora '
// WebSite: http://coolsoft.altervista.org '
// CodeProject: http://www.codeproject.com/KB/edit/NumericUpDownEx.aspx '
// License: CodeProject Open License '
// http://www.codeproject.com/info/cpol10.aspx '
// Feel free to contribute here: http://coolsoft.altervista.org '
// ============================================================================ '
using System;
using System.Reflection;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;
namespace CodeImp.DoomBuilder.BuilderEffects
{
[DesignerCategory("code")]
public class NumericUpDownEx : NumericUpDown
{
// reference to the underlying TextBox control
private TextBox _textbox;
// reference to the underlying UpDownButtons control
private Control _upDownButtons;
/// <summary>
/// object creator
/// </summary>
public NumericUpDownEx()
{
// extract a reference to the underlying TextBox field
_textbox = GetPrivateField<TextBox>(this, "upDownEdit");
if(_textbox == null)
{
throw new ArgumentNullException(this.GetType().FullName + ": Can't find internal TextBox field.");
}
// extract a reference to the underlying UpDownButtons field
_upDownButtons = GetPrivateField<Control>(this, "upDownButtons");
if(_upDownButtons == null)
{
throw new ArgumentNullException(this.GetType().FullName + ": Can't find internal UpDown buttons field.");
}
// add handlers (MouseEnter and MouseLeave events of NumericUpDown
// are not working properly)
_textbox.MouseEnter += _mouseEnterLeave;
_textbox.MouseLeave += _mouseEnterLeave;
_upDownButtons.MouseEnter += _mouseEnterLeave;
_upDownButtons.MouseLeave += _mouseEnterLeave;
base.MouseEnter += _mouseEnterLeave;
base.MouseLeave += _mouseEnterLeave;
}
/// <summary>
/// Extracts a reference to a private underlying field
/// </summary>
protected static internal T GetPrivateField<T>(NumericUpDownEx ctrl, string fieldName) where T : Control
{
// find internal TextBox
FieldInfo fi = typeof(NumericUpDown).GetField(fieldName, BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Instance);
// take some caution... they could change field name in the future!
if(fi == null)
{
return null;
}
else
{
return fi.GetValue(ctrl) as T;
}
}
protected override void OnPaint(PaintEventArgs e)
{
if(_upDownButtons.Visible == false)
{
e.Graphics.Clear(this.BackColor);
}
base.OnPaint(e);
}
/// <summary>
/// WndProc override to kill WN_MOUSEWHEEL message
/// </summary>
protected override void WndProc(ref Message m)
{
const int WM_MOUSEWHEEL = 0x20a;
if(m.Msg == WM_MOUSEWHEEL)
{
switch(_interceptMouseWheel)
{
case InterceptMouseWheelMode.Always:
// standard message
base.WndProc(ref m);
break;
case InterceptMouseWheelMode.WhenMouseOver:
if(_mouseOver)
{
// standard message
base.WndProc(ref m);
}
break;
case InterceptMouseWheelMode.Never:
// kill the message
return;
}
}
else
{
base.WndProc(ref m);
}
}
#region New properties
[DefaultValue(false)]
[Category("Behavior")]
[Description("Automatically select control text when it receives focus.")]
public bool AutoSelect
{
get { return _autoSelect; }
set { _autoSelect = value; }
}
private bool _autoSelect;
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int SelectionStart
{
get { return _textbox.SelectionStart; }
set { _textbox.SelectionStart = value; }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int SelectionLength
{
get { return _textbox.SelectionLength; }
set { _textbox.SelectionLength = value; }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public string SelectedText
{
get { return _textbox.SelectedText; }
set { _textbox.SelectedText = value; }
}
[DefaultValue(typeof(InterceptMouseWheelMode), "Always")]
[Category("Behavior")]
[Description("Enables MouseWheel only under certain conditions.")]
public InterceptMouseWheelMode InterceptMouseWheel
{
get { return _interceptMouseWheel; }
set { _interceptMouseWheel = value; }
}
private InterceptMouseWheelMode _interceptMouseWheel = InterceptMouseWheelMode.Always;
public enum InterceptMouseWheelMode
{
/// <summary>MouseWheel always works (defauld behavior)</summary>
Always,
/// <summary>MouseWheel works only when mouse is over the (focused) control</summary>
WhenMouseOver,
/// <summary>MouseWheel never works</summary>
Never
}
[DefaultValue(typeof(ShowUpDownButtonsMode), "Always")]
[Category("Behavior")]
[Description("Set UpDownButtons visibility mode.")]
public ShowUpDownButtonsMode ShowUpDownButtons
{
get { return _showUpDownButtons; }
set
{
_showUpDownButtons = value;
// update UpDownButtons visibility
UpdateUpDownButtonsVisibility();
}
}
private ShowUpDownButtonsMode _showUpDownButtons = ShowUpDownButtonsMode.Always;
public enum ShowUpDownButtonsMode
{
/// <summary>UpDownButtons are always visible (defauld behavior)</summary>
Always,
/// <summary>UpDownButtons are visible only when mouse is over the control</summary>
WhenMouseOver
}
/// <summary>
/// If set, incrementing value will cause it to restart from Minimum
/// when Maximum is reached (and viceversa).
/// </summary>
[DefaultValue(false)]
[Category("Behavior")]
[Description("If set, incrementing value will cause it to restart from Minimum when Maximum is reached (and viceversa).")]
public bool WrapValue
{
get { return _wrapValue; }
set { _wrapValue = value; }
}
private bool _wrapValue;
#endregion
#region Text selection
// select all the text on focus enter
protected override void OnGotFocus(EventArgs e)
{
if(_autoSelect)
{
_textbox.SelectAll();
}
base.OnGotFocus(e);
}
// MouseUp will kill the SelectAll made on GotFocus.
// Will restore it, but only if user have not made a partial text selection.
protected override void OnMouseUp(MouseEventArgs mevent)
{
if(_autoSelect && _textbox.SelectionLength == 0)
{
_textbox.SelectAll();
}
base.OnMouseUp(mevent);
}
#endregion
#region Additional events
// these events will be raised correctly, when mouse enters on the textbox
public new event EventHandler<EventArgs> MouseEnter;
public new event EventHandler<EventArgs> MouseLeave;
// Events raised BEFORE value decrement(increment
public event CancelEventHandler BeforeValueDecrement;
public event CancelEventHandler BeforeValueIncrement;
// flag to track mouse position
private bool _mouseOver;
// this handler is called at each mouse Enter/Leave movement
private void _mouseEnterLeave(object sender, EventArgs e)
{
Rectangle cr = RectangleToScreen(ClientRectangle);
Point mp = MousePosition;
// actual state
bool isOver = cr.Contains(mp);
// test if status changed
if(_mouseOver ^ isOver)
{
// update state
_mouseOver = isOver;
if(_mouseOver)
{
if(MouseEnter != null)
{
MouseEnter(this, EventArgs.Empty);
}
}
else
{
if(MouseLeave != null)
{
MouseLeave(this, EventArgs.Empty);
}
}
}
// update UpDownButtons visibility
if(_showUpDownButtons != ShowUpDownButtonsMode.Always)
{
UpdateUpDownButtonsVisibility();
}
}
#endregion
#region Value increment/decrement management
// raises the two new events
public override void DownButton()
{
CancelEventArgs e = new CancelEventArgs();
if(BeforeValueDecrement != null)
{
BeforeValueDecrement(this, e);
}
if(e.Cancel) return;
if(_wrapValue && Value - Increment < Minimum)
{
Value = Maximum;
}
else
{
base.DownButton();
}
}
public override void UpButton()
{
CancelEventArgs e = new CancelEventArgs();
if(BeforeValueIncrement != null)
{
BeforeValueIncrement(this, e);
}
if(e.Cancel) return;
if(_wrapValue && Value + Increment > Maximum)
{
Value = Minimum;
}
else
{
base.UpButton();
}
}
#endregion
#region UpDownButtons visibility management
/// <summary>
/// Show or hide the UpDownButtons, according to ShowUpDownButtons property value
/// </summary>
public void UpdateUpDownButtonsVisibility()
{
// test new state
bool newVisible;
switch(_showUpDownButtons)
{
case ShowUpDownButtonsMode.WhenMouseOver:
newVisible = _mouseOver;
break;
default:
newVisible = true;
break;
}
// assign only if needed
if(_upDownButtons.Visible != newVisible)
{
if(newVisible)
{
_textbox.Width = this.ClientRectangle.Width - _upDownButtons.Width;
}
else
{
_textbox.Width = this.ClientRectangle.Width;
}
_upDownButtons.Visible = newVisible;
OnTextBoxResize(_textbox, EventArgs.Empty);
this.Invalidate();
}
}
/// <summary>
/// Custom textbox size management
/// </summary>
protected override void OnTextBoxResize(object source, EventArgs e)
{
if(_textbox == null)
return;
if(_showUpDownButtons == ShowUpDownButtonsMode.Always)
{
// standard management
base.OnTextBoxResize(source, e);
}
else
{
// custom management
// change position if RTL
bool fixPos = this.RightToLeft == RightToLeft.Yes ^ this.UpDownAlign == LeftRightAlignment.Left;
if(_mouseOver)
{
_textbox.Width = this.ClientSize.Width - _textbox.Left - _upDownButtons.Width - 2;
if(fixPos)
_textbox.Location = new Point(16, _textbox.Location.Y);
}
else
{
if(fixPos)
_textbox.Location = new Point(2, _textbox.Location.Y);
_textbox.Width = this.ClientSize.Width - _textbox.Left - 2;
}
}
}
#endregion
}
}