HexEditor.cs 9.5 KB

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