Editor.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using Terminal.Gui;
  5. using System.Linq;
  6. using System.Text.RegularExpressions;
  7. using System.Threading;
  8. using System.Globalization;
  9. namespace UICatalog.Scenarios {
  10. [ScenarioMetadata (Name: "Editor", Description: "A Text Editor using the TextView control.")]
  11. [ScenarioCategory ("Controls")]
  12. [ScenarioCategory ("Dialogs")]
  13. [ScenarioCategory ("Text and Formatting")]
  14. [ScenarioCategory ("Top Level Windows")]
  15. [ScenarioCategory ("Files and IO")]
  16. [ScenarioCategory ("TextView")]
  17. public class Editor : Scenario {
  18. private string _fileName = "demo.txt";
  19. private TextView _textView;
  20. private bool _saved = true;
  21. private ScrollBarView _scrollBar;
  22. private byte [] _originalText;
  23. private string _textToFind;
  24. private string _textToReplace;
  25. private bool _matchCase;
  26. private bool _matchWholeWord;
  27. private Window _winDialog;
  28. private TabView _tabView;
  29. private MenuItem _miForceMinimumPosToZero;
  30. private bool _forceMinimumPosToZero = true;
  31. private readonly List<CultureInfo> _cultureInfos = Application.SupportedCultures;
  32. public override void Init (Toplevel top, ColorScheme colorScheme)
  33. {
  34. Application.Init ();
  35. Top = top;
  36. if (Top == null) {
  37. Top = Application.Top;
  38. }
  39. Win = new Window (_fileName ?? "Untitled") {
  40. X = 0,
  41. Y = 1,
  42. Width = Dim.Fill (),
  43. Height = Dim.Fill (),
  44. ColorScheme = colorScheme,
  45. };
  46. Top.Add (Win);
  47. _textView = new TextView () {
  48. X = 0,
  49. Y = 0,
  50. Width = Dim.Fill (),
  51. Height = Dim.Fill (),
  52. BottomOffset = 1,
  53. RightOffset = 1
  54. };
  55. CreateDemoFile (_fileName);
  56. var siCursorPosition = new StatusItem (Key.Null, "", null);
  57. _textView.UnwrappedCursorPosition += (e) => {
  58. siCursorPosition.Title = $"Ln {e.Y + 1}, Col {e.X + 1}";
  59. };
  60. LoadFile ();
  61. Win.Add (_textView);
  62. var menu = new MenuBar (new MenuBarItem [] {
  63. new MenuBarItem ("_File", new MenuItem [] {
  64. new MenuItem ("_New", "", () => New()),
  65. new MenuItem ("_Open", "", () => Open()),
  66. new MenuItem ("_Save", "", () => Save()),
  67. new MenuItem ("_Save As", "", () => SaveAs()),
  68. new MenuItem ("_Close", "", () => CloseFile()),
  69. null,
  70. new MenuItem ("_Quit", "", () => Quit()),
  71. }),
  72. new MenuBarItem ("_Edit", new MenuItem [] {
  73. new MenuItem ("_Copy", "", () => Copy(),null,null, Key.CtrlMask | Key.C),
  74. new MenuItem ("C_ut", "", () => Cut(),null,null, Key.CtrlMask | Key.W),
  75. new MenuItem ("_Paste", "", () => Paste(),null,null, Key.CtrlMask | Key.Y),
  76. null,
  77. new MenuItem ("_Find", "", () => Find(),null,null, Key.CtrlMask | Key.S),
  78. new MenuItem ("Find _Next", "", () => FindNext(),null,null, Key.CtrlMask | Key.ShiftMask | Key.S),
  79. new MenuItem ("Find P_revious", "", () => FindPrevious(),null,null, Key.CtrlMask | Key.ShiftMask | Key.AltMask | Key.S),
  80. new MenuItem ("_Replace", "", () => Replace(),null,null, Key.CtrlMask | Key.R),
  81. new MenuItem ("Replace Ne_xt", "", () => ReplaceNext(),null,null, Key.CtrlMask | Key.ShiftMask | Key.R),
  82. new MenuItem ("Replace Pre_vious", "", () => ReplacePrevious(),null,null, Key.CtrlMask | Key.ShiftMask | Key.AltMask | Key.R),
  83. new MenuItem ("Replace _All", "", () => ReplaceAll(),null,null, Key.CtrlMask | Key.ShiftMask | Key.AltMask | Key.A),
  84. null,
  85. new MenuItem ("_Select All", "", () => SelectAll(),null,null, Key.CtrlMask | Key.T)
  86. }),
  87. new MenuBarItem ("_ScrollBarView", CreateKeepChecked ()),
  88. new MenuBarItem ("_Cursor", CreateCursorRadio ()),
  89. new MenuBarItem ("Forma_t", new MenuItem [] {
  90. CreateWrapChecked (),
  91. CreateAutocomplete(),
  92. CreateAllowsTabChecked (),
  93. CreateReadOnlyChecked ()
  94. }),
  95. new MenuBarItem ("_Responder", new MenuItem [] {
  96. CreateCanFocusChecked (),
  97. CreateEnabledChecked (),
  98. CreateVisibleChecked ()
  99. }),
  100. new MenuBarItem ("Conte_xtMenu", new MenuItem [] {
  101. _miForceMinimumPosToZero = new MenuItem ("ForceMinimumPosTo_Zero", "", () => {
  102. _miForceMinimumPosToZero.Checked = _forceMinimumPosToZero = !_forceMinimumPosToZero;
  103. _textView.ContextMenu.ForceMinimumPosToZero = _forceMinimumPosToZero;
  104. }) { CheckType = MenuItemCheckStyle.Checked, Checked = _forceMinimumPosToZero },
  105. new MenuBarItem ("_Languages", GetSupportedCultures ())
  106. })
  107. });
  108. menu.UseSubMenusSingleFrame = true;
  109. Top.Add (menu);
  110. var statusBar = new StatusBar (new StatusItem [] {
  111. siCursorPosition,
  112. new StatusItem(Key.F2, "~F2~ Open", () => Open()),
  113. new StatusItem(Key.F3, "~F3~ Save", () => Save()),
  114. new StatusItem(Key.F4, "~F4~ Save As", () => SaveAs()),
  115. new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
  116. new StatusItem(Key.Null, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null)
  117. });
  118. Top.Add (statusBar);
  119. _scrollBar = new ScrollBarView (_textView, true);
  120. _scrollBar.ChangedPosition += () => {
  121. _textView.TopRow = _scrollBar.Position;
  122. if (_textView.TopRow != _scrollBar.Position) {
  123. _scrollBar.Position = _textView.TopRow;
  124. }
  125. _textView.SetNeedsDisplay ();
  126. };
  127. _scrollBar.OtherScrollBarView.ChangedPosition += () => {
  128. _textView.LeftColumn = _scrollBar.OtherScrollBarView.Position;
  129. if (_textView.LeftColumn != _scrollBar.OtherScrollBarView.Position) {
  130. _scrollBar.OtherScrollBarView.Position = _textView.LeftColumn;
  131. }
  132. _textView.SetNeedsDisplay ();
  133. };
  134. _scrollBar.VisibleChanged += () => {
  135. if (_scrollBar.Visible && _textView.RightOffset == 0) {
  136. _textView.RightOffset = 1;
  137. } else if (!_scrollBar.Visible && _textView.RightOffset == 1) {
  138. _textView.RightOffset = 0;
  139. }
  140. };
  141. _scrollBar.OtherScrollBarView.VisibleChanged += () => {
  142. if (_scrollBar.OtherScrollBarView.Visible && _textView.BottomOffset == 0) {
  143. _textView.BottomOffset = 1;
  144. } else if (!_scrollBar.OtherScrollBarView.Visible && _textView.BottomOffset == 1) {
  145. _textView.BottomOffset = 0;
  146. }
  147. };
  148. _textView.DrawContent += (e) => {
  149. _scrollBar.Size = _textView.Lines;
  150. _scrollBar.Position = _textView.TopRow;
  151. if (_scrollBar.OtherScrollBarView != null) {
  152. _scrollBar.OtherScrollBarView.Size = _textView.Maxlength;
  153. _scrollBar.OtherScrollBarView.Position = _textView.LeftColumn;
  154. }
  155. _scrollBar.LayoutSubviews ();
  156. _scrollBar.Refresh ();
  157. };
  158. Win.KeyPress += (e) => {
  159. var keys = ShortcutHelper.GetModifiersKey (e.KeyEvent);
  160. if (_winDialog != null && (e.KeyEvent.Key == Key.Esc
  161. || e.KeyEvent.Key == (Key.Q | Key.CtrlMask))) {
  162. DisposeWinDialog ();
  163. } else if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) {
  164. Quit ();
  165. e.Handled = true;
  166. } else if (_winDialog != null && keys == (Key.Tab | Key.CtrlMask)) {
  167. if (_tabView.SelectedTab == _tabView.Tabs.ElementAt (_tabView.Tabs.Count - 1)) {
  168. _tabView.SelectedTab = _tabView.Tabs.ElementAt (0);
  169. } else {
  170. _tabView.SwitchTabBy (1);
  171. }
  172. e.Handled = true;
  173. } else if (_winDialog != null && keys == (Key.Tab | Key.CtrlMask | Key.ShiftMask)) {
  174. if (_tabView.SelectedTab == _tabView.Tabs.ElementAt (0)) {
  175. _tabView.SelectedTab = _tabView.Tabs.ElementAt (_tabView.Tabs.Count - 1);
  176. } else {
  177. _tabView.SwitchTabBy (-1);
  178. }
  179. e.Handled = true;
  180. }
  181. };
  182. Top.Closed += (_) => Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
  183. }
  184. private void DisposeWinDialog ()
  185. {
  186. _winDialog.Dispose ();
  187. Win.Remove (_winDialog);
  188. _winDialog = null;
  189. }
  190. public override void Setup ()
  191. {
  192. }
  193. private void New (bool checkChanges = true)
  194. {
  195. if (checkChanges && !CanCloseFile ()) {
  196. return;
  197. }
  198. Win.Title = "Untitled.txt";
  199. _fileName = null;
  200. _originalText = new System.IO.MemoryStream ().ToArray ();
  201. _textView.Text = _originalText;
  202. }
  203. private void LoadFile ()
  204. {
  205. if (_fileName != null) {
  206. // FIXED: BUGBUG: #452 TextView.LoadFile keeps file open and provides no way of closing it
  207. _textView.LoadFile (_fileName);
  208. //_textView.Text = System.IO.File.ReadAllText (_fileName);
  209. _originalText = _textView.Text.ToByteArray ();
  210. Win.Title = _fileName;
  211. _saved = true;
  212. }
  213. }
  214. private void Paste ()
  215. {
  216. if (_textView != null) {
  217. _textView.Paste ();
  218. }
  219. }
  220. private void Cut ()
  221. {
  222. if (_textView != null) {
  223. _textView.Cut ();
  224. }
  225. }
  226. private void Copy ()
  227. {
  228. if (_textView != null) {
  229. _textView.Copy ();
  230. }
  231. }
  232. private void SelectAll ()
  233. {
  234. _textView.SelectAll ();
  235. }
  236. private void Find ()
  237. {
  238. CreateFindReplace ();
  239. }
  240. private void FindNext ()
  241. {
  242. ContinueFind ();
  243. }
  244. private void FindPrevious ()
  245. {
  246. ContinueFind (false);
  247. }
  248. private void ContinueFind (bool next = true, bool replace = false)
  249. {
  250. if (!replace && string.IsNullOrEmpty (_textToFind)) {
  251. Find ();
  252. return;
  253. } else if (replace && (string.IsNullOrEmpty (_textToFind)
  254. || (_winDialog == null && string.IsNullOrEmpty (_textToReplace)))) {
  255. Replace ();
  256. return;
  257. }
  258. bool found;
  259. bool gaveFullTurn;
  260. if (next) {
  261. if (!replace) {
  262. found = _textView.FindNextText (_textToFind, out gaveFullTurn, _matchCase, _matchWholeWord);
  263. } else {
  264. found = _textView.FindNextText (_textToFind, out gaveFullTurn, _matchCase, _matchWholeWord,
  265. _textToReplace, true);
  266. }
  267. } else {
  268. if (!replace) {
  269. found = _textView.FindPreviousText (_textToFind, out gaveFullTurn, _matchCase, _matchWholeWord);
  270. } else {
  271. found = _textView.FindPreviousText (_textToFind, out gaveFullTurn, _matchCase, _matchWholeWord,
  272. _textToReplace, true);
  273. }
  274. }
  275. if (!found) {
  276. MessageBox.Query ("Find", $"The following specified text was not found: '{_textToFind}'", "Ok");
  277. } else if (gaveFullTurn) {
  278. MessageBox.Query ("Find", $"No more occurrences were found for the following specified text: '{_textToFind}'", "Ok");
  279. }
  280. }
  281. private void Replace ()
  282. {
  283. CreateFindReplace (false);
  284. }
  285. private void ReplaceNext ()
  286. {
  287. ContinueFind (true, true);
  288. }
  289. private void ReplacePrevious ()
  290. {
  291. ContinueFind (false, true);
  292. }
  293. private void ReplaceAll ()
  294. {
  295. if (string.IsNullOrEmpty (_textToFind) || (string.IsNullOrEmpty (_textToReplace) && _winDialog == null)) {
  296. Replace ();
  297. return;
  298. }
  299. if (_textView.ReplaceAllText (_textToFind, _matchCase, _matchWholeWord, _textToReplace)) {
  300. MessageBox.Query ("Replace All", $"All occurrences were replaced for the following specified text: '{_textToReplace}'", "Ok");
  301. } else {
  302. MessageBox.Query ("Replace All", $"None of the following specified text was found: '{_textToFind}'", "Ok");
  303. }
  304. }
  305. private bool CanCloseFile ()
  306. {
  307. if (_textView.Text == _originalText) {
  308. System.Diagnostics.Debug.Assert (!_textView.IsDirty);
  309. return true;
  310. }
  311. System.Diagnostics.Debug.Assert (_textView.IsDirty);
  312. var r = MessageBox.ErrorQuery ("Save File",
  313. $"Do you want save changes in {Win.Title}?", "Yes", "No", "Cancel");
  314. if (r == 0) {
  315. return Save ();
  316. } else if (r == 1) {
  317. return true;
  318. }
  319. return false;
  320. }
  321. private void Open ()
  322. {
  323. if (!CanCloseFile ()) {
  324. return;
  325. }
  326. var aTypes = new List<string> () { ".txt;.bin;.xml;.json", ".txt", ".bin", ".xml", ".*" };
  327. var d = new OpenDialog ("Open", "Choose the path where to open the file.", aTypes) { AllowsMultipleSelection = false };
  328. Application.Run (d);
  329. if (!d.Canceled && d.FilePaths.Count > 0) {
  330. _fileName = d.FilePaths [0];
  331. LoadFile ();
  332. }
  333. }
  334. private bool Save ()
  335. {
  336. if (_fileName != null) {
  337. // FIXED: BUGBUG: #279 TextView does not know how to deal with \r\n, only \r
  338. // As a result files saved on Windows and then read back will show invalid chars.
  339. return SaveFile (Win.Title.ToString (), _fileName);
  340. } else {
  341. return SaveAs ();
  342. }
  343. }
  344. private bool SaveAs ()
  345. {
  346. var aTypes = new List<string> () { ".txt", ".bin", ".xml", ".*" };
  347. var sd = new SaveDialog ("Save file", "Choose the path where to save the file.", aTypes);
  348. sd.FilePath = System.IO.Path.Combine (sd.FilePath.ToString (), Win.Title.ToString ());
  349. Application.Run (sd);
  350. if (!sd.Canceled) {
  351. if (System.IO.File.Exists (sd.FilePath.ToString ())) {
  352. if (MessageBox.Query ("Save File",
  353. "File already exists. Overwrite any way?", "No", "Ok") == 1) {
  354. return SaveFile (sd.FileName.ToString (), sd.FilePath.ToString ());
  355. } else {
  356. _saved = false;
  357. return _saved;
  358. }
  359. } else {
  360. return SaveFile (sd.FileName.ToString (), sd.FilePath.ToString ());
  361. }
  362. } else {
  363. _saved = false;
  364. return _saved;
  365. }
  366. }
  367. private bool SaveFile (string title, string file)
  368. {
  369. try {
  370. Win.Title = title;
  371. _fileName = file;
  372. System.IO.File.WriteAllText (_fileName, _textView.Text.ToString ());
  373. _originalText = _textView.Text.ToByteArray ();
  374. _saved = true;
  375. _textView.ClearHistoryChanges ();
  376. MessageBox.Query ("Save File", "File was successfully saved.", "Ok");
  377. } catch (Exception ex) {
  378. MessageBox.ErrorQuery ("Error", ex.Message, "Ok");
  379. return false;
  380. }
  381. return true;
  382. }
  383. private void CloseFile ()
  384. {
  385. if (!CanCloseFile ()) {
  386. return;
  387. }
  388. try {
  389. _textView.CloseFile ();
  390. New (false);
  391. } catch (Exception ex) {
  392. MessageBox.ErrorQuery ("Error", ex.Message, "Ok");
  393. }
  394. }
  395. private void Quit ()
  396. {
  397. if (!CanCloseFile ()) {
  398. return;
  399. }
  400. Application.RequestStop ();
  401. }
  402. private void CreateDemoFile (string fileName)
  403. {
  404. var sb = new StringBuilder ();
  405. // FIXED: BUGBUG: #279 TextView does not know how to deal with \r\n, only \r
  406. sb.Append ("Hello world.\n");
  407. sb.Append ("This is a test of the Emergency Broadcast System.\n");
  408. for (int i = 0; i < 30; i++) {
  409. sb.Append ($"{i} - This is a test with a very long line and many lines to test the ScrollViewBar against the TextView. - {i}\n");
  410. }
  411. var sw = System.IO.File.CreateText (fileName);
  412. sw.Write (sb.ToString ());
  413. sw.Close ();
  414. }
  415. private MenuItem [] GetSupportedCultures ()
  416. {
  417. List<MenuItem> supportedCultures = new List<MenuItem> ();
  418. var index = -1;
  419. foreach (var c in _cultureInfos) {
  420. var culture = new MenuItem {
  421. CheckType = MenuItemCheckStyle.Checked
  422. };
  423. if (index == -1) {
  424. culture.Title = "_English";
  425. culture.Help = "en-US";
  426. culture.Checked = Thread.CurrentThread.CurrentUICulture.Name == "en-US";
  427. CreateAction (supportedCultures, culture);
  428. supportedCultures.Add (culture);
  429. index++;
  430. culture = new MenuItem {
  431. CheckType = MenuItemCheckStyle.Checked
  432. };
  433. }
  434. culture.Title = $"_{c.Parent.EnglishName}";
  435. culture.Help = c.Name;
  436. culture.Checked = Thread.CurrentThread.CurrentUICulture.Name == c.Name;
  437. CreateAction (supportedCultures, culture);
  438. supportedCultures.Add (culture);
  439. }
  440. return supportedCultures.ToArray ();
  441. void CreateAction (List<MenuItem> supportedCultures, MenuItem culture)
  442. {
  443. culture.Action += () => {
  444. Thread.CurrentThread.CurrentUICulture = new CultureInfo (culture.Help.ToString ());
  445. culture.Checked = true;
  446. foreach (var item in supportedCultures) {
  447. item.Checked = item.Help.ToString () == Thread.CurrentThread.CurrentUICulture.Name;
  448. }
  449. };
  450. }
  451. }
  452. private MenuItem [] CreateKeepChecked ()
  453. {
  454. var item = new MenuItem ();
  455. item.Title = "Keep Content Always In Viewport";
  456. item.CheckType |= MenuItemCheckStyle.Checked;
  457. item.Checked = true;
  458. item.Action += () => _scrollBar.KeepContentAlwaysInViewport = item.Checked = !item.Checked;
  459. return new MenuItem [] { item };
  460. }
  461. private MenuItem CreateWrapChecked ()
  462. {
  463. var item = new MenuItem {
  464. Title = "Word Wrap"
  465. };
  466. item.CheckType |= MenuItemCheckStyle.Checked;
  467. item.Checked = _textView.WordWrap;
  468. item.Action += () => {
  469. _textView.WordWrap = item.Checked = !item.Checked;
  470. if (_textView.WordWrap) {
  471. _scrollBar.OtherScrollBarView.ShowScrollIndicator = false;
  472. _textView.BottomOffset = 0;
  473. } else {
  474. _textView.BottomOffset = 1;
  475. }
  476. };
  477. return item;
  478. }
  479. private MenuItem CreateAutocomplete ()
  480. {
  481. var auto = new MenuItem ();
  482. auto.Title = "Autocomplete";
  483. auto.CheckType |= MenuItemCheckStyle.Checked;
  484. auto.Checked = false;
  485. auto.Action += () => {
  486. if (auto.Checked = !auto.Checked) {
  487. // setup autocomplete with all words currently in the editor
  488. _textView.Autocomplete.AllSuggestions =
  489. Regex.Matches (_textView.Text.ToString (), "\\w+")
  490. .Select (s => s.Value)
  491. .Distinct ().ToList ();
  492. } else {
  493. _textView.Autocomplete.AllSuggestions.Clear ();
  494. }
  495. };
  496. return auto;
  497. }
  498. private MenuItem CreateAllowsTabChecked ()
  499. {
  500. var item = new MenuItem {
  501. Title = "Allows Tab"
  502. };
  503. item.CheckType |= MenuItemCheckStyle.Checked;
  504. item.Checked = _textView.AllowsTab;
  505. item.Action += () => {
  506. _textView.AllowsTab = item.Checked = !item.Checked;
  507. };
  508. return item;
  509. }
  510. private MenuItem CreateReadOnlyChecked ()
  511. {
  512. var item = new MenuItem {
  513. Title = "Read Only"
  514. };
  515. item.CheckType |= MenuItemCheckStyle.Checked;
  516. item.Checked = _textView.ReadOnly;
  517. item.Action += () => _textView.ReadOnly = item.Checked = !item.Checked;
  518. return item;
  519. }
  520. private MenuItem CreateCanFocusChecked ()
  521. {
  522. var item = new MenuItem {
  523. Title = "CanFocus"
  524. };
  525. item.CheckType |= MenuItemCheckStyle.Checked;
  526. item.Checked = _textView.CanFocus;
  527. item.Action += () => {
  528. _textView.CanFocus = item.Checked = !item.Checked;
  529. if (_textView.CanFocus) {
  530. _textView.SetFocus ();
  531. }
  532. };
  533. return item;
  534. }
  535. private MenuItem CreateEnabledChecked ()
  536. {
  537. var item = new MenuItem {
  538. Title = "Enabled"
  539. };
  540. item.CheckType |= MenuItemCheckStyle.Checked;
  541. item.Checked = _textView.Enabled;
  542. item.Action += () => {
  543. _textView.Enabled = item.Checked = !item.Checked;
  544. if (_textView.Enabled) {
  545. _textView.SetFocus ();
  546. }
  547. };
  548. return item;
  549. }
  550. private MenuItem CreateVisibleChecked ()
  551. {
  552. var item = new MenuItem {
  553. Title = "Visible"
  554. };
  555. item.CheckType |= MenuItemCheckStyle.Checked;
  556. item.Checked = _textView.Visible;
  557. item.Action += () => {
  558. _textView.Visible = item.Checked = !item.Checked;
  559. if (_textView.Visible) {
  560. _textView.SetFocus ();
  561. }
  562. };
  563. return item;
  564. }
  565. MenuItem [] CreateCursorRadio ()
  566. {
  567. List<MenuItem> menuItems = new List<MenuItem> ();
  568. menuItems.Add (new MenuItem ("_Invisible", "", () => SetCursor (CursorVisibility.Invisible)) {
  569. CheckType = MenuItemCheckStyle.Radio,
  570. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Invisible
  571. });
  572. menuItems.Add (new MenuItem ("_Box", "", () => SetCursor (CursorVisibility.Box)) {
  573. CheckType = MenuItemCheckStyle.Radio,
  574. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Box
  575. });
  576. menuItems.Add (new MenuItem ("_Underline", "", () => SetCursor (CursorVisibility.Underline)) {
  577. CheckType = MenuItemCheckStyle.Radio,
  578. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Underline
  579. });
  580. menuItems.Add (new MenuItem ("", "", () => { }, () => false));
  581. menuItems.Add (new MenuItem ("xTerm :", "", () => { }, () => false));
  582. menuItems.Add (new MenuItem ("", "", () => { }, () => false));
  583. menuItems.Add (new MenuItem (" _Default", "", () => SetCursor (CursorVisibility.Default)) {
  584. CheckType = MenuItemCheckStyle.Radio,
  585. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Default
  586. });
  587. menuItems.Add (new MenuItem (" _Vertical", "", () => SetCursor (CursorVisibility.Vertical)) {
  588. CheckType = MenuItemCheckStyle.Radio,
  589. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Vertical
  590. });
  591. menuItems.Add (new MenuItem (" V_ertical Fix", "", () => SetCursor (CursorVisibility.VerticalFix)) {
  592. CheckType = MenuItemCheckStyle.Radio,
  593. Checked = _textView.DesiredCursorVisibility == CursorVisibility.VerticalFix
  594. });
  595. menuItems.Add (new MenuItem (" B_ox Fix", "", () => SetCursor (CursorVisibility.BoxFix)) {
  596. CheckType = MenuItemCheckStyle.Radio,
  597. Checked = _textView.DesiredCursorVisibility == CursorVisibility.BoxFix
  598. });
  599. menuItems.Add (new MenuItem (" U_nderline Fix", "", () => SetCursor (CursorVisibility.UnderlineFix)) {
  600. CheckType = MenuItemCheckStyle.Radio,
  601. Checked = _textView.DesiredCursorVisibility == CursorVisibility.UnderlineFix
  602. });
  603. void SetCursor (CursorVisibility visibility)
  604. {
  605. _textView.DesiredCursorVisibility = visibility;
  606. var title = "";
  607. switch (visibility) {
  608. case CursorVisibility.Default:
  609. title = " _Default";
  610. break;
  611. case CursorVisibility.Invisible:
  612. title = "_Invisible";
  613. break;
  614. case CursorVisibility.Underline:
  615. title = "_Underline";
  616. break;
  617. case CursorVisibility.UnderlineFix:
  618. title = " U_nderline Fix";
  619. break;
  620. case CursorVisibility.Vertical:
  621. title = " _Vertical";
  622. break;
  623. case CursorVisibility.VerticalFix:
  624. title = " V_ertical Fix";
  625. break;
  626. case CursorVisibility.Box:
  627. title = "_Box";
  628. break;
  629. case CursorVisibility.BoxFix:
  630. title = " B_ox Fix";
  631. break;
  632. }
  633. foreach (var menuItem in menuItems) {
  634. menuItem.Checked = menuItem.Title.Equals (title) && visibility == _textView.DesiredCursorVisibility;
  635. }
  636. }
  637. return menuItems.ToArray ();
  638. }
  639. private void CreateFindReplace (bool isFind = true)
  640. {
  641. if (_winDialog != null) {
  642. _winDialog.SetFocus ();
  643. return;
  644. }
  645. _winDialog = new Window (isFind ? "Find" : "Replace") {
  646. X = Win.Bounds.Width / 2 - 30,
  647. Y = Win.Bounds.Height / 2 - 10,
  648. ColorScheme = Colors.TopLevel
  649. };
  650. _winDialog.Border.Effect3D = true;
  651. _tabView = new TabView () {
  652. X = 0,
  653. Y = 0,
  654. Width = Dim.Fill (),
  655. Height = Dim.Fill ()
  656. };
  657. _tabView.AddTab (new TabView.Tab ("Find", FindTab ()), isFind);
  658. var replace = ReplaceTab ();
  659. _tabView.AddTab (new TabView.Tab ("Replace", replace), !isFind);
  660. _tabView.SelectedTabChanged += (s, e) => _tabView.SelectedTab.View.FocusFirst ();
  661. _winDialog.Add (_tabView);
  662. Win.Add (_winDialog);
  663. _winDialog.Width = replace.Width + 4;
  664. _winDialog.Height = replace.Height + 4;
  665. _winDialog.SuperView.BringSubviewToFront (_winDialog);
  666. _winDialog.SetFocus ();
  667. }
  668. private void SetFindText ()
  669. {
  670. _textToFind = !_textView.SelectedText.IsEmpty
  671. ? _textView.SelectedText.ToString ()
  672. : string.IsNullOrEmpty (_textToFind) ? "" : _textToFind;
  673. _textToReplace = string.IsNullOrEmpty (_textToReplace) ? "" : _textToReplace;
  674. }
  675. private View FindTab ()
  676. {
  677. var d = new View ();
  678. d.DrawContent += (e) => {
  679. foreach (var v in d.Subviews) {
  680. v.SetNeedsDisplay ();
  681. }
  682. };
  683. var lblWidth = "Replace:".Length;
  684. var label = new Label ("Find:") {
  685. Y = 1,
  686. Width = lblWidth,
  687. TextAlignment = TextAlignment.Right,
  688. AutoSize = false
  689. };
  690. d.Add (label);
  691. SetFindText ();
  692. var txtToFind = new TextField (_textToFind) {
  693. X = Pos.Right (label) + 1,
  694. Y = Pos.Top (label),
  695. Width = 20
  696. };
  697. txtToFind.Enter += (_) => txtToFind.Text = _textToFind;
  698. d.Add (txtToFind);
  699. var btnFindNext = new Button ("Find _Next") {
  700. X = Pos.Right (txtToFind) + 1,
  701. Y = Pos.Top (label),
  702. Width = 20,
  703. Enabled = !txtToFind.Text.IsEmpty,
  704. TextAlignment = TextAlignment.Centered,
  705. IsDefault = true,
  706. AutoSize = false
  707. };
  708. btnFindNext.Clicked += () => FindNext ();
  709. d.Add (btnFindNext);
  710. var btnFindPrevious = new Button ("Find _Previous") {
  711. X = Pos.Right (txtToFind) + 1,
  712. Y = Pos.Top (btnFindNext) + 1,
  713. Width = 20,
  714. Enabled = !txtToFind.Text.IsEmpty,
  715. TextAlignment = TextAlignment.Centered,
  716. AutoSize = false
  717. };
  718. btnFindPrevious.Clicked += () => FindPrevious ();
  719. d.Add (btnFindPrevious);
  720. txtToFind.TextChanged += (e) => {
  721. _textToFind = txtToFind.Text.ToString ();
  722. _textView.FindTextChanged ();
  723. btnFindNext.Enabled = !txtToFind.Text.IsEmpty;
  724. btnFindPrevious.Enabled = !txtToFind.Text.IsEmpty;
  725. };
  726. var btnCancel = new Button ("Cancel") {
  727. X = Pos.Right (txtToFind) + 1,
  728. Y = Pos.Top (btnFindPrevious) + 2,
  729. Width = 20,
  730. TextAlignment = TextAlignment.Centered,
  731. AutoSize = false
  732. };
  733. btnCancel.Clicked += () => {
  734. DisposeWinDialog ();
  735. };
  736. d.Add (btnCancel);
  737. var ckbMatchCase = new CheckBox ("Match c_ase") {
  738. X = 0,
  739. Y = Pos.Top (txtToFind) + 2,
  740. Checked = _matchCase
  741. };
  742. ckbMatchCase.Toggled += (e) => _matchCase = ckbMatchCase.Checked;
  743. d.Add (ckbMatchCase);
  744. var ckbMatchWholeWord = new CheckBox ("Match _whole word") {
  745. X = 0,
  746. Y = Pos.Top (ckbMatchCase) + 1,
  747. Checked = _matchWholeWord
  748. };
  749. ckbMatchWholeWord.Toggled += (e) => _matchWholeWord = ckbMatchWholeWord.Checked;
  750. d.Add (ckbMatchWholeWord);
  751. d.Width = label.Width + txtToFind.Width + btnFindNext.Width + 2;
  752. d.Height = btnFindNext.Height + btnFindPrevious.Height + btnCancel.Height + 4;
  753. return d;
  754. }
  755. private View ReplaceTab ()
  756. {
  757. var d = new View ();
  758. d.DrawContent += (e) => {
  759. foreach (var v in d.Subviews) {
  760. v.SetNeedsDisplay ();
  761. }
  762. };
  763. var lblWidth = "Replace:".Length;
  764. var label = new Label ("Find:") {
  765. Y = 1,
  766. Width = lblWidth,
  767. TextAlignment = TextAlignment.Right,
  768. AutoSize = false
  769. };
  770. d.Add (label);
  771. SetFindText ();
  772. var txtToFind = new TextField (_textToFind) {
  773. X = Pos.Right (label) + 1,
  774. Y = Pos.Top (label),
  775. Width = 20
  776. };
  777. txtToFind.Enter += (_) => txtToFind.Text = _textToFind;
  778. d.Add (txtToFind);
  779. var btnFindNext = new Button ("Replace _Next") {
  780. X = Pos.Right (txtToFind) + 1,
  781. Y = Pos.Top (label),
  782. Width = 20,
  783. Enabled = !txtToFind.Text.IsEmpty,
  784. TextAlignment = TextAlignment.Centered,
  785. IsDefault = true,
  786. AutoSize = false
  787. };
  788. btnFindNext.Clicked += () => ReplaceNext ();
  789. d.Add (btnFindNext);
  790. label = new Label ("Replace:") {
  791. X = Pos.Left (label),
  792. Y = Pos.Top (label) + 1,
  793. Width = lblWidth,
  794. TextAlignment = TextAlignment.Right
  795. };
  796. d.Add (label);
  797. SetFindText ();
  798. var txtToReplace = new TextField (_textToReplace) {
  799. X = Pos.Right (label) + 1,
  800. Y = Pos.Top (label),
  801. Width = 20
  802. };
  803. txtToReplace.TextChanged += (e) => _textToReplace = txtToReplace.Text.ToString ();
  804. d.Add (txtToReplace);
  805. var btnFindPrevious = new Button ("Replace _Previous") {
  806. X = Pos.Right (txtToFind) + 1,
  807. Y = Pos.Top (btnFindNext) + 1,
  808. Width = 20,
  809. Enabled = !txtToFind.Text.IsEmpty,
  810. TextAlignment = TextAlignment.Centered,
  811. AutoSize = false
  812. };
  813. btnFindPrevious.Clicked += () => ReplacePrevious ();
  814. d.Add (btnFindPrevious);
  815. var btnReplaceAll = new Button ("Replace _All") {
  816. X = Pos.Right (txtToFind) + 1,
  817. Y = Pos.Top (btnFindPrevious) + 1,
  818. Width = 20,
  819. Enabled = !txtToFind.Text.IsEmpty,
  820. TextAlignment = TextAlignment.Centered,
  821. AutoSize = false
  822. };
  823. btnReplaceAll.Clicked += () => ReplaceAll ();
  824. d.Add (btnReplaceAll);
  825. txtToFind.TextChanged += (e) => {
  826. _textToFind = txtToFind.Text.ToString ();
  827. _textView.FindTextChanged ();
  828. btnFindNext.Enabled = !txtToFind.Text.IsEmpty;
  829. btnFindPrevious.Enabled = !txtToFind.Text.IsEmpty;
  830. btnReplaceAll.Enabled = !txtToFind.Text.IsEmpty;
  831. };
  832. var btnCancel = new Button ("Cancel") {
  833. X = Pos.Right (txtToFind) + 1,
  834. Y = Pos.Top (btnReplaceAll) + 1,
  835. Width = 20,
  836. TextAlignment = TextAlignment.Centered,
  837. AutoSize = false
  838. };
  839. btnCancel.Clicked += () => {
  840. DisposeWinDialog ();
  841. };
  842. d.Add (btnCancel);
  843. var ckbMatchCase = new CheckBox ("Match c_ase") {
  844. X = 0,
  845. Y = Pos.Top (txtToFind) + 2,
  846. Checked = _matchCase
  847. };
  848. ckbMatchCase.Toggled += (e) => _matchCase = ckbMatchCase.Checked;
  849. d.Add (ckbMatchCase);
  850. var ckbMatchWholeWord = new CheckBox ("Match _whole word") {
  851. X = 0,
  852. Y = Pos.Top (ckbMatchCase) + 1,
  853. Checked = _matchWholeWord
  854. };
  855. ckbMatchWholeWord.Toggled += (e) => _matchWholeWord = ckbMatchWholeWord.Checked;
  856. d.Add (ckbMatchWholeWord);
  857. d.Width = lblWidth + txtToFind.Width + btnFindNext.Width + 2;
  858. d.Height = btnFindNext.Height + btnFindPrevious.Height + btnCancel.Height + 4;
  859. return d;
  860. }
  861. }
  862. }