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 ; }
2015-03-15 06:26:57 +00:00
private byte [ ] lagFrames = new byte [ 100 ] ; // Large enough value that it shouldn't ever need resizing.
2015-02-24 19:00:12 +00:00
2015-09-02 22:23:29 +00:00
public bool denoteStatesWithIcons { get ; set ; }
public bool denoteStatesWithBGColor { get ; set ; }
public bool denoteMarkersWithIcons { get ; set ; }
public bool denoteMarkersWithBGColor { get ; set ; }
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 ;
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
{
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-10-25 14:10:32 +00:00
// In this case the SelectCell did not invoke the change event since there was nothing to select
// But we went from selected to unselected, that is a change, so catch it here
if ( hadIndex & & CurrentCell . RowIndex . HasValue & & CurrentCell . RowIndex > RowCount )
{
2015-07-25 16:01:26 +00:00
if ( SelectedIndexChanged ! = null )
{
SelectedIndexChanged ( this , new EventArgs ( ) ) ;
}
2014-10-25 14:10:32 +00:00
}
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 ( ) ;
}
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 ) ;
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
if ( ! GlobalWin . MainForm . EmulatorPaused & & _currentX . HasValue )
{
tastudio: fix editing while unpaused + left button held scenario.
now it replicates taseditor:
- editing input while unpaused, if autorestore is off, resumes emulation and ignores seek frame. if you keep holding the LMB, and follow cursor is on, it will keep scrolling, drawing and emulating
- editing input while unpaused, if autoresotre is on, fires autorestore and then just pauses on the seek frame
what's different from taseditor:
- with autorestore and follow cursor on, if you hold the button, and seek frame is below the view, it will keep scrolling down, drawing new input and emulating. taseditor does *not* follow cursor while seeking.
this all doesn't necessarily make sense, but we need people to figure out what is best for work.
2016-07-11 18:38:58 +00:00
// copypaste from OnMouseMove()
2016-07-10 19:25:02 +00:00
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 ( ) ;
}
}
tastudio: fix editing while unpaused + left button held scenario.
now it replicates taseditor:
- editing input while unpaused, if autorestore is off, resumes emulation and ignores seek frame. if you keep holding the LMB, and follow cursor is on, it will keep scrolling, drawing and emulating
- editing input while unpaused, if autoresotre is on, fires autorestore and then just pauses on the seek frame
what's different from taseditor:
- with autorestore and follow cursor on, if you hold the button, and seek frame is below the view, it will keep scrolling down, drawing new input and emulating. taseditor does *not* follow cursor while seeking.
this all doesn't necessarily make sense, but we need people to figure out what is best for work.
2016-07-11 18:38:58 +00:00
else if ( _columnDown ! = null )
2016-07-10 19:25:02 +00:00
{
Refresh ( ) ;
}
}
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-08-14 23:10:56 +00:00
2015-07-25 00:48:02 +00:00
if ( SelectedIndexChanged ! = null )
{
2016-04-17 18:02:21 +00:00
SelectedIndexChanged ( this , new EventArgs ( ) ) ;
2015-07-25 00:48:02 +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>
2014-08-11 01:23:53 +00:00
private bool IsHoveringOnColumnCell
{
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
2015-07-26 18:44:19 +00:00
if ( ! ( IsPaintDown | | RightButtonHeld ) & & newCell . RowIndex = = - 1 )
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
{
2014-08-18 23:50:50 +00:00
if ( ChangedCallback ! = null )
2014-08-11 00:08:16 +00:00
{
2014-08-18 23:50:50 +00:00
ChangedCallback ( ) ;
}
}
2014-10-12 16:37:45 +00:00
// TODO: this shouldn't be exposed. But in order to not expose it, each RollColumn must have a chane callback, and all property changes must call it, it is quicker and easier to just call this when needed
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
}