| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553 |
- #nullable enable
- using System.Data;
- using System.Globalization;
- using System.Text;
- namespace UICatalog.Scenarios;
- [ScenarioMetadata ("TableEditor", "Implements data table editor using the TableView control.")]
- [ScenarioCategory ("TableView")]
- [ScenarioCategory ("Controls")]
- [ScenarioCategory ("Dialogs")]
- [ScenarioCategory ("Text and Formatting")]
- public class TableEditor : Scenario
- {
- private readonly HashSet<FileSystemInfo>? _checkedFileSystemInfos = [];
- private readonly List<IDisposable>? _toDispose = [];
- private readonly List<UnicodeRange>? _ranges =
- [
- new (
- 0x0000,
- 0x001F,
- "ASCII Control Characters"
- ),
- new (0x0080, 0x009F, "C0 Control Characters"),
- new (
- 0x1100,
- 0x11ff,
- "Hangul Jamo"
- ), // This is where wide chars tend to start
- new (0x20A0, 0x20CF, "Currency Symbols"),
- new (0x2100, 0x214F, "Letterlike Symbols"),
- new (0x2190, 0x21ff, "Arrows"),
- new (0x2200, 0x22ff, "Mathematical symbols"),
- new (
- 0x2300,
- 0x23ff,
- "Miscellaneous Technical"
- ),
- new (
- 0x2500,
- 0x25ff,
- "Box Drawing & Geometric Shapes"
- ),
- new (0x2600, 0x26ff, "Miscellaneous Symbols"),
- new (0x2700, 0x27ff, "Dingbats"),
- new (0x2800, 0x28ff, "Braille"),
- new (
- 0x2b00,
- 0x2bff,
- "Miscellaneous Symbols and Arrows"
- ),
- new (
- 0xFB00,
- 0xFb4f,
- "Alphabetic Presentation Forms"
- ),
- new (
- 0x12400,
- 0x1240f,
- "Cuneiform Numbers and Punctuation"
- ),
- new (
- (uint)(Terminal.Gui.Views.UnicodeRange.Ranges.Max (r => r.End) - 16),
- (uint)Terminal.Gui.Views.UnicodeRange.Ranges.Max (r => r.End),
- "End"
- ),
- new (0x0020, 0x007F, "Basic Latin"),
- new (0x00A0, 0x00FF, "Latin-1 Supplement"),
- new (0x0100, 0x017F, "Latin Extended-A"),
- new (0x0180, 0x024F, "Latin Extended-B"),
- new (0x0250, 0x02AF, "IPA Extensions"),
- new (
- 0x02B0,
- 0x02FF,
- "Spacing Modifier Letters"
- ),
- new (
- 0x0300,
- 0x036F,
- "Combining Diacritical Marks"
- ),
- new (0x0370, 0x03FF, "Greek and Coptic"),
- new (0x0400, 0x04FF, "Cyrillic"),
- new (0x0500, 0x052F, "Cyrillic Supplementary"),
- new (0x0530, 0x058F, "Armenian"),
- new (0x0590, 0x05FF, "Hebrew"),
- new (0x0600, 0x06FF, "Arabic"),
- new (0x0700, 0x074F, "Syriac"),
- new (0x0780, 0x07BF, "Thaana"),
- new (0x0900, 0x097F, "Devanagari"),
- new (0x0980, 0x09FF, "Bengali"),
- new (0x0A00, 0x0A7F, "Gurmukhi"),
- new (0x0A80, 0x0AFF, "Gujarati"),
- new (0x0B00, 0x0B7F, "Oriya"),
- new (0x0B80, 0x0BFF, "Tamil"),
- new (0x0C00, 0x0C7F, "Telugu"),
- new (0x0C80, 0x0CFF, "Kannada"),
- new (0x0D00, 0x0D7F, "Malayalam"),
- new (0x0D80, 0x0DFF, "Sinhala"),
- new (0x0E00, 0x0E7F, "Thai"),
- new (0x0E80, 0x0EFF, "Lao"),
- new (0x0F00, 0x0FFF, "Tibetan"),
- new (0x1000, 0x109F, "Myanmar"),
- new (0x10A0, 0x10FF, "Georgian"),
- new (0x1100, 0x11FF, "Hangul Jamo"),
- new (0x1200, 0x137F, "Ethiopic"),
- new (0x13A0, 0x13FF, "Cherokee"),
- new (
- 0x1400,
- 0x167F,
- "Unified Canadian Aboriginal Syllabics"
- ),
- new (0x1680, 0x169F, "Ogham"),
- new (0x16A0, 0x16FF, "Runic"),
- new (0x1700, 0x171F, "Tagalog"),
- new (0x1720, 0x173F, "Hanunoo"),
- new (0x1740, 0x175F, "Buhid"),
- new (0x1760, 0x177F, "Tagbanwa"),
- new (0x1780, 0x17FF, "Khmer"),
- new (0x1800, 0x18AF, "Mongolian"),
- new (0x1900, 0x194F, "Limbu"),
- new (0x1950, 0x197F, "Tai Le"),
- new (0x19E0, 0x19FF, "Khmer Symbols"),
- new (0x1D00, 0x1D7F, "Phonetic Extensions"),
- new (
- 0x1E00,
- 0x1EFF,
- "Latin Extended Additional"
- ),
- new (0x1F00, 0x1FFF, "Greek Extended"),
- new (0x2000, 0x206F, "General Punctuation"),
- new (
- 0x2070,
- 0x209F,
- "Superscripts and Subscripts"
- ),
- new (0x20A0, 0x20CF, "Currency Symbols"),
- new (
- 0x20D0,
- 0x20FF,
- "Combining Diacritical Marks for Symbols"
- ),
- new (0x2100, 0x214F, "Letterlike Symbols"),
- new (0x2150, 0x218F, "Number Forms"),
- new (0x2190, 0x21FF, "Arrows"),
- new (0x2200, 0x22FF, "Mathematical Operators"),
- new (
- 0x2300,
- 0x23FF,
- "Miscellaneous Technical"
- ),
- new (0x2400, 0x243F, "Control Pictures"),
- new (
- 0x2440,
- 0x245F,
- "Optical Character Recognition"
- ),
- new (0x2460, 0x24FF, "Enclosed Alphanumerics"),
- new (0x2500, 0x257F, "Box Drawing"),
- new (0x2580, 0x259F, "Block Elements"),
- new (0x25A0, 0x25FF, "Geometric Shapes"),
- new (0x2600, 0x26FF, "Miscellaneous Symbols"),
- new (0x2700, 0x27BF, "Dingbats"),
- new (
- 0x27C0,
- 0x27EF,
- "Miscellaneous Mathematical Symbols-A"
- ),
- new (0x27F0, 0x27FF, "Supplemental Arrows-A"),
- new (0x2800, 0x28FF, "Braille Patterns"),
- new (0x2900, 0x297F, "Supplemental Arrows-B"),
- new (
- 0x2980,
- 0x29FF,
- "Miscellaneous Mathematical Symbols-B"
- ),
- new (
- 0x2A00,
- 0x2AFF,
- "Supplemental Mathematical Operators"
- ),
- new (
- 0x2B00,
- 0x2BFF,
- "Miscellaneous Symbols and Arrows"
- ),
- new (
- 0x2E80,
- 0x2EFF,
- "CJK Radicals Supplement"
- ),
- new (0x2F00, 0x2FDF, "Kangxi Radicals"),
- new (
- 0x2FF0,
- 0x2FFF,
- "Ideographic Description Characters"
- ),
- new (
- 0x3000,
- 0x303F,
- "CJK Symbols and Punctuation"
- ),
- new (0x3040, 0x309F, "Hiragana"),
- new (0x30A0, 0x30FF, "Katakana"),
- new (0x3100, 0x312F, "Bopomofo"),
- new (
- 0x3130,
- 0x318F,
- "Hangul Compatibility Jamo"
- ),
- new (0x3190, 0x319F, "Kanbun"),
- new (0x31A0, 0x31BF, "Bopomofo Extended"),
- new (
- 0x31F0,
- 0x31FF,
- "Katakana Phonetic Extensions"
- ),
- new (
- 0x3200,
- 0x32FF,
- "Enclosed CJK Letters and Months"
- ),
- new (0x3300, 0x33FF, "CJK Compatibility"),
- new (
- 0x3400,
- 0x4DBF,
- "CJK Unified Ideographs Extension A"
- ),
- new (
- 0x4DC0,
- 0x4DFF,
- "Yijing Hexagram Symbols"
- ),
- new (0x4E00, 0x9FFF, "CJK Unified Ideographs"),
- new (0xA000, 0xA48F, "Yi Syllables"),
- new (0xA490, 0xA4CF, "Yi Radicals"),
- new (0xAC00, 0xD7AF, "Hangul Syllables"),
- new (0xD800, 0xDB7F, "High Surrogates"),
- new (
- 0xDB80,
- 0xDBFF,
- "High Private Use Surrogates"
- ),
- new (0xDC00, 0xDFFF, "Low Surrogates"),
- new (0xE000, 0xF8FF, "Private Use Area"),
- new (
- 0xF900,
- 0xFAFF,
- "CJK Compatibility Ideographs"
- ),
- new (
- 0xFB00,
- 0xFB4F,
- "Alphabetic Presentation Forms"
- ),
- new (
- 0xFB50,
- 0xFDFF,
- "Arabic Presentation Forms-A"
- ),
- new (0xFE00, 0xFE0F, "Variation Selectors"),
- new (0xFE20, 0xFE2F, "Combining Half Marks"),
- new (
- 0xFE30,
- 0xFE4F,
- "CJK Compatibility Forms"
- ),
- new (0xFE50, 0xFE6F, "Small Form Variants"),
- new (
- 0xFE70,
- 0xFEFF,
- "Arabic Presentation Forms-B"
- ),
- new (
- 0xFF00,
- 0xFFEF,
- "Halfwidth and Fullwidth Forms"
- ),
- new (0xFFF0, 0xFFFF, "Specials"),
- new (0x10000, 0x1007F, "Linear B Syllabary"),
- new (0x10080, 0x100FF, "Linear B Ideograms"),
- new (0x10100, 0x1013F, "Aegean Numbers"),
- new (0x10300, 0x1032F, "Old Italic"),
- new (0x10330, 0x1034F, "Gothic"),
- new (0x10380, 0x1039F, "Ugaritic"),
- new (0x10400, 0x1044F, "Deseret"),
- new (0x10450, 0x1047F, "Shavian"),
- new (0x10480, 0x104AF, "Osmanya"),
- new (0x10800, 0x1083F, "Cypriot Syllabary"),
- new (
- 0x1D000,
- 0x1D0FF,
- "Byzantine Musical Symbols"
- ),
- new (0x1D100, 0x1D1FF, "Musical Symbols"),
- new (
- 0x1D300,
- 0x1D35F,
- "Tai Xuan Jing Symbols"
- ),
- new (
- 0x1D400,
- 0x1D7FF,
- "Mathematical Alphanumeric Symbols"
- ),
- new (0x1F600, 0x1F532, "Emojis Symbols"),
- new (
- 0x20000,
- 0x2A6DF,
- "CJK Unified Ideographs Extension B"
- ),
- new (
- 0x2F800,
- 0x2FA1F,
- "CJK Compatibility Ideographs Supplement"
- ),
- new (0xE0000, 0xE007F, "Tags")
- ];
- private Scheme? _alternatingScheme;
- private DataTable? _currentTable;
- private Scheme? _redScheme;
- private Scheme? _redSchemeAlt;
- private TableView? _tableView;
- /// <summary>
- /// Builds a simple table in which cell values contents are the index of the cell. This helps testing that
- /// scrolling etc is working correctly and not skipping out any rows/columns when paging
- /// </summary>
- /// <param name="cols"></param>
- /// <param name="rows"></param>
- /// <returns></returns>
- public static DataTable BuildSimpleDataTable (int cols, int rows)
- {
- var dt = new DataTable ();
- for (var c = 0; c < cols; c++)
- {
- dt.Columns.Add ("Col" + c);
- }
- for (var r = 0; r < rows; r++)
- {
- DataRow newRow = dt.NewRow ();
- for (var c = 0; c < cols; c++)
- {
- newRow [c] = $"R{r}C{c}";
- }
- dt.Rows.Add (newRow);
- }
- return dt;
- }
- public override void Main ()
- {
- // Init
- Application.Init ();
- // Setup - Create a top-level application window and configure it.
- Runnable appWindow = new ();
- _tableView = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) };
- var menu = new MenuBar ();
- // File menu
- menu.Add (
- new MenuBarItem (
- "_File",
- [
- new MenuItem { Title = "_OpenBigExample", Action = () => OpenExample (true) },
- new MenuItem { Title = "_OpenSmallExample", Action = () => OpenExample (false) },
- new MenuItem { Title = "OpenCharacter_Map", Action = OpenUnicodeMap },
- new MenuItem { Title = "OpenTreeExample", Action = OpenTreeExample },
- new MenuItem { Title = "_CloseExample", Action = CloseExample },
- new MenuItem { Title = "_Quit", Action = Quit }
- ]
- )
- );
- // View menu - created with helper method due to complexity
- menu.Add (CreateViewMenu ());
- // Column menu
- menu.Add (
- new MenuBarItem (
- "_Column",
- [
- new MenuItem { Title = "_Set Max Width", Action = SetMaxWidth },
- new MenuItem { Title = "_Set Min Width", Action = SetMinWidth },
- new MenuItem { Title = "_Set MinAcceptableWidth", Action = SetMinAcceptableWidth },
- new MenuItem { Title = "_Set All MinAcceptableWidth=1", Action = SetMinAcceptableWidthToOne }
- ]
- )
- );
- appWindow.Add (menu);
- var selectedCellLabel = new Label
- {
- Text = "0,0"
- };
- var statusBar = new StatusBar (
- [
- new (
- Application.QuitKey,
- "Quit",
- Quit
- ),
- new (
- Key.F2,
- "OpenExample",
- () => OpenExample (true)
- ),
- new (
- Key.F3,
- "CloseExample",
- CloseExample
- ),
- new (
- Key.F4,
- "OpenSimple",
- () => OpenSimple (true)
- ),
- new ()
- {
- HelpText = "Cell:",
- CommandView = selectedCellLabel
- }
- ]
- )
- {
- AlignmentModes = AlignmentModes.IgnoreFirstOrLast
- };
- appWindow.Add (statusBar);
- appWindow.Add (_tableView);
- _tableView!.SelectedCellChanged += (s, e) => { selectedCellLabel.Text = $"{_tableView!.SelectedRow},{_tableView!.SelectedColumn}"; };
- _tableView!.CellActivated += EditCurrentCell;
- _tableView!.KeyDown += TableViewKeyPress;
- //SetupScrollBar ();
- _redScheme = new ()
- {
- Disabled = appWindow.GetAttributeForRole (VisualRole.Disabled),
- HotFocus = appWindow.GetAttributeForRole (VisualRole.HotFocus),
- Focus = appWindow.GetAttributeForRole (VisualRole.Focus),
- Normal = new (Color.Red, appWindow.GetAttributeForRole (VisualRole.Normal).Background)
- };
- _alternatingScheme = new ()
- {
- Disabled = appWindow.GetAttributeForRole (VisualRole.Disabled),
- HotFocus = appWindow.GetAttributeForRole (VisualRole.HotFocus),
- Focus = appWindow.GetAttributeForRole (VisualRole.Focus),
- Normal = new (Color.White, Color.BrightBlue)
- };
- _redSchemeAlt = new ()
- {
- Disabled = appWindow.GetAttributeForRole (VisualRole.Disabled),
- HotFocus = appWindow.GetAttributeForRole (VisualRole.HotFocus),
- Focus = appWindow.GetAttributeForRole (VisualRole.Focus),
- Normal = new (Color.Red, Color.BrightBlue)
- };
- // if user clicks the mouse in TableView
- _tableView!.Activating += (s, e) =>
- {
- if (_currentTable == null)
- {
- return;
- }
- // Only handle mouse clicks
- if (e.Context is not CommandContext<MouseBinding> { Binding.MouseEventArgs: { } mouseArgs })
- {
- return;
- }
- _tableView!.ScreenToCell (mouseArgs.Position, out int? clickedCol);
- if (clickedCol != null)
- {
- if (mouseArgs.Flags.HasFlag (MouseFlags.Button1Clicked))
- {
- // left click in a header
- SortColumn (clickedCol.Value);
- }
- else if (mouseArgs.Flags.HasFlag (MouseFlags.Button3Clicked))
- {
- // right click in a header
- ShowHeaderContextMenu (clickedCol.Value, mouseArgs);
- }
- }
- };
- _tableView!.KeyBindings.ReplaceCommands (Key.Space, Command.Accept);
- // Run - Start the application.
- Application.Run (appWindow);
- appWindow.Dispose ();
- // Shutdown - Calling Application.Shutdown is required.
- Application.Shutdown ();
- }
- private MenuBarItem CreateViewMenu ()
- {
- // Store checkbox references for the toggle methods to access
- Dictionary<string, CheckBox> checkboxes = new ();
- MenuItem CreateCheckBoxMenuItem (string key, string title, bool initialState, Action<bool> onToggle)
- {
- CheckBox checkBox = new ()
- {
- Title = title,
- CheckedState = initialState ? CheckState.Checked : CheckState.UnChecked
- };
- checkBox.CheckedStateChanged += (s, e) => onToggle (checkBox.CheckedState == CheckState.Checked);
- MenuItem item = new () { CommandView = checkBox };
- item.Accepting += (s, e) =>
- {
- checkBox.AdvanceCheckState ();
- e.Handled = true;
- };
- checkboxes [key] = checkBox;
- return item;
- }
- return new (
- "_View",
- [
- CreateCheckBoxMenuItem (
- "ShowHeaders",
- "_ShowHeaders",
- _tableView!.Style.ShowHeaders,
- state =>
- {
- _tableView!.Style.ShowHeaders = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "AlwaysShowHeaders",
- "_AlwaysShowHeaders",
- _tableView!.Style.AlwaysShowHeaders,
- state =>
- {
- _tableView!.Style.AlwaysShowHeaders = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "HeaderOverline",
- "_HeaderOverLine",
- _tableView!.Style.ShowHorizontalHeaderOverline,
- state =>
- {
- _tableView!.Style.ShowHorizontalHeaderOverline = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "HeaderMidline",
- "_HeaderMidLine",
- _tableView!.Style.ShowVerticalHeaderLines,
- state =>
- {
- _tableView!.Style.ShowVerticalHeaderLines = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "HeaderUnderline",
- "_HeaderUnderLine",
- _tableView!.Style.ShowHorizontalHeaderUnderline,
- state =>
- {
- _tableView!.Style.ShowHorizontalHeaderUnderline = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "Bottomline",
- "_BottomLine",
- _tableView!.Style.ShowHorizontalBottomline,
- state =>
- {
- _tableView!.Style.ShowHorizontalBottomline = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "HorizontalScrollIndicators",
- "_HorizontalScrollIndicators",
- _tableView!.Style.ShowHorizontalScrollIndicators,
- state =>
- {
- _tableView!.Style.ShowHorizontalScrollIndicators = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "FullRowSelect",
- "_FullRowSelect",
- _tableView!.FullRowSelect,
- state =>
- {
- _tableView!.FullRowSelect = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "CellLines",
- "_CellLines",
- _tableView!.Style.ShowVerticalCellLines,
- state =>
- {
- _tableView!.Style.ShowVerticalCellLines = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "ExpandLastColumn",
- "_ExpandLastColumn",
- _tableView!.Style.ExpandLastColumn,
- state =>
- {
- _tableView!.Style.ExpandLastColumn = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "AlwaysUseNormalColorForVerticalCellLines",
- "_AlwaysUseNormalColorForVerticalCellLines",
- _tableView!.Style.AlwaysUseNormalColorForVerticalCellLines,
- state =>
- {
- _tableView!.Style.AlwaysUseNormalColorForVerticalCellLines = state;
- _tableView!.Update ();
- }
- ),
- CreateCheckBoxMenuItem (
- "SmoothScrolling",
- "_SmoothHorizontalScrolling",
- _tableView!.Style.SmoothHorizontalScrolling,
- state =>
- {
- _tableView!.Style.SmoothHorizontalScrolling = state;
- _tableView!.Update ();
- }
- ),
- new MenuItem
- {
- Title = "_AllLines",
- Action = () =>
- {
- _tableView!.Style.ShowHorizontalHeaderOverline = true;
- _tableView!.Style.ShowVerticalHeaderLines = true;
- _tableView!.Style.ShowHorizontalHeaderUnderline = true;
- _tableView!.Style.ShowVerticalCellLines = true;
- checkboxes ["HeaderOverline"].CheckedState = CheckState.Checked;
- checkboxes ["HeaderMidline"].CheckedState = CheckState.Checked;
- checkboxes ["HeaderUnderline"].CheckedState = CheckState.Checked;
- checkboxes ["CellLines"].CheckedState = CheckState.Checked;
- _tableView!.Update ();
- }
- },
- new MenuItem
- {
- Title = "_NoLines",
- Action = () =>
- {
- _tableView!.Style.ShowHorizontalHeaderOverline = false;
- _tableView!.Style.ShowVerticalHeaderLines = false;
- _tableView!.Style.ShowHorizontalHeaderUnderline = false;
- _tableView!.Style.ShowVerticalCellLines = false;
- checkboxes ["HeaderOverline"].CheckedState = CheckState.UnChecked;
- checkboxes ["HeaderMidline"].CheckedState = CheckState.UnChecked;
- checkboxes ["HeaderUnderline"].CheckedState = CheckState.UnChecked;
- checkboxes ["CellLines"].CheckedState = CheckState.UnChecked;
- _tableView!.Update ();
- }
- },
- CreateCheckBoxMenuItem (
- "Checkboxes",
- "_Checkboxes",
- false,
- state =>
- {
- if (state)
- {
- ToggleCheckboxes (false);
- checkboxes ["Radioboxes"].CheckedState = CheckState.UnChecked;
- }
- else if (HasCheckboxes ())
- {
- ToggleCheckboxes (false);
- }
- }
- ),
- CreateCheckBoxMenuItem (
- "Radioboxes",
- "_Radioboxes",
- false,
- state =>
- {
- if (state)
- {
- ToggleCheckboxes (true);
- checkboxes ["Checkboxes"].CheckedState = CheckState.UnChecked;
- }
- else if (HasCheckboxes ())
- {
- ToggleCheckboxes (true);
- }
- }
- ),
- CreateCheckBoxMenuItem (
- "AlternatingColors",
- "Alternating Colors",
- false,
- state =>
- {
- if (state)
- {
- _tableView!.Style.RowColorGetter = a => { return a.RowIndex % 2 == 0 ? _alternatingScheme : null; };
- }
- else
- {
- _tableView!.Style.RowColorGetter = null;
- }
- _tableView!.SetNeedsDraw ();
- }
- ),
- CreateCheckBoxMenuItem (
- "Cursor",
- "Invert Selected Cell First Character",
- _tableView!.Style.InvertSelectedCellFirstCharacter,
- state =>
- {
- _tableView!.Style.InvertSelectedCellFirstCharacter = state;
- _tableView!.SetNeedsDraw ();
- }
- ),
- new MenuItem { Title = "_ClearColumnStyles", Action = ClearColumnStyles },
- new MenuItem { Title = "Sho_w All Columns", Action = ShowAllColumns }
- ]
- );
- }
- protected override void Dispose (bool disposing)
- {
- base.Dispose (disposing);
- foreach (IDisposable d in _toDispose!)
- {
- d.Dispose ();
- }
- }
- private DataTable BuildUnicodeMap ()
- {
- var dt = new DataTable ();
- // add cols called 0 to 9
- for (var i = 0; i < 10; i++)
- {
- DataColumn col = dt.Columns.Add (i.ToString (), typeof (uint));
- ColumnStyle style = _tableView!.Style.GetOrCreateColumnStyle (col.Ordinal);
- style.RepresentationGetter = o => new Rune ((uint)o).ToString ();
- }
- // add cols called a to z
- for (int i = 'a'; i < 'a' + 26; i++)
- {
- DataColumn col = dt.Columns.Add (((char)i).ToString (), typeof (uint));
- ColumnStyle style = _tableView!.Style.GetOrCreateColumnStyle (col.Ordinal);
- style.RepresentationGetter = o => new Rune ((uint)o).ToString ();
- }
- // now add table contents
- List<uint> runes = new ();
- foreach (UnicodeRange range in _ranges!)
- {
- for (uint i = range.Start; i <= range.End; i++)
- {
- runes.Add (i);
- }
- }
- DataRow? dr = null;
- for (var i = 0; i < runes.Count; i++)
- {
- if (dr == null || i % dt.Columns.Count == 0)
- {
- dr = dt.Rows.Add ();
- }
- dr [i % dt.Columns.Count] = runes [i].ToString ();
- }
- return dt;
- }
- private void CheckOrUncheckFile (FileSystemInfo info, bool check)
- {
- if (check)
- {
- _checkedFileSystemInfos!.Add (info);
- }
- else
- {
- _checkedFileSystemInfos!.Remove (info);
- }
- }
- private void ClearColumnStyles ()
- {
- _tableView!.Style.ColumnStyles.Clear ();
- _tableView!.Update ();
- }
- private void CloseExample () { _tableView!.Table = null; }
- private void EditCurrentCell (object? sender, CellActivatedEventArgs e)
- {
- if (e.Table is not DataTableSource || _currentTable == null)
- {
- return;
- }
- int tableCol = ToTableCol (e.Col);
- if (tableCol < 0)
- {
- return;
- }
- object o = _currentTable.Rows [e.Row] [tableCol];
- string title = o is uint u ? GetUnicodeCategory (u) + $"(0x{o:X4})" : "Enter new value";
- var oldValue = _currentTable.Rows [e.Row] [tableCol].ToString ();
- var okPressed = false;
- var ok = new Button { Text = "Ok", IsDefault = true };
- ok.Accepting += (s, e) =>
- {
- okPressed = true;
- Application.RequestStop ();
- };
- var cancel = new Button { Text = "Cancel" };
- cancel.Accepting += (s, e) => { Application.RequestStop (); };
- var d = new Dialog { Title = title, Buttons = [ok, cancel] };
- var lbl = new Label { X = 0, Y = 1, Text = _tableView!.Table.ColumnNames [e.Col] };
- var tf = new TextField { Text = oldValue, X = 0, Y = 2, Width = Dim.Fill () };
- d.Add (lbl, tf);
- tf.SetFocus ();
- Application.Run (d);
- d.Dispose ();
- if (okPressed)
- {
- try
- {
- _currentTable.Rows [e.Row] [tableCol] =
- string.IsNullOrWhiteSpace (tf.Text) ? DBNull.Value : tf.Text;
- }
- catch (Exception ex)
- {
- MessageBox.ErrorQuery ((sender as View)?.App, 60, 20, "Failed to set text", ex.Message, "Ok");
- }
- _tableView!.Update ();
- }
- }
- private IEnumerable<FileSystemInfo> GetChildren (FileSystemInfo arg)
- {
- try
- {
- return arg is DirectoryInfo d ? d.GetFileSystemInfos () : Enumerable.Empty<FileSystemInfo> ();
- }
- catch (Exception)
- {
- // Permission denied etc
- return [];
- }
- }
- private int? GetColumn ()
- {
- if (_tableView!.Table == null)
- {
- return null;
- }
- if (_tableView!.SelectedColumn < 0 || _tableView!.SelectedColumn > _tableView!.Table.Columns)
- {
- return null;
- }
- return _tableView!.SelectedColumn;
- }
- private string GetHumanReadableFileSize (FileSystemInfo fsi)
- {
- if (fsi is not FileInfo fi)
- {
- return string.Empty;
- }
- long value = fi.Length;
- CultureInfo culture = CultureInfo.CurrentUICulture;
- return GetHumanReadableFileSize (value, culture);
- }
- private string GetHumanReadableFileSize (long value, CultureInfo culture)
- {
- const long ByteConversion = 1024;
- string [] SizeSuffixes = { "bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
- if (value < 0)
- {
- return "-" + GetHumanReadableFileSize (-value, culture);
- }
- if (value == 0)
- {
- return "0.0 bytes";
- }
- var mag = (int)Math.Log (value, ByteConversion);
- double adjustedSize = value / Math.Pow (1000, mag);
- return string.Format (culture.NumberFormat, "{0:n2} {1}", adjustedSize, SizeSuffixes [mag]);
- }
- private string GetProposedNewSortOrder (int clickedCol, out bool isAsc)
- {
- // work out new sort order
- string sort = _currentTable!.DefaultView.Sort;
- string colName = _tableView!.Table.ColumnNames [clickedCol];
- if (sort?.EndsWith ("ASC") ?? false)
- {
- sort = $"{colName} DESC";
- isAsc = false;
- }
- else
- {
- sort = $"{colName} ASC";
- isAsc = true;
- }
- return sort;
- }
- private string GetUnicodeCategory (uint u) { return _ranges!.FirstOrDefault (r => u >= r.Start && u <= r.End)?.Category ?? "Unknown"; }
- private bool HasCheckboxes () => _tableView!.Table is CheckBoxTableSourceWrapperBase;
- private void HideColumn (int clickedCol)
- {
- ColumnStyle style = _tableView!.Style.GetOrCreateColumnStyle (clickedCol);
- style.Visible = false;
- _tableView!.Update ();
- }
- private void OpenExample (bool big)
- {
- SetTable (TableView.BuildDemoDataTable (big ? 30 : 5, big ? 1000 : 5));
- SetDemoTableStyles ();
- }
- private void OpenSimple (bool big) { SetTable (BuildSimpleDataTable (big ? 30 : 5, big ? 1000 : 5)); }
- private void OpenTreeExample ()
- {
- _tableView!.Style.ColumnStyles.Clear ();
- TreeView<FileSystemInfo> tree = new ()
- {
- AspectGetter = f => f.Name, TreeBuilder = new DelegateTreeBuilder<FileSystemInfo> (GetChildren)
- };
- TreeTableSource<FileSystemInfo> source = new (
- _tableView,
- "Name",
- tree,
- new ()
- {
- { "Extension", f => f.Extension },
- { "CreationTime", f => f.CreationTime },
- { "FileSize", GetHumanReadableFileSize }
- }
- );
- HashSet<string> seen = new ();
- try
- {
- foreach (string path in Environment.GetLogicalDrives ())
- {
- tree.AddObject (new DirectoryInfo (path));
- }
- }
- catch (Exception e)
- {
- MessageBox.ErrorQuery (_tableView?.App, "Could not find local drives", e.Message, "Ok");
- }
- _tableView!.Table = source;
- _toDispose?.Add (tree);
- }
- private void OpenUnicodeMap ()
- {
- SetTable (BuildUnicodeMap ());
- _tableView?.Update ();
- }
- private void Quit () { Application.RequestStop (); }
- private void RunColumnWidthDialog (
- int? col,
- string prompt,
- Action<ColumnStyle, int> setter,
- Func<ColumnStyle, int> getter
- )
- {
- if (col == null)
- {
- return;
- }
- var accepted = false;
- var ok = new Button { Text = "Ok", IsDefault = true };
- ok.Accepting += (s, e) =>
- {
- accepted = true;
- (s as View)?.App?.RequestStop ();
- };
- var cancel = new Button { Text = "Cancel" };
- cancel.Accepting += (s, e) => { (s as View)?.App?.RequestStop (); };
- var d = new Dialog
- {
- Title = prompt,
- Buttons = [ok, cancel]
- };
- ColumnStyle style = _tableView!.Style.GetOrCreateColumnStyle (col.Value);
- var lbl = new Label { X = 0, Y = 0, Text = $"{_tableView!.Table.ColumnNames [col.Value]}: " };
- var tf = new TextField { Text = getter (style).ToString (), X = Pos.Right (lbl), Y = 0, Width = 20 };
- d.Add (lbl, tf);
- tf.SetFocus ();
- _tableView.App?.Run (d);
- d.Dispose ();
- if (accepted)
- {
- try
- {
- setter (style, int.Parse (tf.Text));
- }
- catch (Exception ex)
- {
- MessageBox.ErrorQuery (_tableView.App, 60, 20, "Failed to set", ex.Message, "Ok");
- }
- _tableView!.Update ();
- }
- }
- private void SetDemoTableStyles ()
- {
- _tableView!.Style.ColumnStyles.Clear ();
- var alignMid = new ColumnStyle { Alignment = Alignment.Center };
- var alignRight = new ColumnStyle { Alignment = Alignment.End };
- var dateFormatStyle = new ColumnStyle
- {
- Alignment = Alignment.End,
- RepresentationGetter = v =>
- v is DateTime d ? d.ToString ("yyyy-MM-dd") : v.ToString ()
- };
- var negativeRight = new ColumnStyle
- {
- Format = "0.##",
- MinWidth = 10,
- AlignmentGetter = v => v is double d
- ?
- // align negative values right
- d < 0
- ? Alignment.End
- :
- // align positive values left
- Alignment.Start
- :
- // not a double
- Alignment.Start,
- ColorGetter = a => a.CellValue is double d
- ?
- // color 0 and negative values red
- d <= 0.0000001
- ? a.RowIndex % 2 == 0
- ? _redSchemeAlt
- : _redScheme
- :
- // use normal scheme for positive values
- null
- :
- // not a double
- null
- };
- _tableView!.Style.ColumnStyles.Add (_currentTable!.Columns ["DateCol"]!.Ordinal, dateFormatStyle);
- _tableView!.Style.ColumnStyles.Add (_currentTable!.Columns ["DoubleCol"]!.Ordinal, negativeRight);
- _tableView!.Style.ColumnStyles.Add (_currentTable!.Columns ["NullsCol"]!.Ordinal, alignMid);
- _tableView!.Style.ColumnStyles.Add (_currentTable!.Columns ["IntCol"]!.Ordinal, alignRight);
- _tableView!.Update ();
- }
- private void SetMaxWidth ()
- {
- int? col = GetColumn ();
- RunColumnWidthDialog (col, "MaxWidth", (s, v) => s.MaxWidth = v, s => s.MaxWidth);
- }
- private void SetMinAcceptableWidth ()
- {
- int? col = GetColumn ();
- RunColumnWidthDialog (
- col,
- "MinAcceptableWidth",
- (s, v) => s.MinAcceptableWidth = v,
- s => s.MinAcceptableWidth
- );
- }
- private void SetMinAcceptableWidthToOne ()
- {
- for (var i = 0; i < _tableView!.Table.Columns; i++)
- {
- ColumnStyle style = _tableView!.Style.GetOrCreateColumnStyle (i);
- style.MinAcceptableWidth = 1;
- }
- }
- private void SetMinWidth ()
- {
- int? col = GetColumn ();
- RunColumnWidthDialog (col, "MinWidth", (s, v) => s.MinWidth = v, s => s.MinWidth);
- }
- private void SetTable (DataTable dataTable) { _tableView!.Table = new DataTableSource (_currentTable = dataTable); }
- //private void SetupScrollBar ()
- //{
- // var scrollBar = new ScrollBarView (_tableView, true);
- // scrollBar.ChangedPosition += (s, e) =>
- // {
- // _tableView!.RowOffset = scrollBar.Position;
- // if (_tableView!.RowOffset != scrollBar.Position)
- // {
- // scrollBar.Position = _tableView!.RowOffset;
- // }
- // _tableView!.SetNeedsDraw ();
- // };
- // /*
- // scrollBar.OtherScrollBarView.ChangedPosition += (s,e) => {
- // tableView.LeftItem = scrollBar.OtherScrollBarView.Position;
- // if (tableView.LeftItem != scrollBar.OtherScrollBarView.Position) {
- // scrollBar.OtherScrollBarView.Position = tableView.LeftItem;
- // }
- // tableView.SetNeedsDraw ();
- // };*/
- // _tableView!.DrawingContent += (s, e) =>
- // {
- // scrollBar.Size = _tableView!.Table?.Rows ?? 0;
- // scrollBar.Position = _tableView!.RowOffset;
- // //scrollBar.OtherScrollBarView.Size = tableView.Maxlength - 1;
- // //scrollBar.OtherScrollBarView.Position = tableView.LeftItem;
- // scrollBar.Refresh ();
- // };
- //}
- private void ShowAllColumns ()
- {
- foreach (KeyValuePair<int, ColumnStyle> colStyle in _tableView!.Style.ColumnStyles)
- {
- colStyle.Value.Visible = true;
- }
- _tableView!.Update ();
- }
- private void ShowHeaderContextMenu (int clickedCol, MouseEventArgs e)
- {
- if (HasCheckboxes () && clickedCol == 0)
- {
- return;
- }
- string sort = GetProposedNewSortOrder (clickedCol, out bool isAsc);
- string colName = _tableView!.Table.ColumnNames [clickedCol];
- PopoverMenu? contextMenu = new (
- [
- new (
- $"Hide {TrimArrows (colName)}",
- "",
- () => HideColumn (clickedCol)
- ),
- new (
- $"Sort {StripArrows (sort)}",
- "",
- () => SortColumn (clickedCol, sort, isAsc)
- )
- ]);
- // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused
- // and the context menu is disposed when it is closed.
- e.View?.App!.Popover?.Register (contextMenu);
- contextMenu?.MakeVisible (new (e.ScreenPosition.X + 1, e.ScreenPosition.Y + 1));
- }
- private void SortColumn (int clickedCol)
- {
- string sort = GetProposedNewSortOrder (clickedCol, out bool isAsc);
- // don't try to sort on the toggled column
- if (HasCheckboxes () && clickedCol == 0)
- {
- return;
- }
- SortColumn (clickedCol, sort, isAsc);
- }
- private void SortColumn (int clickedCol, string sort, bool isAsc)
- {
- if (_currentTable == null)
- {
- return;
- }
- // set a sort order
- _currentTable.DefaultView.Sort = sort;
- // copy the rows from the view
- DataTable sortedCopy = _currentTable.DefaultView.ToTable ();
- _currentTable.Rows.Clear ();
- foreach (DataRow r in sortedCopy.Rows)
- {
- _currentTable.ImportRow (r);
- }
- foreach (DataColumn col in _currentTable.Columns)
- {
- // remove any lingering sort indicator
- col.ColumnName = TrimArrows (col.ColumnName);
- // add a new one if this the one that is being sorted
- if (col.Ordinal == clickedCol)
- {
- col.ColumnName += isAsc ? Glyphs.UpArrow : Glyphs.DownArrow;
- }
- }
- _tableView!.Update ();
- }
- private string StripArrows (string columnName) => columnName.Replace ($"{Glyphs.DownArrow}", "").Replace ($"{Glyphs.UpArrow}", "");
- private void TableViewKeyPress (object? sender, Key e)
- {
- if (_currentTable == null)
- {
- return;
- }
- if (e.KeyCode == KeyCode.Delete)
- {
- if (_tableView!.FullRowSelect)
- {
- // Delete button deletes all rows when in full row mode
- foreach (int toRemove in _tableView!.GetAllSelectedCells ()
- .Select (p => p.Y)
- .Distinct ()
- .OrderByDescending (i => i))
- {
- _currentTable.Rows.RemoveAt (toRemove);
- }
- }
- else
- {
- // otherwise set all selected cells to null
- foreach (Point pt in _tableView!.GetAllSelectedCells ())
- {
- _currentTable.Rows [pt.Y] [pt.X] = DBNull.Value;
- }
- }
- _tableView!.Update ();
- e.Handled = true;
- }
- }
- private void ToggleCheckboxes (bool radio)
- {
- if (_tableView!.Table is CheckBoxTableSourceWrapperBase wrapper)
- {
- // unwrap it to remove check boxes
- _tableView!.Table = wrapper.Wrapping;
- // if toggling off checkboxes/radio
- if (wrapper.UseRadioButtons == radio)
- {
- return;
- }
- }
- ITableSource source;
- // Either toggling on checkboxes/radio or switching from radio to checkboxes (or vice versa)
- if (_tableView!.Table is TreeTableSource<FileSystemInfo> treeSource)
- {
- source = new CheckBoxTableSourceWrapperByObject<FileSystemInfo> (
- _tableView,
- treeSource,
- _checkedFileSystemInfos!.Contains,
- CheckOrUncheckFile
- )
- { UseRadioButtons = radio };
- }
- else
- {
- source = new CheckBoxTableSourceWrapperByIndex (_tableView, _tableView!.Table) { UseRadioButtons = radio };
- }
- _tableView!.Table = source;
- }
- private int ToTableCol (int col)
- {
- if (HasCheckboxes ())
- {
- return col - 1;
- }
- return col;
- }
- private string TrimArrows (string columnName) =>
- columnName.TrimEnd (
- (char)Glyphs.UpArrow.Value,
- (char)Glyphs.DownArrow.Value
- );
- public class UnicodeRange (uint start, uint end, string category)
- {
- public readonly string Category = category;
- public readonly uint End = end;
- public readonly uint Start = start;
- }
- }
|