HexEditor.cs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. #nullable enable
  2. using System.Text;
  3. using Terminal.Gui;
  4. namespace UICatalog.Scenarios;
  5. [ScenarioMetadata ("HexEditor", "A binary (hex) editor using the HexView control.")]
  6. [ScenarioCategory ("Controls")]
  7. [ScenarioCategory ("Dialogs")]
  8. [ScenarioCategory ("Text and Formatting")]
  9. [ScenarioCategory ("Navigation")]
  10. [ScenarioCategory ("Files and IO")]
  11. public class HexEditor : Scenario
  12. {
  13. private string? _fileName;
  14. private HexView? _hexView;
  15. private MenuItemv2? _miAllowEdits;
  16. private bool _saved = true;
  17. private Shortcut? _scAddress;
  18. private Shortcut? _scInfo;
  19. private Shortcut? _scPosition;
  20. private StatusBar? _statusBar;
  21. public override void Main ()
  22. {
  23. Application.Init ();
  24. var app = new Toplevel
  25. {
  26. ColorScheme = Colors.ColorSchemes ["Base"]
  27. };
  28. _fileName = "demo.bin";
  29. CreateDemoFile (_fileName);
  30. _hexView = new (new MemoryStream (Encoding.UTF8.GetBytes ("Demo text.")))
  31. {
  32. X = 0,
  33. Y = 1,
  34. Width = Dim.Fill (),
  35. Height = Dim.Fill (1),
  36. Title = _fileName ?? "Untitled",
  37. BorderStyle = LineStyle.Rounded,
  38. };
  39. _hexView.Arrangement = ViewArrangement.Resizable;
  40. _hexView.Edited += _hexView_Edited;
  41. _hexView.PositionChanged += _hexView_PositionChanged;
  42. _hexView.VerticalScrollBar.AutoShow = false;
  43. app.Add (_hexView);
  44. var menu = new MenuBarv2
  45. {
  46. Menus =
  47. [
  48. new (
  49. "_File",
  50. new MenuItemv2 []
  51. {
  52. new ("_New", "", New),
  53. new ("_Open", "", Open),
  54. new ("_Save", "", Save),
  55. null!, // Passing null automatically creates a separator (a Line object).
  56. new ("_Quit", "", Quit)
  57. }
  58. ),
  59. new (
  60. "_Edit",
  61. new MenuItemv2 []
  62. {
  63. new ("_Copy", "", Copy),
  64. new ("C_ut", "", Cut),
  65. new ("_Paste", "", Paste)
  66. }
  67. ),
  68. new (
  69. "_Options",
  70. new MenuItemv2 []
  71. {
  72. _miAllowEdits = new (
  73. "_AllowEdits",
  74. "",
  75. ToggleAllowEdits
  76. )
  77. {
  78. }
  79. }
  80. )
  81. ]
  82. };
  83. CheckBox cb = new CheckBox ()
  84. {
  85. Title = _miAllowEdits.Title,
  86. CheckedState = _hexView.AllowEdits ? CheckState.Checked : CheckState.None,
  87. };
  88. _miAllowEdits.CommandView = cb;
  89. app.Add (menu);
  90. var addressWidthUpDown = new NumericUpDown
  91. {
  92. Value = _hexView.AddressWidth
  93. };
  94. NumericUpDown<long> addressUpDown = new NumericUpDown<long>
  95. {
  96. Value = _hexView.Address,
  97. Format = $"0x{{0:X{_hexView.AddressWidth}}}"
  98. };
  99. addressWidthUpDown.ValueChanging += (sender, args) =>
  100. {
  101. args.Cancel = args.NewValue is < 0 or > 8;
  102. if (!args.Cancel)
  103. {
  104. _hexView.AddressWidth = args.NewValue;
  105. // ReSharper disable once AccessToDisposedClosure
  106. addressUpDown.Format = $"0x{{0:X{_hexView.AddressWidth}}}";
  107. }
  108. };
  109. addressUpDown.ValueChanging += (sender, args) =>
  110. {
  111. args.Cancel = args.NewValue is < 0;
  112. if (!args.Cancel)
  113. {
  114. _hexView.Address = args.NewValue;
  115. }
  116. };
  117. _statusBar = new (
  118. [
  119. new (Key.F2, "Open", Open),
  120. new (Key.F3, "Save", Save),
  121. new ()
  122. {
  123. CommandView = addressWidthUpDown,
  124. HelpText = "Address Width"
  125. },
  126. _scAddress = new ()
  127. {
  128. CommandView = addressUpDown,
  129. HelpText = "Address:"
  130. },
  131. _scInfo = new (Key.Empty, string.Empty, () => { }),
  132. _scPosition = new (Key.Empty, string.Empty, () => { })
  133. ])
  134. {
  135. AlignmentModes = AlignmentModes.IgnoreFirstOrLast
  136. };
  137. app.Add (_statusBar);
  138. _hexView.VerticalScrollBar.AutoShow = true;
  139. _hexView.HorizontalScrollBar.AutoShow = true;
  140. _hexView.Source = LoadFile ();
  141. Application.Run (app);
  142. addressUpDown.Dispose ();
  143. addressWidthUpDown.Dispose ();
  144. app.Dispose ();
  145. Application.Shutdown ();
  146. }
  147. private void _hexView_Edited (object? sender, HexViewEditEventArgs e) { _saved = false; }
  148. private void _hexView_PositionChanged (object? sender, HexViewEventArgs obj)
  149. {
  150. _scInfo!.Title =
  151. $"Bytes: {_hexView!.Source!.Length}";
  152. _scPosition!.Title =
  153. $"L: {obj.Position.Y} C: {obj.Position.X} Per Line: {obj.BytesPerLine}";
  154. if (_scAddress!.CommandView is NumericUpDown<long> addrNumericUpDown)
  155. {
  156. addrNumericUpDown.Value = obj.Address;
  157. }
  158. }
  159. private void Copy () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok"); }
  160. private void CreateDemoFile (string fileName)
  161. {
  162. var sb = new StringBuilder ();
  163. sb.Append ("Hello world.\n");
  164. sb.Append ("This is a test of the Emergency Broadcast System.\n");
  165. StreamWriter sw = File.CreateText (fileName);
  166. sw.Write (sb.ToString ());
  167. sw.Close ();
  168. }
  169. private void CreateUnicodeDemoFile (string fileName)
  170. {
  171. var sb = new StringBuilder ();
  172. sb.Append ("Hello world with wide codepoints: 𝔹Aℝ𝔽.\n");
  173. sb.Append ("This is a test of the Emergency Broadcast System.\n");
  174. byte [] buffer = Encoding.Unicode.GetBytes (sb.ToString ());
  175. var ms = new MemoryStream (buffer);
  176. var file = new FileStream (fileName, FileMode.Create, FileAccess.Write);
  177. ms.WriteTo (file);
  178. file.Close ();
  179. ms.Close ();
  180. }
  181. private void Cut () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok"); }
  182. private Stream LoadFile ()
  183. {
  184. var stream = new MemoryStream ();
  185. if (!_saved && _hexView!.Edits.Count > 0 && _hexView.Source is {})
  186. {
  187. if (MessageBox.ErrorQuery (
  188. "Save",
  189. "The changes were not saved. Want to open without saving?",
  190. "_Yes",
  191. "_No"
  192. )
  193. == 1)
  194. {
  195. return _hexView.Source;
  196. }
  197. _hexView.DiscardEdits ();
  198. _saved = true;
  199. }
  200. if (_fileName is { })
  201. {
  202. byte [] bin = File.ReadAllBytes (_fileName);
  203. stream.Write (bin);
  204. _hexView!.Title = _fileName;
  205. _saved = true;
  206. }
  207. else
  208. {
  209. _hexView!.Title = _fileName ?? "Untitled";
  210. }
  211. return stream;
  212. }
  213. private void New ()
  214. {
  215. _fileName = null;
  216. _hexView!.Source = LoadFile ();
  217. }
  218. private void Open ()
  219. {
  220. var d = new OpenDialog { Title = "Open", AllowsMultipleSelection = false };
  221. Application.Run (d);
  222. if (!d.Canceled)
  223. {
  224. _fileName = d.FilePaths [0];
  225. _hexView!.Source = LoadFile ();
  226. //_hexView.DisplayStart = 0;
  227. }
  228. d.Dispose ();
  229. }
  230. private void Paste () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "_Ok"); }
  231. private void Quit () { Application.RequestStop (); }
  232. private void Save ()
  233. {
  234. if (_fileName != null)
  235. {
  236. using (var fs = new FileStream (_fileName, FileMode.OpenOrCreate))
  237. {
  238. _hexView?.ApplyEdits (fs);
  239. //_hexView.Source.Position = 0;
  240. //_hexView.Source.CopyTo (fs);
  241. //fs.Flush ();
  242. }
  243. _saved = true;
  244. }
  245. else
  246. {
  247. _hexView!.ApplyEdits ();
  248. }
  249. }
  250. private void ToggleAllowEdits ()
  251. {
  252. if (_miAllowEdits?.CommandView is not CheckBox cb)
  253. {
  254. return;
  255. }
  256. _hexView!.AllowEdits = cb.CheckedState == CheckState.Checked;
  257. }
  258. }