using System; using System.Data; using System.Linq; using System.Text; namespace Terminal.Gui { /// /// for a which adds a /// checkbox column as an additional column in the table. /// /// This class wraps another and dynamically /// serves its rows/cols plus an extra column. Data in the wrapped source can be /// dynamic (change over time). public abstract class CheckBoxTableSourceWrapperBase : ITableSource { private readonly TableView tableView; /// /// Creates a new instance of the class presenting the data in /// plus an additional checkbox column. /// /// The this source will be used with. /// This is required for event registration. /// The original data source of the that you /// want to add checkboxes to. public CheckBoxTableSourceWrapperBase (TableView tableView, ITableSource toWrap) { this.Wrapping = toWrap; this.tableView = tableView; tableView.AddKeyBinding (Key.Space, Command.ToggleChecked); tableView.MouseClick += TableView_MouseClick; tableView.CellToggled += TableView_CellToggled; } /// /// Gets or sets the character to use for checked entries. Defaults to /// public Rune CheckedRune { get; set; } = CM.Glyphs.Checked; /// /// Gets or sets the character to use for UnChecked entries. Defaults to /// public Rune UnCheckedRune { get; set; } = CM.Glyphs.UnChecked; /// /// Gets or sets whether to only allow a single row to be toggled at once (Radio button). /// public bool UseRadioButtons { get; set; } /// /// Gets or sets the character to use for checked entry when is true. /// Defaults to /// public Rune RadioCheckedRune { get; set; } = CM.Glyphs.Selected; /// /// Gets or sets the character to use for unchecked entries when is true. /// Defaults to /// public Rune RadioUnCheckedRune { get; set; } = CM.Glyphs.UnSelected; /// /// Gets the that this instance is wrapping. /// public ITableSource Wrapping { get; } /// public object this [int row, int col] { get { if (col == 0) { if(UseRadioButtons) { return IsChecked (row) ? RadioCheckedRune : RadioUnCheckedRune; } return IsChecked(row) ? CheckedRune : UnCheckedRune; } return Wrapping [row, col - 1]; } } /// public int Rows => Wrapping.Rows; /// public int Columns => Wrapping.Columns + 1; /// public string [] ColumnNames { get { var toReturn = Wrapping.ColumnNames.ToList (); toReturn.Insert (0, " "); return toReturn.ToArray (); } } private void TableView_MouseClick (object sender, MouseEventEventArgs e) { // we only care about clicks (not movements) if(!e.MouseEvent.Flags.HasFlag(MouseFlags.Button1Clicked)) { return; } var hit = tableView.ScreenToCell (e.MouseEvent.X,e.MouseEvent.Y, out int? headerIfAny); if(headerIfAny.HasValue && headerIfAny.Value == 0) { // clicking in header with radio buttons does nothing if(UseRadioButtons) { return; } // otherwise it ticks all rows ToggleAllRows (); e.Handled = true; tableView.SetNeedsDisplay (); } else if(hit.HasValue && hit.Value.X == 0) { if(UseRadioButtons) { ClearAllToggles (); ToggleRow (hit.Value.Y); } else { ToggleRow (hit.Value.Y); } e.Handled = true; tableView.SetNeedsDisplay (); } } private void TableView_CellToggled (object sender, CellToggledEventArgs e) { // Suppress default toggle behavior when using checkboxes // and instead handle ourselves var range = tableView.GetAllSelectedCells ().Select (c => c.Y).Distinct ().ToArray(); if(UseRadioButtons) { // multi selection makes it unclear what to toggle in this situation if(range.Length != 1) { e.Cancel = true; return; } ClearAllToggles (); ToggleRow (range.Single ()); } else { ToggleRows (range); } e.Cancel = true; tableView.SetNeedsDisplay (); } /// /// Returns true if is checked. /// /// /// protected abstract bool IsChecked (int row); /// /// Flips the checked state for a collection of rows. If /// some (but not all) are selected they should flip to all /// selected. /// /// protected abstract void ToggleRows (int [] range); /// /// Flips the checked state of the given / /// /// protected abstract void ToggleRow (int row); /// /// Called when the 'toggled all' action is performed. /// This should change state from 'some selected' to /// 'all selected' or clear selection if all area already /// selected. /// protected abstract void ToggleAllRows (); /// /// Clears the toggled state of all rows. /// protected abstract void ClearAllToggles (); } }