2015-09-05 18:33:34 +00:00
using System ;
2014-08-06 01:32:27 +00:00
using System.Collections.Generic ;
2014-08-07 14:55:55 +00:00
using System.ComponentModel ;
using System.Drawing ;
2014-08-06 01:32:27 +00:00
using System.Linq ;
using System.Windows.Forms ;
2014-10-14 13:17:02 +00:00
using BizHawk.Client.Common ;
2014-08-07 18:32:09 +00:00
using BizHawk.Client.EmuHawk.CustomControls ;
2014-08-06 01:32:27 +00:00
namespace BizHawk.Client.EmuHawk
{
2014-08-30 18:42:14 +00:00
//Row width depends on font size and padding
//Column width is specified in column headers
//Row width is specified for horizontal orientation
2015-09-02 22:46:23 +00:00
public partial class InputRoll : Control
2014-08-06 01:32:27 +00:00
{
2014-08-09 13:13:24 +00:00
private readonly GDIRenderer Gdi ;
2015-07-26 03:42:50 +00:00
private readonly SortedSet < Cell > SelectedItems = new SortedSet < Cell > ( new sortCell ( ) ) ;
2014-08-09 13:13:24 +00:00
2014-08-18 21:38:02 +00:00
private readonly VScrollBar VBar ;
private readonly HScrollBar HBar ;
2014-08-15 00:42:03 +00:00
2014-10-17 18:29:09 +00:00
private RollColumns _columns = new RollColumns ( ) ;
private bool _horizontalOrientation ;
private bool _programmaticallyUpdatingScrollBarValues ;
2014-09-01 15:35:48 +00:00
private int _maxCharactersInHorizontal = 1 ;
2014-08-30 18:42:14 +00:00
2014-10-17 18:29:09 +00:00
private int _rowCount ;
2014-08-09 16:11:25 +00:00
private Size _charSize ;
2014-08-09 13:13:24 +00:00
2014-10-19 14:04:59 +00:00
private RollColumn _columnDown ;
private int? _currentX ;
private int? _currentY ;
2015-03-03 06:56:45 +00:00
// Hiding lag frames (Mainly intended for < 60fps play.)
2015-02-24 20:38:46 +00:00
public int LagFramesToHide { get ; set ; }
2015-03-10 17:11:29 +00:00
public bool HideWasLagFrames { get ; set ; }
2017-02-15 16:54:45 +00:00
private byte [ ] lagFrames = new byte [ 256 ] ; // Large enough value that it shouldn't ever need resizing. // apparently not large enough for 4K
2015-02-24 19:00:12 +00:00
2015-12-03 18:16:10 +00:00
public bool allowRightClickSelecton { get ; set ; }
2015-12-03 20:06:28 +00:00
public bool letKeysModifySelection { get ; set ; }
2016-05-07 12:04:42 +00:00
public bool suspendHotkeys { get ; set ; }
2015-08-30 16:45:14 +00:00
2014-10-22 07:59:06 +00:00
private IntPtr RotatedFont ;
2016-04-17 08:47:18 +00:00
private readonly IntPtr NormalFont ;
2015-09-05 18:33:34 +00:00
private Color _foreColor ;
private Color _backColor ;
2014-10-22 07:59:06 +00:00
2014-08-06 01:32:27 +00:00
public InputRoll ( )
{
2014-08-24 14:31:25 +00:00
UseCustomBackground = true ;
2014-08-23 14:30:12 +00:00
GridLines = true ;
2014-09-24 21:10:57 +00:00
CellWidthPadding = 3 ;
2015-07-02 18:37:28 +00:00
CellHeightPadding = 0 ;
2014-08-10 22:23:14 +00:00
CurrentCell = null ;
2015-03-14 16:38:07 +00:00
ScrollMethod = "near" ;
2014-10-22 07:59:06 +00:00
2016-04-17 08:47:18 +00:00
Font CommonFont = new Font ( "Arial" , 8 , FontStyle . Bold ) ;
NormalFont = GDIRenderer . CreateNormalHFont ( CommonFont , 6 ) ;
2015-03-03 21:10:48 +00:00
// PrepDrawString doesn't actually set the font, so this is rather useless.
// I'm leaving this stuff as-is so it will be a bit easier to fix up with another rendering method.
2016-04-17 08:47:18 +00:00
RotatedFont = GDIRenderer . CreateRotatedHFont ( CommonFont , true ) ;
2014-08-10 22:23:14 +00:00
2014-08-06 01:32:27 +00:00
SetStyle ( ControlStyles . AllPaintingInWmPaint , true ) ;
2014-08-10 18:49:17 +00:00
SetStyle ( ControlStyles . UserPaint , true ) ;
2014-08-07 21:52:22 +00:00
SetStyle ( ControlStyles . SupportsTransparentBackColor , true ) ;
2014-08-08 13:36:37 +00:00
SetStyle ( ControlStyles . Opaque , true ) ;
2014-08-08 13:42:05 +00:00
2014-08-10 18:49:17 +00:00
Gdi = new GDIRenderer ( ) ;
using ( var g = CreateGraphics ( ) )
2014-08-10 22:23:14 +00:00
using ( var LCK = Gdi . LockGraphics ( g ) )
{
2016-04-17 08:47:18 +00:00
_charSize = Gdi . MeasureString ( "A" , CommonFont ) ; // TODO make this a property so changing it updates other values.
2014-08-10 22:23:14 +00:00
}
2014-08-18 21:38:02 +00:00
2014-08-31 23:03:59 +00:00
UpdateCellSize ( ) ;
ColumnWidth = CellWidth ;
2014-09-22 15:33:04 +00:00
ColumnHeight = CellHeight + 2 ;
2014-08-31 23:03:59 +00:00
2014-08-31 17:11:47 +00:00
VBar = new VScrollBar
{
2015-01-03 21:52:33 +00:00
// Location gets calculated later (e.g. on resize)
2014-08-31 17:11:47 +00:00
Visible = false ,
SmallChange = CellHeight ,
LargeChange = CellHeight * 20
} ;
HBar = new HScrollBar
{
2015-01-03 21:52:33 +00:00
// Location gets calculated later (e.g. on resize)
2014-08-31 17:11:47 +00:00
Visible = false ,
2015-03-19 19:55:38 +00:00
SmallChange = CellWidth ,
2014-08-31 17:11:47 +00:00
LargeChange = 20
} ;
2015-09-02 22:23:29 +00:00
Controls . Add ( VBar ) ;
Controls . Add ( HBar ) ;
2014-08-19 00:37:38 +00:00
VBar . ValueChanged + = VerticalBar_ValueChanged ;
HBar . ValueChanged + = HorizontalBar_ValueChanged ;
2014-08-30 18:42:14 +00:00
HorizontalOrientation = false ;
2014-08-18 21:38:02 +00:00
RecalculateScrollBars ( ) ;
2014-08-23 13:14:25 +00:00
_columns . ChangedCallback = ColumnChangedCallback ;
2015-07-25 00:48:02 +00:00
_hoverTimer . Interval = 750 ;
_hoverTimer . Tick + = HoverTimerEventProcessor ;
_hoverTimer . Stop ( ) ;
2015-09-05 18:33:34 +00:00
_foreColor = ForeColor ;
_backColor = BackColor ;
2015-07-25 00:48:02 +00:00
}
private void HoverTimerEventProcessor ( object sender , EventArgs e )
{
_hoverTimer . Stop ( ) ;
if ( CellHovered ! = null )
{
CellHovered ( this , new CellEventArgs ( LastCell , CurrentCell ) ) ;
}
2014-08-06 01:32:27 +00:00
}
2014-08-07 14:55:55 +00:00
2014-08-09 17:15:05 +00:00
protected override void Dispose ( bool disposing )
{
Gdi . Dispose ( ) ;
2015-02-24 06:47:32 +00:00
2016-04-17 08:47:18 +00:00
GDIRenderer . DestroyHFont ( NormalFont ) ;
2014-10-22 07:59:06 +00:00
GDIRenderer . DestroyHFont ( RotatedFont ) ;
2014-08-09 17:15:05 +00:00
base . Dispose ( disposing ) ;
}
2015-02-24 06:47:32 +00:00
2015-07-25 00:48:02 +00:00
private Timer _hoverTimer = new Timer ( ) ;
2014-08-07 14:55:55 +00:00
#region Properties
2014-08-07 18:32:09 +00:00
/// <summary>
2014-09-24 21:10:57 +00:00
/// Gets or sets the amount of left and right padding on the text inside a cell
2014-08-07 18:32:09 +00:00
/// </summary>
[DefaultValue(3)]
2014-08-10 22:23:14 +00:00
[Category("Behavior")]
2014-09-24 21:10:57 +00:00
public int CellWidthPadding { get ; set ; }
/// <summary>
/// Gets or sets the amount of top and bottom padding on the text inside a cell
/// </summary>
[DefaultValue(1)]
[Category("Behavior")]
public int CellHeightPadding { get ; set ; }
2014-08-07 18:32:09 +00:00
2014-08-23 14:30:12 +00:00
/// <summary>
/// Displays grid lines around cells
/// </summary>
[Category("Appearance")]
[DefaultValue(true)]
public bool GridLines { get ; set ; }
2014-08-07 14:55:55 +00:00
/// <summary>
/// Gets or sets whether the control is horizontal or vertical
/// </summary>
[Category("Behavior")]
2015-02-24 06:47:32 +00:00
public bool HorizontalOrientation
{
get
{
2014-08-30 18:42:14 +00:00
return _horizontalOrientation ;
}
set
{
2015-02-24 06:47:32 +00:00
if ( _horizontalOrientation ! = value )
{
2015-07-25 08:20:16 +00:00
int temp = ScrollSpeed ;
2015-02-24 06:47:32 +00:00
_horizontalOrientation = value ;
OrientationChanged ( ) ;
2015-07-25 08:20:16 +00:00
HBar . SmallChange = CellWidth ;
VBar . SmallChange = CellHeight ;
ScrollSpeed = temp ;
2014-08-30 18:42:14 +00:00
}
}
}
2014-08-07 14:55:55 +00:00
2015-09-02 22:23:29 +00:00
/// <summary>
/// Gets or sets the scrolling speed
/// </summary>
[Category("Behavior")]
public int ScrollSpeed
{
get
{
if ( HorizontalOrientation )
return HBar . SmallChange / CellWidth ;
else
return VBar . SmallChange / CellHeight ;
}
set
{
if ( HorizontalOrientation )
HBar . SmallChange = value * CellWidth ;
else
VBar . SmallChange = value * CellHeight ;
}
}
2014-08-07 14:55:55 +00:00
/// <summary>
2014-08-31 17:11:47 +00:00
/// Gets or sets the sets the virtual number of rows to be displayed. Does not include the column header row.
2014-08-07 14:55:55 +00:00
/// </summary>
[Category("Behavior")]
2014-08-30 18:42:14 +00:00
public int RowCount
2014-08-18 21:38:02 +00:00
{
get
{
2014-08-30 18:42:14 +00:00
return _rowCount ;
2014-08-18 21:38:02 +00:00
}
set
{
2014-08-30 18:42:14 +00:00
_rowCount = value ;
2014-08-18 21:38:02 +00:00
RecalculateScrollBars ( ) ;
}
}
2014-08-07 14:55:55 +00:00
/// <summary>
/// Gets or sets the sets the columns can be resized
/// </summary>
[Category("Behavior")]
public bool AllowColumnResize { get ; set ; }
/// <summary>
/// Gets or sets the sets the columns can be reordered
/// </summary>
[Category("Behavior")]
public bool AllowColumnReorder { get ; set ; }
2014-08-11 11:11:51 +00:00
/// <summary>
/// Indicates whether the entire row will always be selected
/// </summary>
[Category("Appearance")]
2014-08-14 23:10:56 +00:00
[DefaultValue(false)]
2014-08-11 11:11:51 +00:00
public bool FullRowSelect { get ; set ; }
2014-08-14 23:10:56 +00:00
/// <summary>
/// Allows multiple items to be selected
/// </summary>
[Category("Behavior")]
[DefaultValue(true)]
public bool MultiSelect { get ; set ; }
2014-08-23 13:05:28 +00:00
/// <summary>
/// Gets or sets whether or not the control is in input painting mode
/// </summary>
[Category("Behavior")]
[DefaultValue(false)]
public bool InputPaintingMode { get ; set ; }
2014-08-23 13:19:48 +00:00
/// <summary>
2014-10-12 16:37:45 +00:00
/// All visible columns
2014-08-23 13:19:48 +00:00
/// </summary>
[Category("Behavior")]
2014-10-12 16:37:45 +00:00
public IEnumerable < RollColumn > VisibleColumns { get { return _columns . VisibleColumns ; } }
2015-03-14 16:38:07 +00:00
/// <summary>
/// Gets or sets how the InputRoll scrolls when calling ScrollToIndex.
/// </summary>
[DefaultValue("near")]
[Category("Behavior")]
public string ScrollMethod { get ; set ; }
2015-07-25 00:48:02 +00:00
/// <summary>
/// Gets or sets how the Intever for the hover event
/// </summary>
2015-03-14 16:38:07 +00:00
[Category("Behavior")]
public bool AlwaysScroll { get ; set ; }
2016-01-27 13:18:20 +00:00
/// <summary>
/// Gets or sets the lowest seek interval to activate the progress bar
/// </summary>
[Category("Behavior")]
public int SeekingCutoffInterval { get ; set ; }
2014-10-12 16:37:45 +00:00
/// <summary>
/// Returns all columns including those that are not visible
/// </summary>
/// <returns></returns>
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-10-12 16:37:45 +00:00
public RollColumns AllColumns { get { return _columns ; } }
2014-08-23 13:19:48 +00:00
2015-07-25 00:48:02 +00:00
[DefaultValue(750)]
[Category("Behavior")]
public int HoverInterval
{
get { return _hoverTimer . Interval ; }
set { _hoverTimer . Interval = value ; }
}
2014-08-07 14:55:55 +00:00
#endregion
#region Event Handlers
/// <summary>
2014-09-03 03:16:16 +00:00
/// Fire the QueryItemText event which requests the text for the passed cell
2014-08-07 14:55:55 +00:00
/// </summary>
2014-08-10 22:23:14 +00:00
[Category("Virtual")]
public event QueryItemTextHandler QueryItemText ;
2014-08-07 14:55:55 +00:00
/// <summary>
2014-09-03 03:16:16 +00:00
/// Fire the QueryItemBkColor event which requests the background color for the passed cell
2014-08-07 14:55:55 +00:00
/// </summary>
2014-08-10 22:23:14 +00:00
[Category("Virtual")]
public event QueryItemBkColorHandler QueryItemBkColor ;
2015-03-19 19:55:38 +00:00
[Category("Virtual")]
public event QueryRowBkColorHandler QueryRowBkColor ;
2014-08-07 14:55:55 +00:00
2014-09-03 03:16:16 +00:00
/// <summary>
/// Fire the QueryItemIconHandler event which requests an icon for a given cell
/// </summary>
[Category("Virtual")]
public event QueryItemIconHandler QueryItemIcon ;
2015-02-24 19:00:12 +00:00
/// <summary>
/// SuuperW: Fire the QueryFrameLag event which checks if a given frame is a lag frame
/// </summary>
[Category("Virtual")]
public event QueryFrameLagHandler QueryFrameLag ;
2014-08-07 14:55:55 +00:00
/// <summary>
2014-08-10 22:23:14 +00:00
/// Fires when the mouse moves from one cell to another (including column header cells)
2014-08-07 14:55:55 +00:00
/// </summary>
2014-08-10 22:23:14 +00:00
[Category("Mouse")]
public event CellChangeEventHandler PointedCellChanged ;
2014-08-07 14:55:55 +00:00
2015-07-25 00:48:02 +00:00
/// <summary>
/// Fires when a cell is hovered on
/// </summary>
[Category("Mouse")]
public event HoverEventHandler CellHovered ;
2014-08-14 23:10:56 +00:00
/// <summary>
/// Occurs when a column header is clicked
/// </summary>
2014-08-11 01:49:45 +00:00
[Category("Action")]
2014-10-14 00:31:59 +00:00
public event ColumnClickEventHandler ColumnClick ;
2014-08-11 01:49:45 +00:00
2014-09-25 17:52:21 +00:00
/// <summary>
/// Occurs when a column header is right-clicked
/// </summary>
[Category("Action")]
2014-10-14 00:31:59 +00:00
public event ColumnClickEventHandler ColumnRightClick ;
2014-09-25 17:52:21 +00:00
2014-08-14 23:10:56 +00:00
/// <summary>
/// Occurs whenever the 'SelectedItems' property for this control changes
/// </summary>
[Category("Behavior")]
2014-10-17 18:29:09 +00:00
public event EventHandler SelectedIndexChanged ;
2014-08-14 23:10:56 +00:00
2014-08-23 13:05:28 +00:00
/// <summary>
/// Occurs whenever the mouse wheel is scrolled while the right mouse button is held
/// </summary>
[Category("Behavior")]
public event RightMouseScrollEventHandler RightMouseScrolled ;
2014-10-19 14:46:01 +00:00
[Category("Property Changed")]
[Description("Occurs when the column header has been reordered")]
public event ColumnReorderedEventHandler ColumnReordered ;
2015-07-14 18:13:21 +00:00
[Category("Action")]
[Description("Occurs when the scroll value of the visible rows change (in vertical orientation this is the vertical scroll bar change, and in horizontal it is the horizontal scroll bar)")]
public event RowScrollEvent RowScroll ;
[Category("Action")]
[Description("Occurs when the scroll value of the columns (in vertical orientation this is the horizontal scroll bar change, and in horizontal it is the vertical scroll bar)")]
public event ColumnScrollEvent ColumnScroll ;
2015-07-29 00:03:03 +00:00
[Category("Action")]
[Description("Occurs when a cell is dragged and then dropped into a new cell, old cell is the cell that was being dragged, new cell is its new destination")]
public event CellDroppedEvent CellDropped ;
2014-08-07 14:55:55 +00:00
/// <summary>
2014-08-10 22:23:14 +00:00
/// Retrieve the text for a cell
2014-08-07 14:55:55 +00:00
/// </summary>
2015-08-24 22:30:45 +00:00
public delegate void QueryItemTextHandler ( int index , RollColumn column , out string text , ref int offsetX , ref int offsetY ) ;
2014-08-10 22:23:14 +00:00
/// <summary>
/// Retrieve the background color for a cell
/// </summary>
2014-10-13 19:30:59 +00:00
public delegate void QueryItemBkColorHandler ( int index , RollColumn column , ref Color color ) ;
2015-03-19 19:55:38 +00:00
public delegate void QueryRowBkColorHandler ( int index , ref Color color ) ;
2014-08-10 22:23:14 +00:00
2014-09-03 03:16:16 +00:00
/// <summary>
/// Retrive the image for a given cell
/// </summary>
2015-08-24 22:30:45 +00:00
public delegate void QueryItemIconHandler ( int index , RollColumn column , ref Bitmap icon , ref int offsetX , ref int offsetY ) ;
2014-09-03 03:16:16 +00:00
2015-02-24 19:00:12 +00:00
/// <summary>
/// SuuperW: Check if a given frame is a lag frame
/// </summary>
2015-03-10 17:11:29 +00:00
public delegate bool QueryFrameLagHandler ( int index , bool hideWasLag ) ;
2015-02-24 19:00:12 +00:00
2014-08-10 22:23:14 +00:00
public delegate void CellChangeEventHandler ( object sender , CellEventArgs e ) ;
2014-08-07 14:55:55 +00:00
2015-07-25 00:48:02 +00:00
public delegate void HoverEventHandler ( object sender , CellEventArgs e ) ;
2014-08-23 13:05:28 +00:00
public delegate void RightMouseScrollEventHandler ( object sender , MouseEventArgs e ) ;
2014-10-14 00:31:59 +00:00
public delegate void ColumnClickEventHandler ( object sender , ColumnClickEventArgs e ) ;
2014-10-19 14:46:01 +00:00
public delegate void ColumnReorderedEventHandler ( object sender , ColumnReorderedEventArgs e ) ;
2015-07-14 18:13:21 +00:00
public delegate void RowScrollEvent ( object sender , EventArgs e ) ;
public delegate void ColumnScrollEvent ( object sender , EventArgs e ) ;
2015-07-29 00:03:03 +00:00
public delegate void CellDroppedEvent ( object sender , CellEventArgs e ) ;
2014-08-10 15:57:59 +00:00
public class CellEventArgs
{
public CellEventArgs ( Cell oldCell , Cell newCell )
{
OldCell = oldCell ;
NewCell = newCell ;
}
public Cell OldCell { get ; private set ; }
public Cell NewCell { get ; private set ; }
}
2014-10-14 00:31:59 +00:00
public class ColumnClickEventArgs
{
public ColumnClickEventArgs ( RollColumn column )
{
Column = column ;
}
public RollColumn Column { get ; private set ; }
}
2014-10-19 14:46:01 +00:00
public class ColumnReorderedEventArgs
{
public ColumnReorderedEventArgs ( int oldDisplayIndex , int newDisplayIndex , RollColumn column )
{
Column = column ;
OldDisplayIndex = oldDisplayIndex ;
NewDisplayIndex = NewDisplayIndex ;
}
public RollColumn Column { get ; private set ; }
public int OldDisplayIndex { get ; private set ; }
public int NewDisplayIndex { get ; private set ; }
}
2014-08-07 14:55:55 +00:00
#endregion
2014-08-11 00:24:38 +00:00
#region Api
2014-08-07 14:55:55 +00:00
2014-08-31 15:40:02 +00:00
public void SelectRow ( int index , bool val )
2014-08-23 14:30:12 +00:00
{
2014-10-12 16:37:45 +00:00
if ( _columns . VisibleColumns . Any ( ) )
2014-08-23 14:30:12 +00:00
{
if ( val )
{
SelectCell ( new Cell
{
RowIndex = index ,
Column = _columns [ 0 ]
} ) ;
}
else
{
2015-07-26 03:42:50 +00:00
IEnumerable < Cell > items = SelectedItems . Where ( cell = > cell . RowIndex = = index ) ;
SelectedItems . RemoveWhere ( items . Contains ) ;
2014-08-23 14:30:12 +00:00
}
}
}
2015-02-27 19:06:02 +00:00
public void SelectAll ( )
{
var oldFullRowVal = FullRowSelect ;
FullRowSelect = true ;
for ( int i = 0 ; i < RowCount ; i + + )
{
SelectRow ( i , true ) ;
}
FullRowSelect = oldFullRowVal ;
}
public void DeselectAll ( )
{
SelectedItems . Clear ( ) ;
}
2015-09-02 22:23:29 +00:00
2015-02-27 19:34:31 +00:00
public void TruncateSelection ( int index )
{
2015-07-26 03:42:50 +00:00
SelectedItems . RemoveWhere ( cell = > cell . RowIndex > index ) ;
2015-02-27 19:34:31 +00:00
}
2015-02-27 19:06:02 +00:00
2014-08-23 16:00:56 +00:00
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-10-19 00:24:33 +00:00
public bool IsPointingAtColumnHeader
{
get
{
return IsHoveringOnColumnCell ;
}
}
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-08-23 16:00:56 +00:00
public int? FirstSelectedIndex
{
get
{
2015-07-26 03:42:50 +00:00
if ( AnyRowsSelected )
2014-08-23 16:00:56 +00:00
{
2015-07-26 03:42:50 +00:00
return SelectedRows . Min ( ) ;
2014-08-23 16:00:56 +00:00
}
return null ;
}
}
2014-08-23 13:50:47 +00:00
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-08-23 13:50:47 +00:00
public int? LastSelectedIndex
{
get
{
2015-07-26 03:42:50 +00:00
if ( AnyRowsSelected )
2014-08-23 13:50:47 +00:00
{
2015-07-26 03:42:50 +00:00
return SelectedRows . Max ( ) ;
2014-08-23 13:50:47 +00:00
}
return null ;
}
}
2014-08-30 18:42:14 +00:00
/// <summary>
/// The current Cell that the mouse was in.
/// </summary>
2014-08-11 00:24:38 +00:00
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-08-10 15:57:59 +00:00
public Cell CurrentCell { get ; set ; }
2015-07-25 16:01:26 +00:00
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2015-07-25 16:01:26 +00:00
public bool CurrentCellIsDataCell
{
get
{
return CurrentCell ! = null & & CurrentCell . RowIndex . HasValue & & CurrentCell . Column ! = null ;
}
}
2014-08-30 18:42:14 +00:00
/// <summary>
/// The previous Cell that the mouse was in.
/// </summary>
2014-08-23 13:05:28 +00:00
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public Cell LastCell { get ; set ; }
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public bool IsPaintDown { get ; set ; }
2014-08-11 00:24:38 +00:00
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public bool UseCustomBackground { get ; set ; }
2014-08-31 15:40:02 +00:00
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
2015-02-24 06:47:32 +00:00
public int DrawHeight { get ; private set ; }
2014-08-31 15:40:02 +00:00
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public int DrawWidth { get ; private set ; }
2014-09-01 15:35:48 +00:00
/// <summary>
/// Sets the width of data cells when in Horizontal orientation.
/// </summary>
2014-10-15 16:50:51 +00:00
public int MaxCharactersInHorizontal
{
2014-09-01 15:35:48 +00:00
get
{
return _maxCharactersInHorizontal ;
2015-02-24 06:47:32 +00:00
}
2015-09-02 22:23:29 +00:00
2014-09-01 15:35:48 +00:00
set
{
_maxCharactersInHorizontal = value ;
UpdateCellSize ( ) ;
}
}
2014-08-23 13:05:28 +00:00
[Browsable(false)]
[DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Hidden)]
public bool RightButtonHeld { get ; set ; }
2014-08-07 14:55:55 +00:00
public string UserSettingsSerialized ( )
{
2014-10-18 20:29:23 +00:00
var settings = ConfigService . SaveWithType ( Settings ) ;
2014-10-14 13:17:02 +00:00
return settings ;
}
public void LoadSettingsSerialized ( string settingsJson )
{
var settings = ConfigService . LoadWithType ( settingsJson ) ;
2014-10-18 20:29:23 +00:00
// TODO: don't silently fail, inform the user somehow
if ( settings is InputRollSettings )
{
2015-02-24 20:38:46 +00:00
var rollSettings = settings as InputRollSettings ;
_columns = rollSettings . Columns ;
2016-11-17 18:30:42 +00:00
_columns . ChangedCallback = ColumnChangedCallback ;
2015-02-24 20:38:46 +00:00
HorizontalOrientation = rollSettings . HorizontalOrientation ;
LagFramesToHide = rollSettings . LagFramesToHide ;
2015-03-10 17:11:29 +00:00
HideWasLagFrames = rollSettings . HideWasLagFrames ;
2014-10-18 20:29:23 +00:00
}
}
private InputRollSettings Settings
{
get
{
return new InputRollSettings
{
Columns = _columns ,
2015-02-24 20:38:46 +00:00
HorizontalOrientation = HorizontalOrientation ,
2015-03-10 17:11:29 +00:00
LagFramesToHide = LagFramesToHide ,
HideWasLagFrames = HideWasLagFrames
2014-10-18 20:29:23 +00:00
} ;
}
}
public class InputRollSettings
{
public RollColumns Columns { get ; set ; }
public bool HorizontalOrientation { get ; set ; }
2015-02-24 20:38:46 +00:00
public int LagFramesToHide { get ; set ; }
2015-03-10 17:11:29 +00:00
public bool HideWasLagFrames { get ; set ; }
2014-08-07 14:55:55 +00:00
}
2014-08-23 13:50:47 +00:00
/// <summary>
2014-08-30 18:42:14 +00:00
/// Gets or sets the first visible row index, if scrolling is needed
2014-08-23 13:50:47 +00:00
/// </summary>
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-08-30 18:42:14 +00:00
public int FirstVisibleRow
2014-08-23 13:50:47 +00:00
{
2015-02-25 21:17:50 +00:00
get // SuuperW: This was checking if the scroll bars were needed, which is useless because their Value is 0 if they aren't needed.
2014-08-23 13:50:47 +00:00
{
if ( HorizontalOrientation )
{
2015-02-25 21:17:50 +00:00
return HBar . Value / CellWidth ;
2014-08-23 13:50:47 +00:00
}
2015-02-25 21:17:50 +00:00
return VBar . Value / CellHeight ;
2014-08-23 13:50:47 +00:00
}
set
{
if ( HorizontalOrientation )
{
if ( NeedsHScrollbar )
{
2014-08-27 22:33:27 +00:00
_programmaticallyUpdatingScrollBarValues = true ;
2015-03-14 16:38:07 +00:00
if ( value * CellWidth < = HBar . Maximum )
HBar . Value = value * CellWidth ;
else
HBar . Value = HBar . Maximum ;
2014-08-27 22:33:27 +00:00
_programmaticallyUpdatingScrollBarValues = false ;
2014-08-23 13:50:47 +00:00
}
}
2014-09-01 20:34:10 +00:00
else
2014-08-23 13:50:47 +00:00
{
2014-09-01 20:34:10 +00:00
if ( NeedsVScrollbar )
{
_programmaticallyUpdatingScrollBarValues = true ;
2015-03-14 16:38:07 +00:00
if ( value * CellHeight < = VBar . Maximum )
VBar . Value = value * CellHeight ;
else
VBar . Value = VBar . Maximum ;
2014-09-01 20:34:10 +00:00
_programmaticallyUpdatingScrollBarValues = false ;
}
2014-08-23 13:50:47 +00:00
}
}
}
2015-03-03 06:56:45 +00:00
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2015-02-26 21:12:12 +00:00
private int LastFullyVisibleRow
{
get
{
int HalfRow = 0 ;
if ( ( DrawHeight - ColumnHeight - 3 ) % CellHeight < CellHeight / 2 )
HalfRow = 1 ;
2015-02-27 19:06:02 +00:00
return FirstVisibleRow + VisibleRows - HalfRow + CountLagFramesDisplay ( VisibleRows - HalfRow ) ;
2015-02-26 21:12:12 +00:00
}
}
2015-03-03 06:56:45 +00:00
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-08-30 18:42:14 +00:00
public int LastVisibleRow
2014-08-23 14:49:01 +00:00
{
get
{
2015-02-24 19:00:12 +00:00
return FirstVisibleRow + VisibleRows + CountLagFramesDisplay ( VisibleRows ) ;
2014-08-23 14:49:01 +00:00
}
set
{
2015-02-26 21:12:12 +00:00
int HalfRow = 0 ;
if ( ( DrawHeight - ColumnHeight - 3 ) % CellHeight < CellHeight / 2 )
HalfRow = 1 ;
if ( LagFramesToHide = = 0 )
{
FirstVisibleRow = Math . Max ( value - ( VisibleRows - HalfRow ) , 0 ) ;
}
else
2015-02-25 21:17:50 +00:00
{
2015-02-27 19:06:02 +00:00
if ( Math . Abs ( LastFullyVisibleRow - value ) > VisibleRows ) // Big jump
2015-02-27 19:34:31 +00:00
{
2015-02-26 21:12:12 +00:00
FirstVisibleRow = Math . Max ( value - ( ExpectedDisplayRange ( ) - HalfRow ) , 0 ) ;
2015-02-27 19:34:31 +00:00
SetLagFramesArray ( ) ;
}
2015-02-27 19:06:02 +00:00
// Small jump, more accurate
2015-02-27 19:34:31 +00:00
int lastVisible = LastFullyVisibleRow ;
2015-02-27 19:06:02 +00:00
do
{
if ( ( lastVisible - value ) / ( LagFramesToHide + 1 ) ! = 0 )
FirstVisibleRow = Math . Max ( FirstVisibleRow - ( ( lastVisible - value ) / ( LagFramesToHide + 1 ) ) , 0 ) ;
else
FirstVisibleRow - = Math . Sign ( lastVisible - value ) ;
2015-02-27 19:34:31 +00:00
SetLagFramesArray ( ) ;
lastVisible = LastFullyVisibleRow ;
2015-03-01 05:47:32 +00:00
} while ( ( lastVisible - value < 0 | | lastVisible - value > lagFrames [ VisibleRows - HalfRow ] ) & & FirstVisibleRow ! = 0 ) ;
2015-02-25 21:17:50 +00:00
}
2014-08-23 14:49:01 +00:00
}
}
2014-09-17 23:51:16 +00:00
public bool IsVisible ( int index )
{
2015-02-26 21:12:12 +00:00
return ( index > = FirstVisibleRow ) & & ( index < = LastFullyVisibleRow ) ;
2014-09-17 23:51:16 +00:00
}
2015-03-19 19:55:38 +00:00
public bool IsPartiallyVisible ( int index )
2015-10-25 17:24:33 +00:00
{
return ( index > = FirstVisibleRow ) & & ( index < = LastVisibleRow ) ;
}
2014-09-17 23:51:16 +00:00
2014-08-23 13:50:47 +00:00
/// <summary>
2014-09-01 20:34:10 +00:00
/// Gets the number of rows currently visible including partially visible rows.
2014-08-23 13:50:47 +00:00
/// </summary>
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-08-23 13:50:47 +00:00
public int VisibleRows
{
get
{
if ( HorizontalOrientation )
{
2015-02-25 21:17:50 +00:00
return ( DrawWidth - ColumnWidth ) / CellWidth ;
2014-08-23 13:50:47 +00:00
}
2014-09-17 23:31:59 +00:00
2015-02-26 21:12:12 +00:00
return ( DrawHeight - ColumnHeight - 3 ) / CellHeight ; // Minus three makes it work
2014-08-23 13:50:47 +00:00
}
}
2015-03-03 06:56:45 +00:00
/// <summary>
/// Gets the first visible column index, if scrolling is needed
/// </summary>
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2015-03-03 06:56:45 +00:00
public int FirstVisibleColumn
{
get
{
if ( HorizontalOrientation )
2015-09-02 22:23:29 +00:00
{
2015-03-03 06:56:45 +00:00
return VBar . Value / CellHeight ;
2015-09-02 22:23:29 +00:00
}
2015-03-03 06:56:45 +00:00
else
2015-09-02 22:23:29 +00:00
{
2015-09-05 20:37:52 +00:00
List < RollColumn > columnList = VisibleColumns . ToList ( ) ;
2015-03-19 19:55:38 +00:00
return columnList . FindIndex ( c = > c . Right > HBar . Value ) ;
2015-09-02 22:23:29 +00:00
}
2015-03-03 06:56:45 +00:00
}
}
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2015-03-03 06:56:45 +00:00
public int LastVisibleColumnIndex
{
get
{
List < RollColumn > columnList = VisibleColumns . ToList ( ) ;
int ret ;
if ( HorizontalOrientation )
{
ret = ( VBar . Value + DrawHeight ) / CellHeight ;
if ( ret > = columnList . Count )
ret = columnList . Count - 1 ;
}
else
2015-03-16 20:42:14 +00:00
ret = columnList . FindLastIndex ( c = > c . Left < = DrawWidth + HBar . Value ) ;
2015-03-03 06:56:45 +00:00
return ret ;
}
}
2015-07-29 00:03:03 +00:00
private Cell DraggingCell = null ;
public void DragCurrentCell ( )
{
DraggingCell = CurrentCell ;
}
public void ReleaseCurrentCell ( )
{
if ( DraggingCell ! = null )
{
var draggedCell = DraggingCell ;
DraggingCell = null ;
if ( CurrentCell ! = draggedCell )
{
if ( CellDropped ! = null )
{
CellDropped ( this , new CellEventArgs ( draggedCell , CurrentCell ) ) ;
}
}
}
}
2015-07-26 00:55:52 +00:00
/// <summary>
/// Scrolls to the given index, according to the scroll settings.
/// </summary>
2015-03-14 16:38:07 +00:00
public void ScrollToIndex ( int index )
{
2015-07-26 00:55:52 +00:00
if ( ScrollMethod = = "near" )
2015-03-14 16:38:07 +00:00
{
2015-07-26 00:55:52 +00:00
MakeIndexVisible ( index ) ;
2015-03-14 16:38:07 +00:00
}
if ( ! IsVisible ( index ) | | AlwaysScroll )
{
if ( ScrollMethod = = "top" )
FirstVisibleRow = index ;
else if ( ScrollMethod = = "bottom" )
LastVisibleRow = index ;
else if ( ScrollMethod = = "center" )
{
if ( LagFramesToHide = = 0 )
FirstVisibleRow = Math . Max ( index - ( VisibleRows / 2 ) , 0 ) ;
else
{
if ( Math . Abs ( FirstVisibleRow + CountLagFramesDisplay ( VisibleRows / 2 ) - index ) > VisibleRows ) // Big jump
{
FirstVisibleRow = Math . Max ( index - ( ExpectedDisplayRange ( ) / 2 ) , 0 ) ;
SetLagFramesArray ( ) ;
}
// Small jump, more accurate
int lastVisible = FirstVisibleRow + CountLagFramesDisplay ( VisibleRows / 2 ) ;
do
{
if ( ( lastVisible - index ) / ( LagFramesToHide + 1 ) ! = 0 )
FirstVisibleRow = Math . Max ( FirstVisibleRow - ( ( lastVisible - index ) / ( LagFramesToHide + 1 ) ) , 0 ) ;
else
FirstVisibleRow - = Math . Sign ( lastVisible - index ) ;
SetLagFramesArray ( ) ;
lastVisible = FirstVisibleRow + CountLagFramesDisplay ( VisibleRows / 2 ) ;
} while ( ( lastVisible - index < 0 | | lastVisible - index > lagFrames [ VisibleRows ] ) & & FirstVisibleRow ! = 0 ) ;
}
}
}
}
2015-07-26 00:55:52 +00:00
/// <summary>
/// Scrolls so that the given index is visible, if it isn't already; doesn't use scroll settings.
/// </summary>
public void MakeIndexVisible ( int index )
{
if ( ! IsVisible ( index ) )
{
if ( FirstVisibleRow > index )
FirstVisibleRow = index ;
else
LastVisibleRow = index ;
}
}
2015-03-14 16:38:07 +00:00
2014-08-12 11:07:21 +00:00
[Browsable(false)]
2015-09-02 22:23:29 +00:00
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
2014-10-13 18:28:29 +00:00
public IEnumerable < int > SelectedRows
2014-08-12 11:07:21 +00:00
{
get
{
return SelectedItems
. Where ( cell = > cell . RowIndex . HasValue )
2014-08-14 23:10:56 +00:00
. Select ( cell = > cell . RowIndex . Value )
2014-10-13 18:28:29 +00:00
. Distinct ( ) ;
2014-08-12 11:07:21 +00:00
}
}
2015-07-26 03:42:50 +00:00
public bool AnyRowsSelected
{
get
{
return SelectedItems . Any ( cell = > cell . RowIndex . HasValue ) ;
}
}
2014-08-12 11:07:21 +00:00
2015-07-26 00:55:23 +00:00
public void ClearSelectedRows ( )
{
SelectedItems . Clear ( ) ;
}
2014-10-15 16:50:51 +00:00
public IEnumerable < ToolStripItem > GenerateContextMenuItems ( )
{
yield return new ToolStripSeparator ( ) ;
var rotate = new ToolStripMenuItem
{
Name = "RotateMenuItem" ,
Text = "Rotate" ,
2014-10-15 17:07:01 +00:00
ShortcutKeyDisplayString = RotateHotkeyStr ,
2014-10-15 16:50:51 +00:00
} ;
rotate . Click + = ( o , ev ) = >
{
2015-09-02 22:23:29 +00:00
HorizontalOrientation ^ = true ;
2014-10-15 16:50:51 +00:00
} ;
yield return rotate ;
}
2014-10-15 17:07:01 +00:00
public string RotateHotkeyStr
{
2015-02-24 06:47:32 +00:00
get { return "Ctrl+Shift+F" ; }
2014-10-15 17:07:01 +00:00
}
2014-08-07 14:55:55 +00:00
#endregion
#region Mouse and Key Events
2015-07-18 15:07:36 +00:00
private bool _columnDownMoved = false ;
2014-08-10 22:23:14 +00:00
protected override void OnMouseMove ( MouseEventArgs e )
{
2014-10-19 14:04:59 +00:00
_currentX = e . X ;
_currentY = e . Y ;
2015-07-18 15:07:36 +00:00
if ( _columnDown ! = null )
{
_columnDownMoved = true ;
}
2015-07-26 00:55:52 +00:00
Cell newCell = CalculatePointedCell ( _currentX . Value , _currentY . Value ) ;
2015-02-24 19:00:12 +00:00
// SuuperW: Hide lag frames
if ( QueryFrameLag ! = null & & newCell . RowIndex . HasValue )
{
newCell . RowIndex + = CountLagFramesDisplay ( newCell . RowIndex . Value ) ;
}
2014-09-01 15:35:48 +00:00
newCell . RowIndex + = FirstVisibleRow ;
2015-07-26 00:55:52 +00:00
if ( newCell . RowIndex < 0 )
newCell . RowIndex = 0 ;
2014-08-30 18:42:14 +00:00
if ( ! newCell . Equals ( CurrentCell ) )
{
CellChanged ( newCell ) ;
if ( IsHoveringOnColumnCell | |
( WasHoveringOnColumnCell & & ! IsHoveringOnColumnCell ) )
{
Refresh ( ) ;
}
2014-10-19 14:04:59 +00:00
else if ( _columnDown ! = null )
{
Refresh ( ) ;
}
}
else if ( _columnDown ! = null ) // Kind of silly feeling to have this check twice, but the only alternative I can think of has it refreshing twice when pointed column changes with column down, and speed matters
{
Refresh ( ) ;
2014-08-30 18:42:14 +00:00
}
2014-10-17 18:29:09 +00:00
2014-08-10 22:23:14 +00:00
base . OnMouseMove ( e ) ;
}
protected override void OnMouseEnter ( EventArgs e )
{
CurrentCell = new Cell
{
Column = null ,
RowIndex = null
} ;
base . OnMouseEnter ( e ) ;
}
protected override void OnMouseLeave ( EventArgs e )
{
2014-10-19 14:04:59 +00:00
_currentX = null ;
_currentY = null ;
2014-08-10 22:23:14 +00:00
CurrentCell = null ;
2014-08-23 13:05:28 +00:00
IsPaintDown = false ;
2015-07-25 00:48:02 +00:00
_hoverTimer . Stop ( ) ;
2014-08-11 01:23:53 +00:00
Refresh ( ) ;
2014-08-10 22:23:14 +00:00
base . OnMouseLeave ( e ) ;
}
2014-10-17 18:29:09 +00:00
// TODO add query callback of whether to select the cell or not
2014-08-29 17:42:07 +00:00
protected override void OnMouseDown ( MouseEventArgs e )
2014-08-11 01:49:45 +00:00
{
2016-09-08 19:22:04 +00:00
if ( ! GlobalWin . MainForm . EmulatorPaused & & _currentX . HasValue )
{
// copypaste from OnMouseMove()
Cell newCell = CalculatePointedCell ( _currentX . Value , _currentY . Value ) ;
if ( QueryFrameLag ! = null & & newCell . RowIndex . HasValue )
{
newCell . RowIndex + = CountLagFramesDisplay ( newCell . RowIndex . Value ) ;
}
newCell . RowIndex + = FirstVisibleRow ;
if ( newCell . RowIndex < 0 )
newCell . RowIndex = 0 ;
if ( ! newCell . Equals ( CurrentCell ) )
{
CellChanged ( newCell ) ;
if ( IsHoveringOnColumnCell | |
( WasHoveringOnColumnCell & & ! IsHoveringOnColumnCell ) )
{
Refresh ( ) ;
}
else if ( _columnDown ! = null )
{
Refresh ( ) ;
}
}
else if ( _columnDown ! = null )
{
Refresh ( ) ;
}
}
2014-10-19 14:04:59 +00:00
if ( e . Button = = MouseButtons . Left )
2014-08-11 01:49:45 +00:00
{
2014-10-19 14:04:59 +00:00
if ( IsHoveringOnColumnCell )
{
_columnDown = CurrentCell . Column ;
}
else if ( InputPaintingMode )
{
IsPaintDown = true ;
}
2014-08-11 01:49:45 +00:00
}
2014-08-29 17:42:07 +00:00
if ( e . Button = = MouseButtons . Right )
2014-08-12 11:07:21 +00:00
{
2014-10-19 00:19:17 +00:00
if ( ! IsHoveringOnColumnCell )
2014-09-25 17:52:21 +00:00
{
RightButtonHeld = true ;
}
2014-08-29 17:42:07 +00:00
}
if ( e . Button = = MouseButtons . Left )
{
2014-10-19 00:16:58 +00:00
if ( IsHoveringOnDataCell )
2014-08-12 11:07:21 +00:00
{
2014-09-01 15:35:48 +00:00
if ( ModifierKeys = = Keys . Alt )
2014-08-14 22:48:59 +00:00
{
2015-08-06 17:24:56 +00:00
// do marker drag here
2014-09-01 15:35:48 +00:00
}
else if ( ModifierKeys = = Keys . Shift )
{
if ( SelectedItems . Any ( ) )
{
2014-09-21 16:21:10 +00:00
if ( FullRowSelect )
{
var selected = SelectedItems . Any ( c = > c . RowIndex . HasValue & & CurrentCell . RowIndex . HasValue & & c . RowIndex = = CurrentCell . RowIndex ) ;
if ( ! selected )
{
var rowIndices = SelectedItems
. Where ( c = > c . RowIndex . HasValue )
. Select ( c = > c . RowIndex ? ? - 1 )
. Where ( c = > c > = 0 ) // Hack to avoid possible Nullable exceptions
2014-10-17 18:29:09 +00:00
. Distinct ( )
. ToList ( ) ;
2014-09-21 16:21:10 +00:00
var firstIndex = rowIndices . Min ( ) ;
var lastIndex = rowIndices . Max ( ) ;
if ( CurrentCell . RowIndex . Value < firstIndex )
{
for ( int i = CurrentCell . RowIndex . Value ; i < firstIndex ; i + + )
{
SelectCell ( new Cell
{
RowIndex = i ,
Column = CurrentCell . Column
} ) ;
}
}
else if ( CurrentCell . RowIndex . Value > lastIndex )
{
for ( int i = lastIndex + 1 ; i < = CurrentCell . RowIndex . Value ; i + + )
{
SelectCell ( new Cell
{
RowIndex = i ,
Column = CurrentCell . Column
} ) ;
}
}
else // Somewhere in between, a scenario that can happen with ctrl-clicking, find the previous and highlight from there
{
var nearest = rowIndices
. Where ( x = > x < CurrentCell . RowIndex . Value )
. Max ( ) ;
for ( int i = nearest + 1 ; i < = CurrentCell . RowIndex . Value ; i + + )
{
SelectCell ( new Cell
{
RowIndex = i ,
Column = CurrentCell . Column
} ) ;
}
}
}
}
else
{
MessageBox . Show ( "Shift click logic for individual cells has not yet implemented" ) ;
}
2014-09-01 15:35:48 +00:00
}
else
{
SelectCell ( CurrentCell ) ;
}
}
else if ( ModifierKeys = = Keys . Control )
{
2014-09-21 16:00:42 +00:00
SelectCell ( CurrentCell , toggle : true ) ;
2014-08-14 22:48:59 +00:00
}
else
{
2014-10-25 14:10:32 +00:00
var hadIndex = SelectedItems . Any ( ) ;
2014-09-01 15:35:48 +00:00
SelectedItems . Clear ( ) ;
2014-08-14 23:10:56 +00:00
SelectCell ( CurrentCell ) ;
2014-08-14 22:48:59 +00:00
}
2014-08-30 18:42:14 +00:00
2014-09-01 15:35:48 +00:00
Refresh ( ) ;
2017-01-19 18:55:18 +00:00
if ( SelectedIndexChanged ! = null )
{
SelectedIndexChanged ( this , new EventArgs ( ) ) ;
}
2014-09-01 15:35:48 +00:00
}
2014-08-23 13:05:28 +00:00
}
base . OnMouseDown ( e ) ;
2015-12-03 18:16:10 +00:00
if ( allowRightClickSelecton & & e . Button = = MouseButtons . Right )
{
if ( ! IsHoveringOnColumnCell )
{
_currentX = e . X ;
_currentY = e . Y ;
Cell newCell = CalculatePointedCell ( _currentX . Value , _currentY . Value ) ;
2016-09-25 18:47:37 +00:00
newCell . RowIndex + = FirstVisibleRow ;
2015-12-03 18:16:10 +00:00
CellChanged ( newCell ) ;
SelectCell ( CurrentCell ) ;
}
}
2014-08-23 13:05:28 +00:00
}
protected override void OnMouseUp ( MouseEventArgs e )
{
2014-10-19 00:19:17 +00:00
if ( IsHoveringOnColumnCell )
2014-10-19 00:16:58 +00:00
{
2015-07-18 15:07:36 +00:00
if ( _columnDown ! = null & & _columnDownMoved )
2014-10-19 14:04:59 +00:00
{
DoColumnReorder ( ) ;
_columnDown = null ;
Refresh ( ) ;
}
else if ( e . Button = = MouseButtons . Left )
2014-10-19 00:19:17 +00:00
{
ColumnClickEvent ( ColumnAtX ( e . X ) ) ;
}
else if ( e . Button = = MouseButtons . Right )
{
ColumnRightClickEvent ( ColumnAtX ( e . X ) ) ;
}
2014-10-19 00:16:58 +00:00
}
2014-08-23 13:05:28 +00:00
2014-10-19 14:04:59 +00:00
_columnDown = null ;
2015-07-18 15:07:36 +00:00
_columnDownMoved = false ;
2014-10-19 14:04:59 +00:00
RightButtonHeld = false ;
IsPaintDown = false ;
2014-08-23 13:05:28 +00:00
base . OnMouseUp ( e ) ;
2014-10-19 00:16:58 +00:00
}
2014-08-23 13:05:28 +00:00
2014-08-29 15:53:59 +00:00
private void IncrementScrollBar ( ScrollBar bar , bool increment )
2014-08-31 23:03:59 +00:00
{
2014-08-29 15:53:59 +00:00
int newVal ;
if ( increment )
{
newVal = bar . Value + bar . SmallChange ;
2015-03-16 20:42:14 +00:00
if ( newVal > bar . Maximum - bar . LargeChange )
2014-08-29 15:53:59 +00:00
{
2015-03-16 20:42:14 +00:00
newVal = bar . Maximum - bar . LargeChange ;
2014-08-31 23:03:59 +00:00
}
2014-08-29 15:53:59 +00:00
}
else
{
newVal = bar . Value - bar . SmallChange ;
if ( newVal < 0 )
2014-08-31 23:03:59 +00:00
{
2014-08-29 15:53:59 +00:00
newVal = 0 ;
}
}
_programmaticallyUpdatingScrollBarValues = true ;
bar . Value = newVal ;
_programmaticallyUpdatingScrollBarValues = false ;
}
2014-08-23 13:05:28 +00:00
protected override void OnMouseWheel ( MouseEventArgs e )
{
if ( RightButtonHeld )
{
DoRightMouseScroll ( this , e ) ;
}
else
{
2014-08-29 15:53:59 +00:00
if ( HorizontalOrientation )
{
2015-02-24 19:00:12 +00:00
do
{
IncrementScrollBar ( HBar , e . Delta < 0 ) ;
SetLagFramesFirst ( ) ;
} while ( lagFrames [ 0 ] ! = 0 & & HBar . Value ! = 0 & & HBar . Value ! = HBar . Maximum ) ;
2014-08-29 15:53:59 +00:00
}
else
{
2015-02-24 19:00:12 +00:00
do
{
IncrementScrollBar ( VBar , e . Delta < 0 ) ;
SetLagFramesFirst ( ) ;
} while ( lagFrames [ 0 ] ! = 0 & & VBar . Value ! = 0 & & VBar . Value ! = VBar . Maximum ) ;
2014-08-29 15:53:59 +00:00
}
2015-03-15 06:26:57 +00:00
if ( _currentX ! = null )
OnMouseMove ( new MouseEventArgs ( System . Windows . Forms . MouseButtons . None , 0 , _currentX . Value , _currentY . Value , 0 ) ) ;
2014-08-29 15:53:59 +00:00
Refresh ( ) ;
2014-08-23 13:05:28 +00:00
}
}
2014-08-30 18:42:14 +00:00
private void DoRightMouseScroll ( object sender , MouseEventArgs e )
{
if ( RightMouseScrolled ! = null )
{
RightMouseScrolled ( sender , e ) ;
}
}
private void ColumnClickEvent ( RollColumn column )
{
if ( ColumnClick ! = null )
{
2014-10-14 00:31:59 +00:00
ColumnClick ( this , new ColumnClickEventArgs ( column ) ) ;
2014-08-30 18:42:14 +00:00
}
}
2014-09-25 17:52:21 +00:00
private void ColumnRightClickEvent ( RollColumn column )
{
if ( ColumnRightClick ! = null )
{
2014-10-14 00:31:59 +00:00
ColumnRightClick ( this , new ColumnClickEventArgs ( column ) ) ;
2014-09-25 17:52:21 +00:00
}
}
2014-10-15 16:50:51 +00:00
protected override void OnKeyDown ( KeyEventArgs e )
{
2016-05-07 12:04:42 +00:00
if ( ! suspendHotkeys )
2014-10-15 16:50:51 +00:00
{
2016-05-07 12:04:42 +00:00
if ( e . Control & & ! e . Alt & & e . Shift & & e . KeyCode = = Keys . F ) // Ctrl+Shift+F
2014-10-22 23:03:17 +00:00
{
2016-05-07 12:04:42 +00:00
HorizontalOrientation ^ = true ;
2014-10-22 23:03:17 +00:00
}
2016-05-07 12:04:42 +00:00
else if ( ! e . Control & & ! e . Alt & & ! e . Shift & & e . KeyCode = = Keys . PageUp ) // Page Up
2014-10-22 23:03:17 +00:00
{
2016-05-07 12:04:42 +00:00
if ( FirstVisibleRow > 0 )
2014-10-22 23:03:17 +00:00
{
2016-05-07 12:04:42 +00:00
LastVisibleRow = FirstVisibleRow ;
Refresh ( ) ;
2014-10-22 23:03:17 +00:00
}
2016-05-07 12:04:42 +00:00
}
else if ( ! e . Control & & ! e . Alt & & ! e . Shift & & e . KeyCode = = Keys . PageDown ) // Page Down
{
var totalRows = LastVisibleRow - FirstVisibleRow ;
if ( totalRows < = RowCount )
{
var final = LastVisibleRow + totalRows ;
if ( final > RowCount )
{
final = RowCount ;
}
2014-10-22 23:03:17 +00:00
2016-05-07 12:04:42 +00:00
LastVisibleRow = final ;
Refresh ( ) ;
}
}
else if ( ! e . Control & & ! e . Alt & & ! e . Shift & & e . KeyCode = = Keys . Home ) // Home
{
FirstVisibleRow = 0 ;
2014-10-22 23:03:17 +00:00
Refresh ( ) ;
}
2016-05-07 12:04:42 +00:00
else if ( ! e . Control & & ! e . Alt & & ! e . Shift & & e . KeyCode = = Keys . End ) // End
{
LastVisibleRow = RowCount ;
Refresh ( ) ;
}
else if ( e . Control & & ! e . Shift & & ! e . Alt & & e . KeyCode = = Keys . Up ) // Ctrl + Up
2015-12-03 20:06:28 +00:00
{
2016-05-07 12:04:42 +00:00
if ( SelectedRows . Any ( ) & & letKeysModifySelection )
2015-12-03 20:06:28 +00:00
{
2016-05-07 12:04:42 +00:00
foreach ( var row in SelectedRows . ToList ( ) )
{
SelectRow ( row - 1 , true ) ;
SelectRow ( row , false ) ;
}
2015-12-03 20:06:28 +00:00
}
}
2016-05-07 12:04:42 +00:00
else if ( e . Control & & ! e . Shift & & ! e . Alt & & e . KeyCode = = Keys . Down ) // Ctrl + Down
2015-12-03 20:06:28 +00:00
{
2016-05-07 12:04:42 +00:00
if ( SelectedRows . Any ( ) & & letKeysModifySelection )
2015-12-03 20:06:28 +00:00
{
2016-05-07 12:04:42 +00:00
foreach ( var row in SelectedRows . Reverse ( ) . ToList ( ) )
{
SelectRow ( row + 1 , true ) ;
SelectRow ( row , false ) ;
}
2015-12-03 20:06:28 +00:00
}
}
2016-05-07 12:04:42 +00:00
else if ( ! e . Control & & e . Shift & & ! e . Alt & & e . KeyCode = = Keys . Up ) // Shift + Up
2015-12-03 20:06:28 +00:00
{
2016-05-07 12:04:42 +00:00
if ( SelectedRows . Any ( ) & & letKeysModifySelection )
{
SelectRow ( SelectedRows . First ( ) - 1 , true ) ;
}
2015-12-03 20:06:28 +00:00
}
2016-05-07 12:04:42 +00:00
else if ( ! e . Control & & e . Shift & & ! e . Alt & & e . KeyCode = = Keys . Down ) // Shift + Down
2015-12-03 20:06:28 +00:00
{
2016-05-07 12:04:42 +00:00
if ( SelectedRows . Any ( ) & & letKeysModifySelection )
{
SelectRow ( SelectedRows . Last ( ) + 1 , true ) ;
}
2015-12-03 20:06:28 +00:00
}
2016-05-07 12:04:42 +00:00
else if ( ! e . Control & & ! e . Shift & & ! e . Alt & & e . KeyCode = = Keys . Up ) // Up
2015-12-03 20:06:28 +00:00
{
2016-05-07 12:04:42 +00:00
if ( FirstVisibleRow > 0 )
{
FirstVisibleRow - - ;
Refresh ( ) ;
}
2015-12-03 20:06:28 +00:00
}
2016-05-07 12:04:42 +00:00
else if ( ! e . Control & & ! e . Shift & & ! e . Alt & & e . KeyCode = = Keys . Down ) // Down
2015-12-03 20:06:28 +00:00
{
2016-05-07 12:04:42 +00:00
if ( FirstVisibleRow < RowCount - 1 )
{
FirstVisibleRow + + ;
Refresh ( ) ;
}
2015-12-03 20:06:28 +00:00
}
}
2014-10-15 16:50:51 +00:00
base . OnKeyDown ( e ) ;
}
2014-08-10 22:23:14 +00:00
#endregion
2014-08-18 21:38:02 +00:00
#region Change Events
protected override void OnResize ( EventArgs e )
{
RecalculateScrollBars ( ) ;
base . OnResize ( e ) ;
Refresh ( ) ;
}
2014-08-30 18:42:14 +00:00
private void OrientationChanged ( )
2014-08-23 13:05:28 +00:00
{
2014-09-01 20:34:10 +00:00
RecalculateScrollBars ( ) ;
2014-10-17 18:29:09 +00:00
// TODO scroll to correct positions
2014-09-01 20:34:10 +00:00
2014-10-15 22:14:44 +00:00
ColumnChangedCallback ( ) ;
2014-09-01 20:34:10 +00:00
RecalculateScrollBars ( ) ;
2014-09-01 15:35:48 +00:00
Refresh ( ) ;
2014-08-30 18:42:14 +00:00
}
2014-08-31 15:40:02 +00:00
2014-08-30 18:42:14 +00:00
/// <summary>
/// Call this function to change the CurrentCell to newCell
/// </summary>
private void CellChanged ( Cell newCell )
2014-09-01 20:34:10 +00:00
{
2014-09-28 17:28:57 +00:00
LastCell = CurrentCell ;
CurrentCell = newCell ;
2015-02-24 06:47:32 +00:00
if ( PointedCellChanged ! = null & &
2014-10-11 17:38:03 +00:00
( LastCell . Column ! = CurrentCell . Column | | LastCell . RowIndex ! = CurrentCell . RowIndex ) )
2014-08-30 18:42:14 +00:00
{
PointedCellChanged ( this , new CellEventArgs ( LastCell , CurrentCell ) ) ;
2014-08-23 13:05:28 +00:00
}
2015-07-25 00:48:02 +00:00
if ( CurrentCell ! = null & & CurrentCell . Column ! = null & & CurrentCell . RowIndex . HasValue )
{
_hoverTimer . Start ( ) ;
}
else
{
_hoverTimer . Stop ( ) ;
}
2014-08-23 13:05:28 +00:00
}
2014-08-19 00:37:38 +00:00
private void VerticalBar_ValueChanged ( object sender , EventArgs e )
{
2014-08-27 22:33:27 +00:00
if ( ! _programmaticallyUpdatingScrollBarValues )
{
Refresh ( ) ;
}
2015-07-14 18:13:21 +00:00
if ( _horizontalOrientation )
{
if ( ColumnScroll ! = null )
{
ColumnScroll ( this , e ) ;
}
}
else
{
if ( RowScroll ! = null )
{
RowScroll ( this , e ) ;
}
}
2016-07-10 19:25:02 +00:00
2016-09-04 09:03:12 +00:00
if ( CurrentCell = = null )
return ;
2014-08-19 00:37:38 +00:00
}
private void HorizontalBar_ValueChanged ( object sender , EventArgs e )
{
2014-08-27 22:33:27 +00:00
if ( ! _programmaticallyUpdatingScrollBarValues )
{
Refresh ( ) ;
}
2015-07-14 18:13:21 +00:00
if ( _horizontalOrientation )
{
if ( RowScroll ! = null )
{
RowScroll ( this , e ) ;
}
}
else
{
if ( ColumnScroll ! = null )
{
ColumnScroll ( this , e ) ;
}
}
2014-08-19 00:37:38 +00:00
}
2014-08-18 23:50:50 +00:00
private void ColumnChangedCallback ( )
{
RecalculateScrollBars ( ) ;
2014-10-12 16:37:45 +00:00
if ( _columns . VisibleColumns . Any ( ) )
2014-08-31 23:03:59 +00:00
{
2014-10-12 16:37:45 +00:00
ColumnWidth = _columns . VisibleColumns . Max ( c = > c . Width . Value ) + CellWidthPadding * 4 ;
2014-08-31 23:03:59 +00:00
}
2014-08-18 23:50:50 +00:00
}
2014-08-30 18:42:14 +00:00
#endregion
#region Helpers
2015-09-02 22:46:23 +00:00
// TODO: Make into an extension method
private static Color Add ( Color color , int val )
{
var col = color . ToArgb ( ) ;
col + = val ;
return Color . FromArgb ( col ) ;
}
2014-10-19 14:04:59 +00:00
private void DoColumnReorder ( )
{
if ( _columnDown ! = CurrentCell . Column )
{
2014-10-19 14:46:01 +00:00
var oldIndex = _columns . IndexOf ( _columnDown ) ;
2014-10-19 14:04:59 +00:00
var newIndex = _columns . IndexOf ( CurrentCell . Column ) ;
2014-10-19 14:46:01 +00:00
if ( ColumnReordered ! = null )
{
ColumnReordered ( this , new ColumnReorderedEventArgs ( oldIndex , newIndex , _columnDown ) ) ;
}
2014-10-19 14:04:59 +00:00
_columns . Remove ( _columnDown ) ;
_columns . Insert ( newIndex , _columnDown ) ;
}
}
2014-08-31 15:40:02 +00:00
//ScrollBar.Maximum = DesiredValue + ScrollBar.LargeChange - 1
//See MSDN Page for more information on the dumb ScrollBar.Maximum Property
2014-08-18 21:38:02 +00:00
private void RecalculateScrollBars ( )
{
2014-08-31 15:40:02 +00:00
UpdateDrawSize ( ) ;
2014-10-12 16:37:45 +00:00
var columns = _columns . VisibleColumns . ToList ( ) ;
2014-08-31 15:40:02 +00:00
if ( HorizontalOrientation )
2014-08-18 21:38:02 +00:00
{
2014-10-12 16:37:45 +00:00
NeedsVScrollbar = columns . Count > DrawHeight / CellHeight ;
2015-03-16 20:42:14 +00:00
NeedsHScrollbar = RowCount > 1 ;
2014-08-31 15:40:02 +00:00
}
else
{
2015-03-16 20:42:14 +00:00
NeedsVScrollbar = RowCount > 1 ;
2014-08-31 15:40:02 +00:00
NeedsHScrollbar = TotalColWidth . HasValue & & TotalColWidth . Value - DrawWidth + 1 > 0 ;
}
UpdateDrawSize ( ) ;
2015-03-16 20:42:14 +00:00
if ( VisibleRows > 0 )
{
if ( HorizontalOrientation )
{
2015-03-19 19:55:38 +00:00
VBar . LargeChange = DrawHeight / 2 ;
2015-03-16 20:42:14 +00:00
HBar . Maximum = Math . Max ( ( VisibleRows - 1 ) * CellHeight , HBar . Maximum ) ;
HBar . LargeChange = ( VisibleRows - 1 ) * CellHeight ;
}
else
{
VBar . Maximum = Math . Max ( ( VisibleRows - 1 ) * CellHeight , VBar . Maximum ) ; // ScrollBar.Maximum is dumb
VBar . LargeChange = ( VisibleRows - 1 ) * CellHeight ;
2015-03-19 19:55:38 +00:00
HBar . LargeChange = DrawWidth / 2 ;
2015-03-16 20:42:14 +00:00
}
}
2014-08-21 21:09:21 +00:00
2014-09-01 20:34:10 +00:00
//Update VBar
2014-08-31 15:40:02 +00:00
if ( NeedsVScrollbar )
{
2014-08-18 21:38:02 +00:00
if ( HorizontalOrientation )
{
2015-03-19 19:55:38 +00:00
VBar . Maximum = ( ( columns . Count ( ) * CellHeight ) - DrawHeight ) + VBar . LargeChange ;
2014-08-18 21:38:02 +00:00
}
else
{
2015-03-16 20:42:14 +00:00
VBar . Maximum = RowsToPixels ( RowCount + 1 ) - ( CellHeight * 3 ) + VBar . LargeChange - 1 ;
2014-08-18 21:38:02 +00:00
}
2014-08-21 21:09:21 +00:00
2015-01-03 21:52:33 +00:00
VBar . Location = new Point ( Width - VBar . Width , 0 ) ;
VBar . Height = Height ;
2014-08-21 21:09:21 +00:00
VBar . Visible = true ;
2014-08-18 21:38:02 +00:00
}
else
{
VBar . Visible = false ;
2014-08-31 15:40:02 +00:00
VBar . Value = 0 ;
2014-08-18 21:38:02 +00:00
}
2014-09-01 20:34:10 +00:00
//Update HBar
2014-08-18 21:38:02 +00:00
if ( NeedsHScrollbar )
{
if ( HorizontalOrientation )
{
2015-03-16 20:42:14 +00:00
HBar . Maximum = RowsToPixels ( RowCount + 1 ) - ( CellHeight * 3 ) + HBar . LargeChange - 1 ;
2014-08-18 21:38:02 +00:00
}
else
{
2014-08-31 15:40:02 +00:00
HBar . Maximum = TotalColWidth . Value - DrawWidth + HBar . LargeChange ;
2014-08-30 18:42:14 +00:00
}
2015-01-03 21:52:33 +00:00
HBar . Location = new Point ( 0 , Height - HBar . Height ) ;
HBar . Width = Width - ( NeedsVScrollbar ? ( VBar . Width + 1 ) : 0 ) ;
HBar . Visible = true ;
2014-08-18 21:38:02 +00:00
}
else
{
HBar . Visible = false ;
2014-08-31 15:40:02 +00:00
HBar . Value = 0 ;
}
}
private void UpdateDrawSize ( )
{
2015-02-24 06:47:32 +00:00
if ( NeedsVScrollbar )
2014-08-31 15:40:02 +00:00
{
2015-02-24 06:47:32 +00:00
DrawWidth = Width - VBar . Width ;
2014-08-31 15:40:02 +00:00
}
else
{
DrawWidth = Width ;
}
2015-02-24 06:47:32 +00:00
if ( NeedsHScrollbar )
{
2014-08-31 15:40:02 +00:00
DrawHeight = Height - HBar . Height ;
}
else
{
2015-02-24 06:47:32 +00:00
DrawHeight = Height ;
2014-08-18 21:38:02 +00:00
}
}
2014-08-30 18:42:14 +00:00
/// <summary>
/// If FullRowSelect is enabled, selects all cells in the row that contains the given cell. Otherwise only given cell is added.
/// </summary>
/// <param name="cell">The cell to select.</param>
2014-09-21 16:00:42 +00:00
private void SelectCell ( Cell cell , bool toggle = false )
2014-08-14 23:10:56 +00:00
{
2014-09-21 16:27:49 +00:00
if ( cell . RowIndex . HasValue & & cell . RowIndex < RowCount )
2014-08-14 23:10:56 +00:00
{
2014-09-21 16:27:49 +00:00
if ( ! MultiSelect )
{
SelectedItems . Clear ( ) ;
}
2014-08-14 23:10:56 +00:00
2014-09-21 16:27:49 +00:00
if ( FullRowSelect )
2014-08-14 23:10:56 +00:00
{
2014-09-21 16:27:49 +00:00
if ( toggle & & SelectedItems . Any ( x = > x . RowIndex . HasValue & & x . RowIndex = = cell . RowIndex ) )
{
var items = SelectedItems
. Where ( x = > x . RowIndex . HasValue & & x . RowIndex = = cell . RowIndex )
. ToList ( ) ;
2014-09-21 16:00:42 +00:00
2014-09-21 16:27:49 +00:00
foreach ( var item in items )
{
SelectedItems . Remove ( item ) ;
}
}
else
2014-08-14 23:10:56 +00:00
{
2014-09-21 16:27:49 +00:00
foreach ( var column in _columns )
{
SelectedItems . Add ( new Cell
{
RowIndex = cell . RowIndex ,
Column = column
} ) ;
}
2014-09-21 16:00:42 +00:00
}
}
else
{
2014-09-21 16:27:49 +00:00
if ( toggle & & SelectedItems . Any ( x = > x . RowIndex . HasValue & & x . RowIndex = = cell . RowIndex ) )
2014-09-21 16:00:42 +00:00
{
2014-09-21 16:27:49 +00:00
var item = SelectedItems
. FirstOrDefault ( x = > x . Equals ( cell ) ) ;
if ( item ! = null )
2014-09-21 16:00:42 +00:00
{
2014-09-21 16:27:49 +00:00
SelectedItems . Remove ( item ) ;
}
2014-09-21 16:00:42 +00:00
}
2014-09-21 16:27:49 +00:00
else
2014-09-21 16:00:42 +00:00
{
2014-09-21 16:27:49 +00:00
SelectedItems . Add ( CurrentCell ) ;
2014-09-21 16:00:42 +00:00
}
}
2014-09-21 16:27:49 +00:00
}
2014-08-14 23:10:56 +00:00
}
2014-08-30 18:42:14 +00:00
/// <summary>
/// Bool that indicates if CurrentCell is a Column Cell.
/// </summary>
2017-03-08 13:18:15 +00:00
public bool IsHoveringOnColumnCell
2014-08-11 01:23:53 +00:00
{
get
{
return CurrentCell ! = null & &
CurrentCell . Column ! = null & &
! CurrentCell . RowIndex . HasValue ;
}
}
2014-08-30 18:42:14 +00:00
/// <summary>
/// Bool that indicates if CurrentCell is a Data Cell.
/// </summary>
2014-08-12 11:07:21 +00:00
private bool IsHoveringOnDataCell
{
get
{
return CurrentCell ! = null & &
CurrentCell . Column ! = null & &
CurrentCell . RowIndex . HasValue ;
}
}
2014-08-30 18:42:14 +00:00
/// <summary>
/// Bool that indicates if CurrentCell is a Column Cell.
/// </summary>
private bool WasHoveringOnColumnCell
{
get
{
return LastCell ! = null & &
LastCell . Column ! = null & &
! LastCell . RowIndex . HasValue ;
}
}
/// <summary>
/// Bool that indicates if CurrentCell is a Data Cell.
/// </summary>
private bool WasHoveringOnDataCell
{
get
{
return LastCell ! = null & &
LastCell . Column ! = null & &
LastCell . RowIndex . HasValue ;
}
}
/// <summary>
/// Finds the specific cell that contains the (x, y) coordinate.
/// </summary>
2014-09-01 15:35:48 +00:00
/// <remarks>The row number that it returns will be between 0 and VisibleRows, NOT the absolute row number.</remarks>
2014-08-30 18:42:14 +00:00
/// <param name="x">X coordinate point.</param>
/// <param name="y">Y coordinate point.</param>
2014-09-01 15:35:48 +00:00
/// <returns>The cell with row number and RollColumn reference, both of which can be null. </returns>
2014-08-30 18:42:14 +00:00
private Cell CalculatePointedCell ( int x , int y )
2014-08-10 15:57:59 +00:00
{
var newCell = new Cell ( ) ;
2014-10-12 16:37:45 +00:00
var columns = _columns . VisibleColumns . ToList ( ) ;
2014-08-10 15:57:59 +00:00
// If pointing to a column header
2014-10-12 16:37:45 +00:00
if ( columns . Any ( ) )
2014-08-10 15:57:59 +00:00
{
if ( HorizontalOrientation )
{
2015-07-26 00:55:52 +00:00
newCell . RowIndex = PixelsToRows ( x ) ;
2014-08-10 15:57:59 +00:00
2015-03-19 19:55:38 +00:00
int colIndex = ( y + VBar . Value ) / CellHeight ;
2014-10-12 16:37:45 +00:00
if ( colIndex > = 0 & & colIndex < columns . Count )
2014-08-10 15:57:59 +00:00
{
2014-10-12 16:37:45 +00:00
newCell . Column = columns [ colIndex ] ;
2014-08-10 15:57:59 +00:00
}
}
else
{
2015-07-26 00:55:52 +00:00
newCell . RowIndex = PixelsToRows ( y ) ;
2014-08-30 18:42:14 +00:00
newCell . Column = ColumnAtX ( x ) ;
2014-08-11 01:23:53 +00:00
}
2014-08-10 15:57:59 +00:00
}
2015-07-26 00:55:52 +00:00
2017-03-08 13:18:15 +00:00
if ( ! ( IsPaintDown | | RightButtonHeld ) & & newCell . RowIndex < = - 1 ) // -2 if we're entering from the top
2015-07-26 18:44:19 +00:00
newCell . RowIndex = null ;
2014-08-30 18:42:14 +00:00
return newCell ;
2014-08-10 15:57:59 +00:00
}
2014-08-30 18:42:14 +00:00
/// <summary>
/// A boolean that indicates if the InputRoll is too large vertically and requires a vertical scrollbar.
/// </summary>
2015-02-24 06:47:32 +00:00
private bool NeedsVScrollbar { get ; set ; }
2014-08-15 00:42:03 +00:00
2014-08-30 18:42:14 +00:00
/// <summary>
/// A boolean that indicates if the InputRoll is too large horizontally and requires a horizontal scrollbar.
/// </summary>
2015-02-24 06:47:32 +00:00
private bool NeedsHScrollbar { get ; set ; }
2014-08-07 14:55:55 +00:00
2014-08-30 18:42:14 +00:00
/// <summary>
/// Updates the width of the supplied column.
/// <remarks>Call when changing the ColumnCell text, CellPadding, or text font.</remarks>
/// </summary>
/// <param name="col">The RollColumn object to update.</param>
/// <returns>The new width of the RollColumn object.</returns>
private int UpdateWidth ( RollColumn col )
2014-08-09 16:11:25 +00:00
{
2014-09-24 21:10:57 +00:00
col . Width = ( ( col . Text . Length * _charSize . Width ) + ( CellWidthPadding * 4 ) ) ;
2014-08-30 18:42:14 +00:00
return col . Width . Value ;
2014-08-08 18:30:57 +00:00
}
2014-08-31 15:40:02 +00:00
/// <summary>
/// Gets the total width of all the columns by using the last column's Right property.
/// </summary>
/// <returns>A nullable Int representing total width.</returns>
private int? TotalColWidth
2015-02-24 06:47:32 +00:00
{
get
{
2014-10-12 16:37:45 +00:00
if ( _columns . VisibleColumns . Any ( ) )
2014-08-31 15:40:02 +00:00
{
2014-10-12 16:37:45 +00:00
return _columns . VisibleColumns . Last ( ) . Right ;
2014-08-31 15:40:02 +00:00
}
return null ;
}
}
2014-08-30 18:42:14 +00:00
/// <summary>
/// Returns the RollColumn object at the specified visible x coordinate. Coordinate should be between 0 and Width of the InputRoll Control.
/// </summary>
/// <param name="x">The x coordinate.</param>
/// <returns>RollColumn object that contains the x coordinate or null if none exists.</returns>
2014-08-11 01:49:45 +00:00
private RollColumn ColumnAtX ( int x )
{
2015-03-19 19:55:38 +00:00
foreach ( RollColumn column in _columns . VisibleColumns )
2014-08-11 01:49:45 +00:00
{
2014-08-30 18:42:14 +00:00
if ( column . Left . Value - HBar . Value < = x & & column . Right . Value - HBar . Value > = x )
2014-08-11 01:49:45 +00:00
{
return column ;
}
}
return null ;
}
2014-08-31 23:03:59 +00:00
/// <summary>
/// Converts a row number to a horizontal or vertical coordinate.
/// </summary>
/// <returns>A vertical coordinate if Vertical Oriented, otherwise a horizontal coordinate.</returns>
private int RowsToPixels ( int index )
{
if ( _horizontalOrientation )
{
2014-10-15 22:14:44 +00:00
return ( index * CellWidth ) + ColumnWidth ;
2014-08-31 23:03:59 +00:00
}
2014-10-15 22:14:44 +00:00
2015-02-24 06:47:32 +00:00
return ( index * CellHeight ) + ColumnHeight ;
2014-08-31 23:03:59 +00:00
}
/// <summary>
/// Converts a horizontal or vertical coordinate to a row number.
/// </summary>
/// <param name="pixels">A vertical coordinate if Vertical Oriented, otherwise a horizontal coordinate.</param>
/// <returns>A row number between 0 and VisibleRows if it is a Datarow, otherwise a negative number if above all Datarows.</returns>
private int PixelsToRows ( int pixels )
{
2015-07-26 00:55:52 +00:00
// Using Math.Floor and float because integer division rounds towards 0 but we want to round down.
2014-08-31 23:03:59 +00:00
if ( _horizontalOrientation )
{
2015-07-26 00:55:52 +00:00
return ( int ) Math . Floor ( ( float ) ( pixels - ColumnWidth ) / CellWidth ) ;
2014-08-31 23:03:59 +00:00
}
2015-07-26 00:55:52 +00:00
return ( int ) Math . Floor ( ( float ) ( pixels - ColumnHeight ) / CellHeight ) ;
2014-08-31 23:03:59 +00:00
}
/// <summary>
/// The width of the largest column cell in Horizontal Orientation
/// </summary>
private int ColumnWidth { get ; set ; }
/// <summary>
/// The height of a column cell in Vertical Orientation.
/// </summary>
private int ColumnHeight { get ; set ; }
//Cell defaults
/// <summary>
/// The width of a cell in Horizontal Orientation. Only can be changed by changing the Font or CellPadding.
/// </summary>
private int CellWidth { get ; set ; }
2015-07-25 12:40:17 +00:00
[Browsable(false)]
public int RowHeight { get { return CellHeight ; } }
2014-08-31 23:03:59 +00:00
/// <summary>
/// The height of a cell in Vertical Orientation. Only can be changed by changing the Font or CellPadding.
/// </summary>
private int CellHeight { get ; set ; }
/// <summary>
2014-09-01 15:35:48 +00:00
/// Call when _charSize, MaxCharactersInHorizontal, or CellPadding is changed.
2014-08-31 23:03:59 +00:00
/// </summary>
private void UpdateCellSize ( )
{
2014-10-15 22:14:44 +00:00
CellHeight = _charSize . Height + ( CellHeightPadding * 2 ) ;
2015-02-24 06:47:32 +00:00
CellWidth = ( _charSize . Width * MaxCharactersInHorizontal ) + ( CellWidthPadding * 4 ) ; // Double the padding for horizontal because it looks better
2014-08-31 23:03:59 +00:00
}
2014-08-31 17:11:47 +00:00
2015-02-24 19:00:12 +00:00
// SuuperW: Count lag frames between FirstDisplayed and given display position
private int CountLagFramesDisplay ( int relativeIndex )
{
2015-02-25 21:17:50 +00:00
if ( QueryFrameLag ! = null & & LagFramesToHide ! = 0 )
2015-02-24 19:00:12 +00:00
{
int count = 0 ;
for ( int i = 0 ; i < = relativeIndex ; i + + )
count + = lagFrames [ i ] ;
return count ;
}
return 0 ;
}
2015-02-26 21:12:12 +00:00
// Count lag frames between FirstDisplayed and given relative frame index
private int CountLagFramesAbsolute ( int relativeIndex )
2015-02-24 19:00:12 +00:00
{
2015-02-25 21:17:50 +00:00
if ( QueryFrameLag ! = null & & LagFramesToHide ! = 0 )
2015-02-24 19:00:12 +00:00
{
int count = 0 ;
2015-02-26 21:12:12 +00:00
for ( int i = 0 ; i + count < = relativeIndex ; i + + )
2015-02-24 19:00:12 +00:00
count + = lagFrames [ i ] ;
return count ;
}
return 0 ;
}
private void SetLagFramesArray ( )
{
2015-02-25 21:17:50 +00:00
if ( QueryFrameLag ! = null & & LagFramesToHide ! = 0 )
2015-02-24 19:00:12 +00:00
{
bool showNext = false ;
// First one needs to check BACKWARDS for lag frame count.
SetLagFramesFirst ( ) ;
int f = lagFrames [ 0 ] ;
2015-03-10 17:11:29 +00:00
if ( QueryFrameLag ( FirstVisibleRow + f , HideWasLagFrames ) )
2015-02-24 19:00:12 +00:00
showNext = true ;
for ( int i = 1 ; i < = VisibleRows ; i + + )
{
lagFrames [ i ] = 0 ;
if ( ! showNext )
{
2015-02-24 20:38:46 +00:00
for ( ; lagFrames [ i ] < LagFramesToHide ; lagFrames [ i ] + + )
2015-02-24 19:00:12 +00:00
{
2015-03-10 17:11:29 +00:00
if ( ! QueryFrameLag ( FirstVisibleRow + i + f , HideWasLagFrames ) )
2015-02-24 19:00:12 +00:00
break ;
f + + ;
}
}
else
{
2015-03-10 17:11:29 +00:00
if ( ! QueryFrameLag ( FirstVisibleRow + i + f , HideWasLagFrames ) )
2015-02-24 19:00:12 +00:00
showNext = false ;
}
2015-03-10 17:11:29 +00:00
if ( lagFrames [ i ] = = LagFramesToHide & & QueryFrameLag ( FirstVisibleRow + i + f , HideWasLagFrames ) )
2015-02-24 19:00:12 +00:00
{
showNext = true ;
}
}
}
2015-02-25 21:17:50 +00:00
else
2015-09-05 19:09:55 +00:00
{
2015-03-03 06:56:45 +00:00
for ( int i = 0 ; i < = VisibleRows ; i + + )
2015-09-05 19:09:55 +00:00
{
2015-02-25 21:17:50 +00:00
lagFrames [ i ] = 0 ;
2015-09-05 19:09:55 +00:00
}
}
2015-02-24 19:00:12 +00:00
}
private void SetLagFramesFirst ( )
{
2015-02-25 21:17:50 +00:00
if ( QueryFrameLag ! = null & & LagFramesToHide ! = 0 )
2015-02-24 19:00:12 +00:00
{
// Count how many lag frames are above displayed area.
int count = 0 ;
do
{
count + + ;
2015-03-10 17:11:29 +00:00
} while ( QueryFrameLag ( FirstVisibleRow - count , HideWasLagFrames ) & & count < = LagFramesToHide ) ;
2015-02-24 19:00:12 +00:00
count - - ;
// Count forward
int fCount = - 1 ;
do
{
fCount + + ;
2015-03-10 17:11:29 +00:00
} while ( QueryFrameLag ( FirstVisibleRow + fCount , HideWasLagFrames ) & & count + fCount < LagFramesToHide ) ;
2015-03-15 06:26:57 +00:00
lagFrames [ 0 ] = ( byte ) fCount ;
2015-02-24 19:00:12 +00:00
}
else
lagFrames [ 0 ] = 0 ;
}
2015-02-26 21:12:12 +00:00
// Number of displayed + hidden frames, if fps is as expected
private int ExpectedDisplayRange ( )
{
return ( VisibleRows + 1 ) * LagFramesToHide ;
}
2014-08-07 14:55:55 +00:00
#endregion
2014-08-11 00:08:16 +00:00
#region Classes
public class RollColumns : List < RollColumn >
2014-08-07 14:55:55 +00:00
{
2014-08-23 13:19:48 +00:00
public RollColumn this [ string name ]
{
get
{
return this . SingleOrDefault ( column = > column . Name = = name ) ;
}
}
2015-02-24 06:47:32 +00:00
2014-10-12 16:37:45 +00:00
public IEnumerable < RollColumn > VisibleColumns
{
get
{
return this . Where ( c = > c . Visible ) ;
}
}
2014-08-18 23:50:50 +00:00
public Action ChangedCallback { get ; set ; }
private void DoChangeCallback ( )
2014-08-07 14:55:55 +00:00
{
2016-11-17 18:30:42 +00:00
// no check will make it crash for user too, not sure which way of alarm we prefer. no alarm at all will cause all sorts of subtle bugs
if ( ChangedCallback = = null )
System . Diagnostics . Debug . Fail ( "ColumnChangedCallback has died!" ) ;
else
2014-08-18 23:50:50 +00:00
ChangedCallback ( ) ;
}
2016-09-25 22:55:34 +00:00
// TODO: this shouldn't be exposed. But in order to not expose it, each RollColumn must have a change callback, and all property changes must call it, it is quicker and easier to just call this when needed
2014-10-12 16:37:45 +00:00
public void ColumnsChanged ( )
2014-08-30 18:42:14 +00:00
{
int pos = 0 ;
2014-10-12 16:37:45 +00:00
var columns = VisibleColumns . ToList ( ) ;
for ( int i = 0 ; i < columns . Count ; i + + )
2014-08-30 18:42:14 +00:00
{
2014-10-12 16:37:45 +00:00
columns [ i ] . Left = pos ;
pos + = columns [ i ] . Width . Value ;
columns [ i ] . Right = pos ;
2014-08-30 18:42:14 +00:00
}
DoChangeCallback ( ) ;
}
2014-08-18 23:50:50 +00:00
public new void Add ( RollColumn column )
{
if ( this . Any ( c = > c . Name = = column . Name ) )
{
2014-08-23 15:19:48 +00:00
// The designer sucks, doing nothing for now
return ;
//throw new InvalidOperationException("A column with this name already exists.");
2014-08-18 23:50:50 +00:00
}
base . Add ( column ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-18 23:50:50 +00:00
}
public new void AddRange ( IEnumerable < RollColumn > collection )
{
2015-02-24 06:47:32 +00:00
foreach ( var column in collection )
2014-08-18 23:50:50 +00:00
{
if ( this . Any ( c = > c . Name = = column . Name ) )
{
2014-08-23 15:19:48 +00:00
// The designer sucks, doing nothing for now
return ;
2014-08-18 23:50:50 +00:00
throw new InvalidOperationException ( "A column with this name already exists." ) ;
}
}
base . AddRange ( collection ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-18 23:50:50 +00:00
}
public new void Insert ( int index , RollColumn column )
{
if ( this . Any ( c = > c . Name = = column . Name ) )
{
throw new InvalidOperationException ( "A column with this name already exists." ) ;
}
base . Insert ( index , column ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-18 23:50:50 +00:00
}
public new void InsertRange ( int index , IEnumerable < RollColumn > collection )
{
foreach ( var column in collection )
{
if ( this . Any ( c = > c . Name = = column . Name ) )
{
throw new InvalidOperationException ( "A column with this name already exists." ) ;
}
}
base . InsertRange ( index , collection ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-18 23:50:50 +00:00
}
public new bool Remove ( RollColumn column )
{
var result = base . Remove ( column ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-18 23:50:50 +00:00
return result ;
}
public new int RemoveAll ( Predicate < RollColumn > match )
{
var result = base . RemoveAll ( match ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-18 23:50:50 +00:00
return result ;
}
public new void RemoveAt ( int index )
{
base . RemoveAt ( index ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-18 23:50:50 +00:00
}
public new void RemoveRange ( int index , int count )
{
base . RemoveRange ( index , count ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-18 23:50:50 +00:00
}
public new void Clear ( )
{
base . Clear ( ) ;
2014-08-30 18:42:14 +00:00
ColumnsChanged ( ) ;
2014-08-11 00:08:16 +00:00
}
2014-08-09 16:11:25 +00:00
2014-08-11 00:08:16 +00:00
public IEnumerable < string > Groups
2014-08-09 16:11:25 +00:00
{
2014-08-11 00:08:16 +00:00
get
{
return this
. Select ( x = > x . Group )
. Distinct ( ) ;
}
2014-08-09 16:11:25 +00:00
}
}
2014-08-07 14:55:55 +00:00
2014-08-11 00:08:16 +00:00
public class RollColumn
{
public enum InputType { Boolean , Float , Text , Image }
2014-08-07 18:32:09 +00:00
2014-08-11 00:08:16 +00:00
public string Group { get ; set ; }
public int? Width { get ; set ; }
2014-08-30 18:42:14 +00:00
public int? Left { get ; set ; }
public int? Right { get ; set ; }
2014-08-11 00:08:16 +00:00
public string Name { get ; set ; }
public string Text { get ; set ; }
public InputType Type { get ; set ; }
2014-10-12 16:37:45 +00:00
public bool Visible { get ; set ; }
2014-09-25 17:24:17 +00:00
/// <summary>
/// Column will be drawn with an emphasized look, if true
/// </summary>
2015-03-22 16:55:34 +00:00
private bool _emphasis ;
public bool Emphasis
{
get { return _emphasis ; }
set { _emphasis = value ; }
}
2014-10-12 16:37:45 +00:00
public RollColumn ( )
{
Visible = true ;
}
2014-08-11 00:08:16 +00:00
}
2014-08-10 15:57:59 +00:00
2014-08-30 18:42:14 +00:00
/// <summary>
///
/// </summary>
2014-08-11 00:08:16 +00:00
public class Cell
{
2014-08-30 18:42:14 +00:00
public RollColumn Column { get ; internal set ; }
public int? RowIndex { get ; internal set ; }
public string CurrentText { get ; internal set ; }
2014-08-10 15:57:59 +00:00
2014-08-11 00:08:16 +00:00
public Cell ( ) { }
2014-08-10 15:57:59 +00:00
2014-08-11 00:08:16 +00:00
public Cell ( Cell cell )
{
Column = cell . Column ;
RowIndex = cell . RowIndex ;
}
2014-08-10 15:57:59 +00:00
2015-07-30 02:15:00 +00:00
public bool IsDataCell
{
get { return Column ! = null & & RowIndex . HasValue ; }
}
2014-08-11 00:08:16 +00:00
public override bool Equals ( object obj )
2014-08-10 15:57:59 +00:00
{
2014-08-11 00:08:16 +00:00
if ( obj is Cell )
{
var cell = obj as Cell ;
return this . Column = = cell . Column & & this . RowIndex = = cell . RowIndex ;
}
return base . Equals ( obj ) ;
2014-08-10 15:57:59 +00:00
}
2014-08-11 00:08:16 +00:00
public override int GetHashCode ( )
{
return Column . GetHashCode ( ) + RowIndex . GetHashCode ( ) ;
}
2014-08-10 15:57:59 +00:00
}
2014-08-11 00:08:16 +00:00
2015-07-26 03:42:50 +00:00
private class sortCell : IComparer < Cell >
{
int IComparer < Cell > . Compare ( Cell a , Cell b )
{
Cell c1 = a as Cell ;
Cell c2 = b as Cell ;
if ( c1 . RowIndex . HasValue )
{
if ( c2 . RowIndex . HasValue )
{
int row = c1 . RowIndex . Value . CompareTo ( c2 . RowIndex . Value ) ;
if ( row = = 0 )
{
return c1 . Column . Name . CompareTo ( c2 . Column . Name ) ;
}
else
return row ;
}
else
return 1 ;
}
else if ( c2 . RowIndex . HasValue )
return - 1 ;
else
return c1 . Column . Name . CompareTo ( c2 . Column . Name ) ;
}
}
2014-08-11 00:08:16 +00:00
#endregion
2014-08-10 15:57:59 +00:00
}
2014-08-06 01:32:27 +00:00
}