HexEditor.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. using System.IO;
  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 ("Overlapped")]
  10. [ScenarioCategory ("Files and IO")]
  11. public class HexEditor : Scenario
  12. {
  13. private string _fileName = "demo.bin";
  14. private HexView _hexView;
  15. private MenuItem _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. 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.Edited += _hexView_Edited;
  39. _hexView.PositionChanged += _hexView_PositionChanged;
  40. app.Add (_hexView);
  41. var menu = new MenuBar
  42. {
  43. Menus =
  44. [
  45. new (
  46. "_File",
  47. new MenuItem []
  48. {
  49. new ("_New", "", () => New ()),
  50. new ("_Open", "", () => Open ()),
  51. new ("_Save", "", () => Save ()),
  52. null,
  53. new ("_Quit", "", () => Quit ())
  54. }
  55. ),
  56. new (
  57. "_Edit",
  58. new MenuItem []
  59. {
  60. new ("_Copy", "", () => Copy ()),
  61. new ("C_ut", "", () => Cut ()),
  62. new ("_Paste", "", () => Paste ())
  63. }
  64. ),
  65. new (
  66. "_Options",
  67. new []
  68. {
  69. _miAllowEdits = new (
  70. "_AllowEdits",
  71. "",
  72. () => ToggleAllowEdits ()
  73. )
  74. {
  75. Checked = _hexView.AllowEdits,
  76. CheckType = MenuItemCheckStyle
  77. .Checked
  78. }
  79. }
  80. )
  81. ]
  82. };
  83. app.Add (menu);
  84. var addressWidthUpDown = new NumericUpDown
  85. {
  86. Value = _hexView.AddressWidth
  87. };
  88. NumericUpDown<long> addressUpDown = new NumericUpDown<long>
  89. {
  90. Value = _hexView.Address,
  91. Format = $"0x{{0:X{_hexView.AddressWidth}}}"
  92. };
  93. addressWidthUpDown.ValueChanging += (sender, args) =>
  94. {
  95. args.Cancel = args.NewValue is < 0 or > 8;
  96. if (!args.Cancel)
  97. {
  98. _hexView.AddressWidth = args.NewValue;
  99. // ReSharper disable once AccessToDisposedClosure
  100. addressUpDown.Format = $"0x{{0:X{_hexView.AddressWidth}}}";
  101. }
  102. };
  103. addressUpDown.ValueChanging += (sender, args) =>
  104. {
  105. args.Cancel = args.NewValue is < 0;
  106. if (!args.Cancel)
  107. {
  108. _hexView.Address = args.NewValue;
  109. }
  110. };
  111. _statusBar = new (
  112. [
  113. new (Key.F2, "Open", Open),
  114. new (Key.F3, "Save", Save),
  115. new ()
  116. {
  117. CommandView = addressWidthUpDown,
  118. HelpText = "Address Width"
  119. },
  120. _scAddress = new ()
  121. {
  122. CommandView = addressUpDown,
  123. HelpText = "Address:"
  124. },
  125. _scInfo = new (Key.Empty, string.Empty, () => { }),
  126. _scPosition = new (Key.Empty, string.Empty, () => { })
  127. ])
  128. {
  129. AlignmentModes = AlignmentModes.IgnoreFirstOrLast
  130. };
  131. app.Add (_statusBar);
  132. _hexView.Source = LoadFile ();
  133. Application.Run (app);
  134. addressUpDown.Dispose ();
  135. addressWidthUpDown.Dispose ();
  136. app.Dispose ();
  137. Application.Shutdown ();
  138. }
  139. private void _hexView_Edited (object sender, HexViewEditEventArgs e) { _saved = false; }
  140. private void _hexView_PositionChanged (object sender, HexViewEventArgs obj)
  141. {
  142. _scInfo.Title =
  143. $"Bytes: {_hexView.Source!.Length}";
  144. _scPosition.Title =
  145. $"L: {obj.CursorPosition.Y} C: {obj.CursorPosition.X} Per Line: {obj.BytesPerLine}";
  146. if (_scAddress.CommandView is NumericUpDown<long> addrNumericUpDown)
  147. {
  148. addrNumericUpDown.Value = obj.Address;
  149. }
  150. }
  151. private void Copy () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok"); }
  152. private void CreateDemoFile (string fileName)
  153. {
  154. var sb = new StringBuilder ();
  155. sb.Append ("Hello world.\n");
  156. sb.Append ("This is a test of the Emergency Broadcast System.\n");
  157. StreamWriter sw = File.CreateText (fileName);
  158. sw.Write (sb.ToString ());
  159. sw.Close ();
  160. }
  161. private void CreateUnicodeDemoFile (string fileName)
  162. {
  163. var sb = new StringBuilder ();
  164. sb.Append ("Hello world with wide codepoints: 𝔹Aℝ𝔽.\n");
  165. sb.Append ("This is a test of the Emergency Broadcast System.\n");
  166. byte [] buffer = Encoding.Unicode.GetBytes (sb.ToString ());
  167. var ms = new MemoryStream (buffer);
  168. var file = new FileStream (fileName, FileMode.Create, FileAccess.Write);
  169. ms.WriteTo (file);
  170. file.Close ();
  171. ms.Close ();
  172. }
  173. private void Cut () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "Ok"); }
  174. private Stream LoadFile ()
  175. {
  176. var stream = new MemoryStream ();
  177. if (!_saved && _hexView.Edits.Count > 0)
  178. {
  179. if (MessageBox.ErrorQuery (
  180. "Save",
  181. "The changes were not saved. Want to open without saving?",
  182. "_Yes",
  183. "_No"
  184. )
  185. == 1)
  186. {
  187. return _hexView.Source;
  188. }
  189. _hexView.DiscardEdits ();
  190. _saved = true;
  191. }
  192. if (_fileName != null)
  193. {
  194. byte [] bin = File.ReadAllBytes (_fileName);
  195. stream.Write (bin);
  196. _hexView.Title = _fileName;
  197. _saved = true;
  198. }
  199. else
  200. {
  201. _hexView.Title = _fileName ?? "Untitled";
  202. }
  203. return stream;
  204. }
  205. private void New ()
  206. {
  207. _fileName = null;
  208. _hexView.Source = LoadFile ();
  209. }
  210. private void Open ()
  211. {
  212. var d = new OpenDialog { Title = "Open", AllowsMultipleSelection = false };
  213. Application.Run (d);
  214. if (!d.Canceled)
  215. {
  216. _fileName = d.FilePaths [0];
  217. _hexView.Source = LoadFile ();
  218. _hexView.DisplayStart = 0;
  219. }
  220. d.Dispose ();
  221. }
  222. private void Paste () { MessageBox.ErrorQuery ("Not Implemented", "Functionality not yet implemented.", "_Ok"); }
  223. private void Quit () { Application.RequestStop (); }
  224. private void Save ()
  225. {
  226. if (_fileName != null)
  227. {
  228. using (var fs = new FileStream (_fileName, FileMode.OpenOrCreate))
  229. {
  230. _hexView.ApplyEdits (fs);
  231. //_hexView.Source.Position = 0;
  232. //_hexView.Source.CopyTo (fs);
  233. //fs.Flush ();
  234. }
  235. _saved = true;
  236. }
  237. else
  238. {
  239. _hexView.ApplyEdits ();
  240. }
  241. }
  242. private void ToggleAllowEdits () { _hexView.AllowEdits = (bool)(_miAllowEdits.Checked = !_miAllowEdits.Checked); }
  243. }