Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

1624 lines
43 KiB
C#

//
// System.Windows.Forms.ScrollBar.cs
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
// Copyright (C) 2004-2005, Novell, Inc.
//
// Authors:
// Jordi Mas i Hernandez jordi@ximian.com
//
//
// COMPLETE
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace System.Windows.Forms
{
[ComVisible (true)]
[ClassInterface (ClassInterfaceType.AutoDispatch)]
[DefaultEvent ("Scroll")]
[DefaultProperty ("Value")]
public abstract class ScrollBar : Control
{
#region Local Variables
private int position;
private int minimum;
private int maximum;
private int large_change;
private int small_change;
internal int scrollbutton_height;
internal int scrollbutton_width;
private Rectangle first_arrow_area = new Rectangle (); // up or left
private Rectangle second_arrow_area = new Rectangle (); // down or right
private Rectangle thumb_pos = new Rectangle ();
private Rectangle thumb_area = new Rectangle ();
internal ButtonState firstbutton_state = ButtonState.Normal;
internal ButtonState secondbutton_state = ButtonState.Normal;
private bool firstbutton_pressed = false;
private bool secondbutton_pressed = false;
private bool thumb_pressed = false;
private float pixel_per_pos = 0;
private Timer timer = new Timer ();
private TimerType timer_type;
private int thumb_size = 40;
private const int thumb_min_size = 8;
private const int thumb_notshown_size = 40;
internal bool use_manual_thumb_size;
internal int manual_thumb_size;
internal bool vert;
internal bool implicit_control;
private int lastclick_pos; // Position of the last button-down event
private int thumbclick_offset; // Position of the last button-down event relative to the thumb edge
private Rectangle dirty;
internal ThumbMoving thumb_moving = ThumbMoving.None;
bool first_button_entered;
bool second_button_entered;
bool thumb_entered;
#endregion // Local Variables
private enum TimerType
{
HoldButton,
RepeatButton,
HoldThumbArea,
RepeatThumbArea
}
internal enum ThumbMoving
{
None,
Forward,
Backwards,
}
#region events
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler AutoSizeChanged {
add { base.AutoSizeChanged += value; }
remove { base.AutoSizeChanged -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler BackColorChanged {
add { base.BackColorChanged += value; }
remove { base.BackColorChanged -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler BackgroundImageChanged {
add { base.BackgroundImageChanged += value; }
remove { base.BackgroundImageChanged -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler BackgroundImageLayoutChanged {
add { base.BackgroundImageLayoutChanged += value; }
remove { base.BackgroundImageLayoutChanged -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler Click {
add { base.Click += value; }
remove { base.Click -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler DoubleClick {
add { base.DoubleClick += value; }
remove { base.DoubleClick -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler FontChanged {
add { base.FontChanged += value; }
remove { base.FontChanged -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler ForeColorChanged {
add { base.ForeColorChanged += value; }
remove { base.ForeColorChanged -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler ImeModeChanged {
add { base.ImeModeChanged += value; }
remove { base.ImeModeChanged -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event MouseEventHandler MouseClick {
add { base.MouseClick += value; }
remove { base.MouseClick -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event MouseEventHandler MouseDoubleClick {
add { base.MouseDoubleClick += value; }
remove { base.MouseDoubleClick -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event MouseEventHandler MouseDown {
add { base.MouseDown += value; }
remove { base.MouseDown -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event MouseEventHandler MouseMove {
add { base.MouseMove += value; }
remove { base.MouseMove -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event MouseEventHandler MouseUp {
add { base.MouseUp += value; }
remove { base.MouseUp -= value; }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event PaintEventHandler Paint {
add { base.Paint += value; }
remove { base.Paint -= value; }
}
static object ScrollEvent = new object ();
static object ValueChangedEvent = new object ();
public event ScrollEventHandler Scroll {
add { Events.AddHandler (ScrollEvent, value); }
remove { Events.RemoveHandler (ScrollEvent, value); }
}
[Browsable (false)]
[EditorBrowsable (EditorBrowsableState.Never)]
public new event EventHandler TextChanged {
add { base.TextChanged += value; }
remove { base.TextChanged -= value; }
}
public event EventHandler ValueChanged {
add { Events.AddHandler (ValueChangedEvent, value); }
remove { Events.RemoveHandler (ValueChangedEvent, value); }
}
#endregion Events
public ScrollBar ()
{
position = 0;
minimum = 0;
maximum = 100;
large_change = 10;
small_change = 1;
timer.Tick += new EventHandler (OnTimer);
MouseEnter += new EventHandler (OnMouseEnter);
MouseLeave += new EventHandler (OnMouseLeave);
base.KeyDown += new KeyEventHandler (OnKeyDownSB);
base.MouseDown += new MouseEventHandler (OnMouseDownSB);
base.MouseUp += new MouseEventHandler (OnMouseUpSB);
base.MouseMove += new MouseEventHandler (OnMouseMoveSB);
base.Resize += new EventHandler (OnResizeSB);
base.TabStop = false;
base.Cursor = Cursors.Default;
SetStyle (ControlStyles.UserPaint | ControlStyles.StandardClick | ControlStyles.UseTextForAccessibility, false);
}
#region Internal & Private Properties
internal Rectangle FirstArrowArea {
get {
return this.first_arrow_area;
}
set {
this.first_arrow_area = value;
}
}
internal Rectangle SecondArrowArea {
get {
return this.second_arrow_area;
}
set {
this.second_arrow_area = value;
}
}
int MaximumAllowed {
get {
return use_manual_thumb_size ? maximum - manual_thumb_size + 1 :
maximum - LargeChange + 1;
}
}
internal Rectangle ThumbPos {
get {
return thumb_pos;
}
set {
thumb_pos = value;
}
}
internal bool FirstButtonEntered {
get { return first_button_entered; }
private set {
if (first_button_entered == value)
return;
first_button_entered = value;
if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
Invalidate (first_arrow_area);
}
}
internal bool SecondButtonEntered {
get { return second_button_entered; }
private set {
if (second_button_entered == value)
return;
second_button_entered = value;
if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
Invalidate (second_arrow_area);
}
}
internal bool ThumbEntered {
get { return thumb_entered; }
private set {
if (thumb_entered == value)
return;
thumb_entered = value;
if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
Invalidate (thumb_pos);
}
}
internal bool ThumbPressed {
get { return thumb_pressed; }
private set {
if (thumb_pressed == value)
return;
thumb_pressed = value;
if (ThemeEngine.Current.ScrollBarHasPressedThumbStyle)
Invalidate (thumb_pos);
}
}
#endregion // Internal & Private Properties
#region Public Properties
[EditorBrowsable (EditorBrowsableState.Never)]
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public override bool AutoSize {
get { return base.AutoSize; }
set { base.AutoSize = value; }
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Browsable (false)]
public override Color BackColor
{
get { return base.BackColor; }
set {
if (base.BackColor == value)
return;
base.BackColor = value;
Refresh ();
}
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Browsable (false)]
public override Image BackgroundImage
{
get { return base.BackgroundImage; }
set {
if (base.BackgroundImage == value)
return;
base.BackgroundImage = value;
}
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Browsable (false)]
public override ImageLayout BackgroundImageLayout {
get { return base.BackgroundImageLayout; }
set { base.BackgroundImageLayout = value; }
}
protected override CreateParams CreateParams
{
get { return base.CreateParams; }
}
protected override Padding DefaultMargin {
get { return Padding.Empty; }
}
protected override ImeMode DefaultImeMode
{
get { return ImeMode.Disable; }
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Browsable (false)]
public override Font Font
{
get { return base.Font; }
set {
if (base.Font.Equals (value))
return;
base.Font = value;
}
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Browsable (false)]
public override Color ForeColor
{
get { return base.ForeColor; }
set {
if (base.ForeColor == value)
return;
base.ForeColor = value;
Refresh ();
}
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Browsable (false)]
public new ImeMode ImeMode
{
get { return base.ImeMode; }
set {
if (base.ImeMode == value)
return;
base.ImeMode = value;
}
}
[DefaultValue (10)]
[RefreshProperties(RefreshProperties.Repaint)]
[MWFDescription("Scroll amount when clicking in the scroll area"), MWFCategory("Behaviour")]
public int LargeChange {
get { return Math.Min (large_change, maximum - minimum + 1); }
set {
if (value < 0)
throw new ArgumentOutOfRangeException ("LargeChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
if (large_change != value) {
large_change = value;
// thumb area depends on large change value,
// so we need to recalculate it.
CalcThumbArea ();
UpdatePos (Value, true);
InvalidateDirty ();
// UIA Framework: Generate UIA Event to indicate LargeChange change
OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.LargeIncrement, value));
}
}
}
[DefaultValue (100)]
[RefreshProperties(RefreshProperties.Repaint)]
[MWFDescription("Highest value for scrollbar"), MWFCategory("Behaviour")]
public int Maximum {
get { return maximum; }
set {
if (maximum == value)
return;
maximum = value;
// UIA Framework: Generate UIA Event to indicate Maximum change
OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.Last, value));
if (maximum < minimum)
minimum = maximum;
if (Value > maximum)
Value = maximum;
// thumb area depends on maximum value,
// so we need to recalculate it.
CalcThumbArea ();
UpdatePos (Value, true);
InvalidateDirty ();
}
}
internal void SetValues (int maximum, int large_change)
{
SetValues (-1, maximum, -1, large_change);
}
internal void SetValues (int minimum, int maximum, int small_change, int large_change)
{
bool update = false;
if (-1 != minimum && this.minimum != minimum) {
this.minimum = minimum;
if (minimum > this.maximum)
this.maximum = minimum;
update = true;
// change the position if it is out of range now
position = Math.Max (position, minimum);
}
if (-1 != maximum && this.maximum != maximum) {
this.maximum = maximum;
if (maximum < this.minimum)
this.minimum = maximum;
update = true;
// change the position if it is out of range now
position = Math.Min (position, maximum);
}
if (-1 != small_change && this.small_change != small_change) {
this.small_change = small_change;
}
if (this.large_change != large_change) {
this.large_change = large_change;
update = true;
}
if (update) {
CalcThumbArea ();
UpdatePos (Value, true);
InvalidateDirty ();
}
}
[DefaultValue (0)]
[RefreshProperties(RefreshProperties.Repaint)]
[MWFDescription("Smallest value for scrollbar"), MWFCategory("Behaviour")]
public int Minimum {
get { return minimum; }
set {
if (minimum == value)
return;
minimum = value;
// UIA Framework: Generate UIA Event to indicate Minimum change
OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.First, value));
if (minimum > maximum)
maximum = minimum;
// thumb area depends on minimum value,
// so we need to recalculate it.
CalcThumbArea ();
UpdatePos (Value, true);
InvalidateDirty ();
}
}
[DefaultValue (1)]
[MWFDescription("Scroll amount when clicking scroll arrows"), MWFCategory("Behaviour")]
public int SmallChange {
get { return small_change > LargeChange ? LargeChange : small_change; }
set {
if ( value < 0 )
throw new ArgumentOutOfRangeException ("SmallChange", string.Format ("Value '{0}' must be greater than or equal to 0.", value));
if (small_change != value) {
small_change = value;
UpdatePos (Value, true);
InvalidateDirty ();
// UIA Framework: Generate UIA Event to indicate SmallChange change
OnUIAValueChanged (new ScrollEventArgs (ScrollEventType.SmallIncrement, value));
}
}
}
[DefaultValue (false)]
public new bool TabStop {
get { return base.TabStop; }
set { base.TabStop = value; }
}
[EditorBrowsable (EditorBrowsableState.Never)]
[Bindable (false)]
[Browsable (false)]
[DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
public override string Text {
get { return base.Text; }
set { base.Text = value; }
}
[Bindable(true)]
[DefaultValue (0)]
[MWFDescription("Current value for scrollbar"), MWFCategory("Behaviour")]
public int Value {
get { return position; }
set {
if ( value < minimum || value > maximum )
throw new ArgumentOutOfRangeException ("Value", string.Format ("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
if (position != value){
position = value;
OnValueChanged (EventArgs.Empty);
if (IsHandleCreated) {
Rectangle thumb_rect = thumb_pos;
UpdateThumbPos ((vert ? thumb_area.Y : thumb_area.X) + (int)(((float)(position - minimum)) * pixel_per_pos), false, false);
MoveThumb (thumb_rect, vert ? thumb_pos.Y : thumb_pos.X);
}
}
}
}
#endregion //Public Properties
#region Public Methods
protected override Rectangle GetScaledBounds (Rectangle bounds, SizeF factor, BoundsSpecified specified)
{
// Basically, we want to keep our small edge and scale the long edge
// ie: if we are vertical, don't scale our width
if (vert)
return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Height) | (specified & BoundsSpecified.Location));
else
return base.GetScaledBounds (bounds, factor, (specified & BoundsSpecified.Width) | (specified & BoundsSpecified.Location));
}
protected override void OnEnabledChanged (EventArgs e)
{
base.OnEnabledChanged (e);
if (Enabled)
firstbutton_state = secondbutton_state = ButtonState.Normal;
else
firstbutton_state = secondbutton_state = ButtonState.Inactive;
Refresh ();
}
protected override void OnHandleCreated (System.EventArgs e)
{
base.OnHandleCreated (e);
CalcButtonSizes ();
CalcThumbArea ();
UpdateThumbPos (thumb_area.Y + (int)(((float)(position - minimum)) * pixel_per_pos), true, false);
}
protected virtual void OnScroll (ScrollEventArgs se)
{
ScrollEventHandler eh = (ScrollEventHandler)(Events [ScrollEvent]);
if (eh == null)
return;
if (se.NewValue < Minimum) {
se.NewValue = Minimum;
}
if (se.NewValue > Maximum) {
se.NewValue = Maximum;
}
eh (this, se);
}
private void SendWMScroll(ScrollBarCommands cmd) {
if ((Parent != null) && Parent.IsHandleCreated) {
if (vert) {
XplatUI.SendMessage(Parent.Handle, Msg.WM_VSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
} else {
XplatUI.SendMessage(Parent.Handle, Msg.WM_HSCROLL, (IntPtr)cmd, implicit_control ? IntPtr.Zero : Handle);
}
}
}
protected virtual void OnValueChanged (EventArgs e)
{
EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
if (eh != null)
eh (this, e);
}
public override string ToString()
{
return string.Format("{0}, Minimum: {1}, Maximum: {2}, Value: {3}",
GetType( ).FullName, minimum, maximum, position);
}
protected void UpdateScrollInfo ()
{
Refresh ();
}
protected override void WndProc (ref Message m)
{
base.WndProc (ref m);
}
#endregion //Public Methods
#region Private Methods
private void CalcButtonSizes ()
{
if (vert) {
if (Height < ThemeEngine.Current.ScrollBarButtonSize * 2)
scrollbutton_height = Height /2;
else
scrollbutton_height = ThemeEngine.Current.ScrollBarButtonSize;
} else {
if (Width < ThemeEngine.Current.ScrollBarButtonSize * 2)
scrollbutton_width = Width /2;
else
scrollbutton_width = ThemeEngine.Current.ScrollBarButtonSize;
}
}
private void CalcThumbArea ()
{
int lchange = use_manual_thumb_size ? manual_thumb_size : LargeChange;
// Thumb area
if (vert) {
thumb_area.Height = Height - scrollbutton_height - scrollbutton_height;
thumb_area.X = 0;
thumb_area.Y = scrollbutton_height;
thumb_area.Width = Width;
if (Height < thumb_notshown_size)
thumb_size = 0;
else {
double per = ((double) lchange / (double)((1 + maximum - minimum)));
thumb_size = 1 + (int) (thumb_area.Height * per);
if (thumb_size < thumb_min_size)
thumb_size = thumb_min_size;
// Give the user something to drag if LargeChange is zero
if (LargeChange == 0)
thumb_size = 17;
}
pixel_per_pos = ((float)(thumb_area.Height - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
} else {
thumb_area.Y = 0;
thumb_area.X = scrollbutton_width;
thumb_area.Height = Height;
thumb_area.Width = Width - scrollbutton_width - scrollbutton_width;
if (Width < thumb_notshown_size)
thumb_size = 0;
else {
double per = ((double) lchange / (double)((1 + maximum - minimum)));
thumb_size = 1 + (int) (thumb_area.Width * per);
if (thumb_size < thumb_min_size)
thumb_size = thumb_min_size;
// Give the user something to drag if LargeChange is zero
if (LargeChange == 0)
thumb_size = 17;
}
pixel_per_pos = ((float)(thumb_area.Width - thumb_size) / (float) ((maximum - minimum - lchange) + 1));
}
}
private void LargeIncrement ()
{
ScrollEventArgs event_args;
int pos = Math.Min (MaximumAllowed, position + large_change);
event_args = new ScrollEventArgs (ScrollEventType.LargeIncrement, pos);
OnScroll (event_args);
Value = event_args.NewValue;
event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
OnScroll (event_args);
Value = event_args.NewValue;
// UIA Framework event invoked when the "LargeIncrement
// Button" is "clicked" either by using the Invoke Pattern
// or the space between the thumb and the bottom/right button
OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeIncrement, Value));
}
private void LargeDecrement ()
{
ScrollEventArgs event_args;
int pos = Math.Max (Minimum, position - large_change);
event_args = new ScrollEventArgs (ScrollEventType.LargeDecrement, pos);
OnScroll (event_args);
Value = event_args.NewValue;
event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
OnScroll (event_args);
Value = event_args.NewValue;
// UIA Framework event invoked when the "LargeDecrement
// Button" is "clicked" either by using the Invoke Pattern
// or the space between the thumb and the top/left button
OnUIAScroll (new ScrollEventArgs (ScrollEventType.LargeDecrement, Value));
}
private void OnResizeSB (Object o, EventArgs e)
{
if (Width <= 0 || Height <= 0)
return;
CalcButtonSizes ();
CalcThumbArea ();
UpdatePos (position, true);
Refresh ();
}
internal override void OnPaintInternal (PaintEventArgs pevent)
{
ThemeEngine.Current.DrawScrollBar (pevent.Graphics, pevent.ClipRectangle, this);
}
private void OnTimer (Object source, EventArgs e)
{
ClearDirty ();
switch (timer_type) {
case TimerType.HoldButton:
SetRepeatButtonTimer ();
break;
case TimerType.RepeatButton:
{
if ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Minimum) {
SmallDecrement();
SendWMScroll(ScrollBarCommands.SB_LINEUP);
}
if ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed && position != Maximum) {
SmallIncrement();
SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
}
break;
}
case TimerType.HoldThumbArea:
SetRepeatThumbAreaTimer ();
break;
case TimerType.RepeatThumbArea:
{
Point pnt, pnt_screen;
Rectangle thumb_area_screen = thumb_area;
pnt_screen = PointToScreen (new Point (thumb_area.X, thumb_area.Y));
thumb_area_screen.X = pnt_screen.X;
thumb_area_screen.Y = pnt_screen.Y;
if (thumb_area_screen.Contains (MousePosition) == false) {
timer.Enabled = false;
thumb_moving = ThumbMoving.None;
DirtyThumbArea ();
InvalidateDirty ();
}
pnt = PointToClient (MousePosition);
if (vert)
lastclick_pos = pnt.Y;
else
lastclick_pos = pnt.X;
if (thumb_moving == ThumbMoving.Forward) {
if ((vert && (thumb_pos.Y + thumb_size > lastclick_pos)) ||
(!vert && (thumb_pos.X + thumb_size > lastclick_pos)) ||
(thumb_area.Contains (pnt) == false)) {
timer.Enabled = false;
thumb_moving = ThumbMoving.None;
Refresh ();
return;
} else {
LargeIncrement ();
SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
}
} else {
if ((vert && (thumb_pos.Y < lastclick_pos)) ||
(!vert && (thumb_pos.X < lastclick_pos))){
timer.Enabled = false;
thumb_moving = ThumbMoving.None;
SendWMScroll(ScrollBarCommands.SB_PAGEUP);
Refresh ();
} else {
LargeDecrement ();
SendWMScroll(ScrollBarCommands.SB_PAGEUP);
}
}
break;
}
default:
break;
}
InvalidateDirty ();
}
private void MoveThumb (Rectangle original_thumbpos, int value)
{
/* so, the reason this works can best be
* described by the following 1 dimensional
* pictures
*
* say you have a scrollbar thumb positioned
* thusly:
*
* <---------------------| |------------------------------>
*
* and you want it to end up looking like this:
*
* <-----------------------------| |---------------------->
*
* that can be done with the scrolling api by
* extending the rectangle to encompass both
* positions:
*
* start of range end of range
* \ /
* <---------------------| |-------|---------------------->
*
* so, we end up scrolling just this little region:
*
* | |-------|
*
* and end up with ********| |
*
* where ****** is space that is automatically
* redrawn.
*
* It's clear that in both cases (left to
* right, right to left) we need to extend the
* size of the scroll rectangle to encompass
* both. In the right to left case, we also
* need to decrement the X coordinate.
*
* We call Update after scrolling to make sure
* there's no garbage left in the window to be
* copied again if we're called before the
* paint events have been handled.
*
*/
int delta;
if (vert) {
delta = value - original_thumbpos.Y;
if (delta < 0) {
original_thumbpos.Y += delta;
original_thumbpos.Height -= delta;
}
else {
original_thumbpos.Height += delta;
}
XplatUI.ScrollWindow (Handle, original_thumbpos, 0, delta, false);
}
else {
delta = value - original_thumbpos.X;
if (delta < 0) {
original_thumbpos.X += delta;
original_thumbpos.Width -= delta;
}
else {
original_thumbpos.Width += delta;
}
XplatUI.ScrollWindow (Handle, original_thumbpos, delta, 0, false);
}
Update ();
}
private void OnMouseMoveSB (object sender, MouseEventArgs e)
{
if (Enabled == false)
return;
FirstButtonEntered = first_arrow_area.Contains (e.Location);
SecondButtonEntered = second_arrow_area.Contains (e.Location);
if (thumb_size == 0)
return;
ThumbEntered = thumb_pos.Contains (e.Location);
if (firstbutton_pressed) {
if (!first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
firstbutton_state = ButtonState.Normal;
Invalidate (first_arrow_area);
Update();
return;
} else if (first_arrow_area.Contains (e.X, e.Y) && ((firstbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
firstbutton_state = ButtonState.Pushed;
Invalidate (first_arrow_area);
Update();
return;
}
} else if (secondbutton_pressed) {
if (!second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Pushed) == ButtonState.Pushed)) {
secondbutton_state = ButtonState.Normal;
Invalidate (second_arrow_area);
Update();
return;
} else if (second_arrow_area.Contains (e.X, e.Y) && ((secondbutton_state & ButtonState.Normal) == ButtonState.Normal)) {
secondbutton_state = ButtonState.Pushed;
Invalidate (second_arrow_area);
Update();
return;
}
} else if (thumb_pressed == true) {
if (vert) {
int thumb_edge = e.Y - thumbclick_offset;
if (thumb_edge < thumb_area.Y)
thumb_edge = thumb_area.Y;
else if (thumb_edge > thumb_area.Bottom - thumb_size)
thumb_edge = thumb_area.Bottom - thumb_size;
if (thumb_edge != thumb_pos.Y) {
Rectangle thumb_rect = thumb_pos;
UpdateThumbPos (thumb_edge, false, true);
MoveThumb (thumb_rect, thumb_pos.Y);
OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
}
SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
} else {
int thumb_edge = e.X - thumbclick_offset;
if (thumb_edge < thumb_area.X)
thumb_edge = thumb_area.X;
else if (thumb_edge > thumb_area.Right - thumb_size)
thumb_edge = thumb_area.Right - thumb_size;
if (thumb_edge != thumb_pos.X) {
Rectangle thumb_rect = thumb_pos;
UpdateThumbPos (thumb_edge, false, true);
MoveThumb (thumb_rect, thumb_pos.X);
OnScroll (new ScrollEventArgs (ScrollEventType.ThumbTrack, position));
}
SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
}
}
}
private void OnMouseDownSB (object sender, MouseEventArgs e)
{
ClearDirty ();
if (Enabled == false || (e.Button & MouseButtons.Left) == 0)
return;
if (firstbutton_state != ButtonState.Inactive && first_arrow_area.Contains (e.X, e.Y)) {
SendWMScroll(ScrollBarCommands.SB_LINEUP);
firstbutton_state = ButtonState.Pushed;
firstbutton_pressed = true;
Invalidate (first_arrow_area);
Update();
if (!timer.Enabled) {
SetHoldButtonClickTimer ();
timer.Enabled = true;
}
}
if (secondbutton_state != ButtonState.Inactive && second_arrow_area.Contains (e.X, e.Y)) {
SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
secondbutton_state = ButtonState.Pushed;
secondbutton_pressed = true;
Invalidate (second_arrow_area);
Update();
if (!timer.Enabled) {
SetHoldButtonClickTimer ();
timer.Enabled = true;
}
}
if (thumb_size > 0 && thumb_pos.Contains (e.X, e.Y)) {
ThumbPressed = true;
SendWMScroll(ScrollBarCommands.SB_THUMBTRACK);
if (vert) {
thumbclick_offset = e.Y - thumb_pos.Y;
lastclick_pos = e.Y;
}
else {
thumbclick_offset = e.X - thumb_pos.X;
lastclick_pos = e.X;
}
} else {
if (thumb_size > 0 && thumb_area.Contains (e.X, e.Y)) {
if (vert) {
lastclick_pos = e.Y;
if (e.Y > thumb_pos.Y + thumb_pos.Height) {
SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
LargeIncrement ();
thumb_moving = ThumbMoving.Forward;
Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
ClientRectangle.Width,
ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
scrollbutton_height));
} else {
SendWMScroll(ScrollBarCommands.SB_PAGEUP);
LargeDecrement ();
thumb_moving = ThumbMoving.Backwards;
Dirty (new Rectangle (0, scrollbutton_height,
ClientRectangle.Width,
thumb_pos.Y - scrollbutton_height));
}
} else {
lastclick_pos = e.X;
if (e.X > thumb_pos.X + thumb_pos.Width) {
SendWMScroll(ScrollBarCommands.SB_PAGEDOWN);
thumb_moving = ThumbMoving.Forward;
LargeIncrement ();
Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
scrollbutton_width,
ClientRectangle.Height));
} else {
SendWMScroll(ScrollBarCommands.SB_PAGEUP);
thumb_moving = ThumbMoving.Backwards;
LargeDecrement ();
Dirty (new Rectangle (scrollbutton_width, 0,
thumb_pos.X - scrollbutton_width,
ClientRectangle.Height));
}
}
SetHoldThumbAreaTimer ();
timer.Enabled = true;
InvalidateDirty ();
}
}
}
private void OnMouseUpSB (object sender, MouseEventArgs e)
{
ClearDirty ();
if (Enabled == false)
return;
timer.Enabled = false;
if (thumb_moving != ThumbMoving.None) {
DirtyThumbArea ();
thumb_moving = ThumbMoving.None;
}
if (firstbutton_pressed) {
firstbutton_state = ButtonState.Normal;
if (first_arrow_area.Contains (e.X, e.Y)) {
SmallDecrement ();
}
SendWMScroll(ScrollBarCommands.SB_LINEUP);
firstbutton_pressed = false;
Dirty (first_arrow_area);
} else if (secondbutton_pressed) {
secondbutton_state = ButtonState.Normal;
if (second_arrow_area.Contains (e.X, e.Y)) {
SmallIncrement ();
}
SendWMScroll(ScrollBarCommands.SB_LINEDOWN);
Dirty (second_arrow_area);
secondbutton_pressed = false;
} else if (thumb_pressed == true) {
OnScroll (new ScrollEventArgs (ScrollEventType.ThumbPosition, position));
OnScroll (new ScrollEventArgs (ScrollEventType.EndScroll, position));
SendWMScroll(ScrollBarCommands.SB_THUMBPOSITION);
ThumbPressed = false;
return;
}
InvalidateDirty ();
}
private void OnKeyDownSB (Object o, KeyEventArgs key)
{
if (Enabled == false)
return;
ClearDirty ();
switch (key.KeyCode){
case Keys.Up:
{
SmallDecrement ();
break;
}
case Keys.Down:
{
SmallIncrement ();
break;
}
case Keys.PageUp:
{
LargeDecrement ();
break;
}
case Keys.PageDown:
{
LargeIncrement ();
break;
}
case Keys.Home:
{
SetHomePosition ();
break;
}
case Keys.End:
{
SetEndPosition ();
break;
}
default:
break;
}
InvalidateDirty ();
}
// I hate to do this, but we don't have the resources to track
// down everything internal that is setting a value outside the
// correct range, so we'll clamp it to the acceptable values.
internal void SafeValueSet (int value)
{
value = Math.Min (value, maximum);
value = Math.Max (value, minimum);
Value = value;
}
private void SetEndPosition ()
{
ScrollEventArgs event_args;
int pos = MaximumAllowed;
event_args = new ScrollEventArgs (ScrollEventType.Last, pos);
OnScroll (event_args);
pos = event_args.NewValue;
event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
OnScroll (event_args);
pos = event_args.NewValue;
SetValue (pos);
}
private void SetHomePosition ()
{
ScrollEventArgs event_args;
int pos = Minimum;
event_args = new ScrollEventArgs (ScrollEventType.First, pos);
OnScroll (event_args);
pos = event_args.NewValue;
event_args = new ScrollEventArgs (ScrollEventType.EndScroll, pos);
OnScroll (event_args);
pos = event_args.NewValue;
SetValue (pos);
}
private void SmallIncrement ()
{
ScrollEventArgs event_args;
int pos = Math.Min (MaximumAllowed, position + SmallChange);
event_args = new ScrollEventArgs (ScrollEventType.SmallIncrement, pos);
OnScroll (event_args);
Value = event_args.NewValue;
event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
OnScroll (event_args);
Value = event_args.NewValue;
// UIA Framework event invoked when the "SmallIncrement
// Button" (a.k.a bottom/right button) is "clicked" either
// by using the Invoke Pattern or the button itself
OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallIncrement, Value));
}
private void SmallDecrement ()
{
ScrollEventArgs event_args;
int pos = Math.Max (Minimum, position - SmallChange);
event_args = new ScrollEventArgs (ScrollEventType.SmallDecrement, pos);
OnScroll (event_args);
Value = event_args.NewValue;
event_args = new ScrollEventArgs (ScrollEventType.EndScroll, Value);
OnScroll (event_args);
Value = event_args.NewValue;
// UIA Framework event invoked when the "SmallDecrement
// Button" (a.k.a top/left button) is "clicked" either
// by using the Invoke Pattern or the button itself
OnUIAScroll (new ScrollEventArgs (ScrollEventType.SmallDecrement, Value));
}
private void SetHoldButtonClickTimer ()
{
timer.Enabled = false;
timer.Interval = 200;
timer_type = TimerType.HoldButton;
timer.Enabled = true;
}
private void SetRepeatButtonTimer ()
{
timer.Enabled = false;
timer.Interval = 50;
timer_type = TimerType.RepeatButton;
timer.Enabled = true;
}
private void SetHoldThumbAreaTimer ()
{
timer.Enabled = false;
timer.Interval = 200;
timer_type = TimerType.HoldThumbArea;
timer.Enabled = true;
}
private void SetRepeatThumbAreaTimer ()
{
timer.Enabled = false;
timer.Interval = 50;
timer_type = TimerType.RepeatThumbArea;
timer.Enabled = true;
}
private void UpdatePos (int newPos, bool update_thumbpos)
{
int pos;
if (newPos < minimum)
pos = minimum;
else
if (newPos > MaximumAllowed)
pos = MaximumAllowed;
else
pos = newPos;
// pos can't be less than minimum or greater than maximum
if (pos < minimum)
pos = minimum;
if (pos > maximum)
pos = maximum;
if (update_thumbpos) {
if (vert)
UpdateThumbPos (thumb_area.Y + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
else
UpdateThumbPos (thumb_area.X + (int)(((float)(pos - minimum)) * pixel_per_pos), true, false);
SetValue (pos);
}
else {
position = pos; // Updates directly the value to avoid thumb pos update
// XXX some reason we don't call OnValueChanged?
EventHandler eh = (EventHandler)(Events [ValueChangedEvent]);
if (eh != null)
eh (this, EventArgs.Empty);
}
}
private void UpdateThumbPos (int pixel, bool dirty, bool update_value)
{
float new_pos = 0;
if (vert) {
if (dirty)
Dirty (thumb_pos);
if (pixel < thumb_area.Y)
thumb_pos.Y = thumb_area.Y;
else if (pixel > thumb_area.Bottom - thumb_size)
thumb_pos.Y = thumb_area.Bottom - thumb_size;
else
thumb_pos.Y = pixel;
thumb_pos.X = 0;
thumb_pos.Width = ThemeEngine.Current.ScrollBarButtonSize;
thumb_pos.Height = thumb_size;
new_pos = (float) (thumb_pos.Y - thumb_area.Y);
new_pos = new_pos / pixel_per_pos;
if (dirty)
Dirty (thumb_pos);
} else {
if (dirty)
Dirty (thumb_pos);
if (pixel < thumb_area.X)
thumb_pos.X = thumb_area.X;
else if (pixel > thumb_area.Right - thumb_size)
thumb_pos.X = thumb_area.Right - thumb_size;
else
thumb_pos.X = pixel;
thumb_pos.Y = 0;
thumb_pos.Width = thumb_size;
thumb_pos.Height = ThemeEngine.Current.ScrollBarButtonSize;
new_pos = (float) (thumb_pos.X - thumb_area.X);
new_pos = new_pos / pixel_per_pos;
if (dirty)
Dirty (thumb_pos);
}
if (update_value)
UpdatePos ((int) new_pos + minimum, false);
}
private void SetValue (int value)
{
if ( value < minimum || value > maximum )
throw new ArgumentException(
String.Format("'{0}' is not a valid value for 'Value'. 'Value' should be between 'Minimum' and 'Maximum'", value));
if (position != value){
position = value;
OnValueChanged (EventArgs.Empty);
UpdatePos (value, true);
}
}
private void ClearDirty ()
{
dirty = Rectangle.Empty;
}
private void Dirty (Rectangle r)
{
if (dirty == Rectangle.Empty) {
dirty = r;
return;
}
dirty = Rectangle.Union (dirty, r);
}
private void DirtyThumbArea ()
{
if (thumb_moving == ThumbMoving.Forward) {
if (vert) {
Dirty (new Rectangle (0, thumb_pos.Y + thumb_pos.Height,
ClientRectangle.Width,
ClientRectangle.Height - (thumb_pos.Y + thumb_pos.Height) -
scrollbutton_height));
} else {
Dirty (new Rectangle (thumb_pos.X + thumb_pos.Width, 0,
ClientRectangle.Width - (thumb_pos.X + thumb_pos.Width) -
scrollbutton_width,
ClientRectangle.Height));
}
} else if (thumb_moving == ThumbMoving.Backwards) {
if (vert) {
Dirty(new Rectangle (0, scrollbutton_height,
ClientRectangle.Width,
thumb_pos.Y - scrollbutton_height));
} else {
Dirty (new Rectangle (scrollbutton_width, 0,
thumb_pos.X - scrollbutton_width,
ClientRectangle.Height));
}
}
}
private void InvalidateDirty ()
{
Invalidate (dirty);
Update();
dirty = Rectangle.Empty;
}
void OnMouseEnter (object sender, EventArgs e)
{
if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
Region region_to_invalidate = new Region (first_arrow_area);
region_to_invalidate.Union (second_arrow_area);
Invalidate (region_to_invalidate);
}
}
void OnMouseLeave (object sender, EventArgs e)
{
Region region_to_invalidate = new Region ();
region_to_invalidate.MakeEmpty ();
bool dirty = false;
if (ThemeEngine.Current.ScrollBarHasHoverArrowButtonStyle) {
region_to_invalidate.Union (first_arrow_area);
region_to_invalidate.Union (second_arrow_area);
dirty = true;
} else
if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
if (first_button_entered) {
region_to_invalidate.Union (first_arrow_area);
dirty = true;
} else if (second_button_entered) {
region_to_invalidate.Union (second_arrow_area);
dirty = true;
}
if (ThemeEngine.Current.ScrollBarHasHotElementStyles)
if (thumb_entered) {
region_to_invalidate.Union (thumb_pos);
dirty = true;
}
first_button_entered = false;
second_button_entered = false;
thumb_entered = false;
if (dirty)
Invalidate (region_to_invalidate);
region_to_invalidate.Dispose ();
}
#endregion //Private Methods
protected override void OnMouseWheel (MouseEventArgs e)
{
base.OnMouseWheel (e);
}
#region UIA Framework Section: Events, Methods and Properties.
//NOTE:
// We are using Reflection to add/remove internal events.
// Class ScrollBarButtonInvokePatternInvokeEvent uses the events.
//
// Types used to generate UIA InvokedEvent
// * args.Type = ScrollEventType.LargeIncrement. Space between Thumb and bottom/right Button
// * args.Type = ScrollEventType.LargeDecrement. Space between Thumb and top/left Button
// * args.Type = ScrollEventType.SmallIncrement. Small increment UIA Button (bottom/right Button)
// * args.Type = ScrollEventType.SmallDecrement. Small decrement UIA Button (top/left Button)
// Types used to generate RangeValue-related events
// * args.Type = ScrollEventType.LargeIncrement. LargeChange event
// * args.Type = ScrollEventType.Last. Maximum event
// * args.Type = ScrollEventType.First. Minimum event
// * args.Type = ScrollEventType.SmallIncrement. SmallChange event
static object UIAScrollEvent = new object ();
static object UIAValueChangeEvent = new object ();
internal event ScrollEventHandler UIAScroll {
add { Events.AddHandler (UIAScrollEvent, value); }
remove { Events.RemoveHandler (UIAScrollEvent, value); }
}
internal event ScrollEventHandler UIAValueChanged {
add { Events.AddHandler (UIAValueChangeEvent, value); }
remove { Events.RemoveHandler (UIAValueChangeEvent, value); }
}
internal void OnUIAScroll (ScrollEventArgs args)
{
ScrollEventHandler eh = (ScrollEventHandler) Events [UIAScrollEvent];
if (eh != null)
eh (this, args);
}
internal void OnUIAValueChanged (ScrollEventArgs args)
{
ScrollEventHandler eh = (ScrollEventHandler) Events [UIAValueChangeEvent];
if (eh != null)
eh (this, args);
}
//NOTE:
// Wrapper methods used by the Reflection.
// Class ScrollBarButtonInvokeProviderBehavior uses the events.
//
internal void UIALargeIncrement ()
{
LargeIncrement ();
}
internal void UIALargeDecrement ()
{
LargeDecrement ();
}
internal void UIASmallIncrement ()
{
SmallIncrement ();
}
internal void UIASmallDecrement ()
{
SmallDecrement ();
}
internal Rectangle UIAThumbArea {
get { return thumb_area; }
}
internal Rectangle UIAThumbPosition {
get { return thumb_pos; }
}
#endregion UIA Framework Section: Events, Methods and Properties.
}
}