Editor.cs 27 KB

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