Editor.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4. using System.Linq;
  5. using System.Text.RegularExpressions;
  6. using System.Threading;
  7. using System.Globalization;
  8. using Terminal.Gui;
  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 List<CultureInfo> _cultureInfos;
  32. public override void Init ()
  33. {
  34. Application.Init ();
  35. _cultureInfos = Application.SupportedCultures;
  36. ConfigurationManager.Themes.Theme = Theme;
  37. ConfigurationManager.Apply ();
  38. Win = new Window () {
  39. Title = _fileName ?? "Untitled",
  40. X = 0,
  41. Y = 1,
  42. Width = Dim.Fill (),
  43. Height = Dim.Fill (),
  44. ColorScheme = Colors.ColorSchemes [TopLevelColorScheme],
  45. };
  46. Application.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. Application.Top.Add (menu);
  105. var siCursorPosition = new StatusItem (Key.Null, "", null);
  106. var statusBar = new StatusBar (new StatusItem [] {
  107. siCursorPosition,
  108. new StatusItem(Key.F2, "~F2~ Open", () => Open()),
  109. new StatusItem(Key.F3, "~F3~ Save", () => Save()),
  110. new StatusItem(Key.F4, "~F4~ Save As", () => SaveAs()),
  111. new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()),
  112. new StatusItem(Key.Null, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null)
  113. });
  114. _textView.UnwrappedCursorPosition += (s, e) => {
  115. siCursorPosition.Title = $"Ln {e.Point.Y + 1}, Col {e.Point.X + 1}";
  116. statusBar.SetNeedsDisplay ();
  117. };
  118. Application.Top.Add (statusBar);
  119. _scrollBar = new ScrollBarView (_textView, true);
  120. _scrollBar.ChangedPosition += (s, e) => {
  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 += (s, e) => {
  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 += (s, e) => {
  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 += (s, e) => {
  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 += (s, 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 += (s, e) => {
  159. var keys = ShortcutHelper.GetModifiersKey (e.KeyEvent);
  160. if (_winDialog != null && (e.KeyEvent.Key == Key.Esc
  161. || e.KeyEvent.Key == Application.QuitKey)) {
  162. DisposeWinDialog ();
  163. } else if (e.KeyEvent.Key == Application.QuitKey) {
  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. Application.Top.Closed += (s, e) => 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 = Encoding.Unicode.GetString (_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.Load (_fileName);
  208. //_textView.Text = System.IO.File.ReadAllText (_fileName);
  209. _originalText = Encoding.Unicode.GetBytes(_textView.Text);
  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 == Encoding.Unicode.GetString (_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<IAllowedType> () {
  327. new AllowedType("Text",".txt;.bin;.xml;.json", ".txt", ".bin", ".xml", ".json"),
  328. new AllowedTypeAny()
  329. };
  330. var d = new OpenDialog ("Open", aTypes) { AllowsMultipleSelection = false };
  331. Application.Run (d);
  332. if (!d.Canceled && d.FilePaths.Count > 0) {
  333. _fileName = d.FilePaths [0];
  334. LoadFile ();
  335. }
  336. }
  337. private bool Save ()
  338. {
  339. if (_fileName != null) {
  340. // FIXED: BUGBUG: #279 TextView does not know how to deal with \r\n, only \r
  341. // As a result files saved on Windows and then read back will show invalid chars.
  342. return SaveFile (Win.Title, _fileName);
  343. } else {
  344. return SaveAs ();
  345. }
  346. }
  347. private bool SaveAs ()
  348. {
  349. var aTypes = new List<IAllowedType> () {
  350. new AllowedType("Text Files", ".txt", ".bin", ".xml"),
  351. new AllowedTypeAny()
  352. };
  353. var sd = new SaveDialog ("Save file", aTypes);
  354. sd.Path = System.IO.Path.Combine (sd.FileName, Win.Title);
  355. Application.Run (sd);
  356. if (!sd.Canceled) {
  357. if (System.IO.File.Exists (sd.Path)) {
  358. if (MessageBox.Query ("Save File",
  359. "File already exists. Overwrite any way?", "No", "Ok") == 1) {
  360. return SaveFile (sd.FileName, sd.Path);
  361. } else {
  362. _saved = false;
  363. return _saved;
  364. }
  365. } else {
  366. return SaveFile (sd.FileName, sd.Path);
  367. }
  368. } else {
  369. _saved = false;
  370. return _saved;
  371. }
  372. }
  373. private bool SaveFile (string title, string file)
  374. {
  375. try {
  376. Win.Title = title;
  377. _fileName = file;
  378. System.IO.File.WriteAllText (_fileName, _textView.Text);
  379. _originalText = Encoding.Unicode.GetBytes(_textView.Text);
  380. _saved = true;
  381. _textView.ClearHistoryChanges ();
  382. MessageBox.Query ("Save File", "File was successfully saved.", "Ok");
  383. } catch (Exception ex) {
  384. MessageBox.ErrorQuery ("Error", ex.Message, "Ok");
  385. return false;
  386. }
  387. return true;
  388. }
  389. private void CloseFile ()
  390. {
  391. if (!CanCloseFile ()) {
  392. return;
  393. }
  394. try {
  395. _textView.CloseFile ();
  396. New (false);
  397. } catch (Exception ex) {
  398. MessageBox.ErrorQuery ("Error", ex.Message, "Ok");
  399. }
  400. }
  401. private void Quit ()
  402. {
  403. if (!CanCloseFile ()) {
  404. return;
  405. }
  406. Application.RequestStop ();
  407. }
  408. private void CreateDemoFile (string fileName)
  409. {
  410. var sb = new StringBuilder ();
  411. // FIXED: BUGBUG: #279 TextView does not know how to deal with \r\n, only \r
  412. sb.Append ("Hello world.\n");
  413. sb.Append ("This is a test of the Emergency Broadcast System.\n");
  414. for (int i = 0; i < 30; i++) {
  415. sb.Append ($"{i} - This is a test with a very long line and many lines to test the ScrollViewBar against the TextView. - {i}\n");
  416. }
  417. var sw = System.IO.File.CreateText (fileName);
  418. sw.Write (sb.ToString ());
  419. sw.Close ();
  420. }
  421. private MenuItem [] GetSupportedCultures ()
  422. {
  423. List<MenuItem> supportedCultures = new List<MenuItem> ();
  424. var index = -1;
  425. foreach (var c in _cultureInfos) {
  426. var culture = new MenuItem {
  427. CheckType = MenuItemCheckStyle.Checked
  428. };
  429. if (index == -1) {
  430. culture.Title = "_English";
  431. culture.Help = "en-US";
  432. culture.Checked = Thread.CurrentThread.CurrentUICulture.Name == "en-US";
  433. CreateAction (supportedCultures, culture);
  434. supportedCultures.Add (culture);
  435. index++;
  436. culture = new MenuItem {
  437. CheckType = MenuItemCheckStyle.Checked
  438. };
  439. }
  440. culture.Title = $"_{c.Parent.EnglishName}";
  441. culture.Help = c.Name;
  442. culture.Checked = Thread.CurrentThread.CurrentUICulture.Name == c.Name;
  443. CreateAction (supportedCultures, culture);
  444. supportedCultures.Add (culture);
  445. }
  446. return supportedCultures.ToArray ();
  447. void CreateAction (List<MenuItem> supportedCultures, MenuItem culture)
  448. {
  449. culture.Action += () => {
  450. Thread.CurrentThread.CurrentUICulture = new CultureInfo (culture.Help);
  451. culture.Checked = true;
  452. foreach (var item in supportedCultures) {
  453. item.Checked = item.Help == Thread.CurrentThread.CurrentUICulture.Name;
  454. }
  455. };
  456. }
  457. }
  458. private MenuItem [] CreateKeepChecked ()
  459. {
  460. var item = new MenuItem ();
  461. item.Title = "Keep Content Always In Viewport";
  462. item.CheckType |= MenuItemCheckStyle.Checked;
  463. item.Checked = true;
  464. item.Action += () => _scrollBar.KeepContentAlwaysInViewport = (bool)(item.Checked = !item.Checked);
  465. return new MenuItem [] { item };
  466. }
  467. private MenuItem CreateWrapChecked ()
  468. {
  469. var item = new MenuItem {
  470. Title = "Word Wrap"
  471. };
  472. item.CheckType |= MenuItemCheckStyle.Checked;
  473. item.Checked = _textView.WordWrap;
  474. item.Action += () => {
  475. _textView.WordWrap = (bool)(item.Checked = !item.Checked);
  476. if (_textView.WordWrap) {
  477. _scrollBar.OtherScrollBarView.ShowScrollIndicator = false;
  478. _textView.BottomOffset = 0;
  479. } else {
  480. _textView.BottomOffset = 1;
  481. }
  482. };
  483. return item;
  484. }
  485. private MenuItem CreateAutocomplete ()
  486. {
  487. var singleWordGenerator = new SingleWordSuggestionGenerator ();
  488. _textView.Autocomplete.SuggestionGenerator = singleWordGenerator;
  489. var auto = new MenuItem ();
  490. auto.Title = "Autocomplete";
  491. auto.CheckType |= MenuItemCheckStyle.Checked;
  492. auto.Checked = false;
  493. auto.Action += () => {
  494. if ((bool)(auto.Checked = !auto.Checked)) {
  495. // setup autocomplete with all words currently in the editor
  496. singleWordGenerator.AllSuggestions =
  497. Regex.Matches (_textView.Text, "\\w+")
  498. .Select (s => s.Value)
  499. .Distinct ().ToList ();
  500. } else {
  501. singleWordGenerator.AllSuggestions.Clear ();
  502. }
  503. };
  504. return auto;
  505. }
  506. private MenuItem CreateAllowsTabChecked ()
  507. {
  508. var item = new MenuItem {
  509. Title = "Allows Tab"
  510. };
  511. item.CheckType |= MenuItemCheckStyle.Checked;
  512. item.Checked = _textView.AllowsTab;
  513. item.Action += () => {
  514. _textView.AllowsTab = (bool)(item.Checked = !item.Checked);
  515. };
  516. return item;
  517. }
  518. private MenuItem CreateReadOnlyChecked ()
  519. {
  520. var item = new MenuItem {
  521. Title = "Read Only"
  522. };
  523. item.CheckType |= MenuItemCheckStyle.Checked;
  524. item.Checked = _textView.ReadOnly;
  525. item.Action += () => _textView.ReadOnly = (bool)(item.Checked = !item.Checked);
  526. return item;
  527. }
  528. private MenuItem CreateCanFocusChecked ()
  529. {
  530. var item = new MenuItem {
  531. Title = "CanFocus"
  532. };
  533. item.CheckType |= MenuItemCheckStyle.Checked;
  534. item.Checked = _textView.CanFocus;
  535. item.Action += () => {
  536. _textView.CanFocus = (bool)(item.Checked = !item.Checked);
  537. if (_textView.CanFocus) {
  538. _textView.SetFocus ();
  539. }
  540. };
  541. return item;
  542. }
  543. private MenuItem CreateEnabledChecked ()
  544. {
  545. var item = new MenuItem {
  546. Title = "Enabled"
  547. };
  548. item.CheckType |= MenuItemCheckStyle.Checked;
  549. item.Checked = _textView.Enabled;
  550. item.Action += () => {
  551. _textView.Enabled = (bool)(item.Checked = !item.Checked);
  552. if (_textView.Enabled) {
  553. _textView.SetFocus ();
  554. }
  555. };
  556. return item;
  557. }
  558. private MenuItem CreateVisibleChecked ()
  559. {
  560. var item = new MenuItem {
  561. Title = "Visible"
  562. };
  563. item.CheckType |= MenuItemCheckStyle.Checked;
  564. item.Checked = _textView.Visible;
  565. item.Action += () => {
  566. _textView.Visible = (bool)(item.Checked = !item.Checked);
  567. if (_textView.Visible) {
  568. _textView.SetFocus ();
  569. }
  570. };
  571. return item;
  572. }
  573. MenuItem [] CreateCursorRadio ()
  574. {
  575. List<MenuItem> menuItems = new List<MenuItem> ();
  576. menuItems.Add (new MenuItem ("_Invisible", "", () => SetCursor (CursorVisibility.Invisible)) {
  577. CheckType = MenuItemCheckStyle.Radio,
  578. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Invisible
  579. });
  580. menuItems.Add (new MenuItem ("_Box", "", () => SetCursor (CursorVisibility.Box)) {
  581. CheckType = MenuItemCheckStyle.Radio,
  582. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Box
  583. });
  584. menuItems.Add (new MenuItem ("_Underline", "", () => SetCursor (CursorVisibility.Underline)) {
  585. CheckType = MenuItemCheckStyle.Radio,
  586. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Underline
  587. });
  588. menuItems.Add (new MenuItem ("", "", () => { }, () => false));
  589. menuItems.Add (new MenuItem ("xTerm :", "", () => { }, () => false));
  590. menuItems.Add (new MenuItem ("", "", () => { }, () => false));
  591. menuItems.Add (new MenuItem (" _Default", "", () => SetCursor (CursorVisibility.Default)) {
  592. CheckType = MenuItemCheckStyle.Radio,
  593. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Default
  594. });
  595. menuItems.Add (new MenuItem (" _Vertical", "", () => SetCursor (CursorVisibility.Vertical)) {
  596. CheckType = MenuItemCheckStyle.Radio,
  597. Checked = _textView.DesiredCursorVisibility == CursorVisibility.Vertical
  598. });
  599. menuItems.Add (new MenuItem (" V_ertical Fix", "", () => SetCursor (CursorVisibility.VerticalFix)) {
  600. CheckType = MenuItemCheckStyle.Radio,
  601. Checked = _textView.DesiredCursorVisibility == CursorVisibility.VerticalFix
  602. });
  603. menuItems.Add (new MenuItem (" B_ox Fix", "", () => SetCursor (CursorVisibility.BoxFix)) {
  604. CheckType = MenuItemCheckStyle.Radio,
  605. Checked = _textView.DesiredCursorVisibility == CursorVisibility.BoxFix
  606. });
  607. menuItems.Add (new MenuItem (" U_nderline Fix", "", () => SetCursor (CursorVisibility.UnderlineFix)) {
  608. CheckType = MenuItemCheckStyle.Radio,
  609. Checked = _textView.DesiredCursorVisibility == CursorVisibility.UnderlineFix
  610. });
  611. void SetCursor (CursorVisibility visibility)
  612. {
  613. _textView.DesiredCursorVisibility = visibility;
  614. var title = "";
  615. switch (visibility) {
  616. case CursorVisibility.Default:
  617. title = " _Default";
  618. break;
  619. case CursorVisibility.Invisible:
  620. title = "_Invisible";
  621. break;
  622. case CursorVisibility.Underline:
  623. title = "_Underline";
  624. break;
  625. case CursorVisibility.UnderlineFix:
  626. title = " U_nderline Fix";
  627. break;
  628. case CursorVisibility.Vertical:
  629. title = " _Vertical";
  630. break;
  631. case CursorVisibility.VerticalFix:
  632. title = " V_ertical Fix";
  633. break;
  634. case CursorVisibility.Box:
  635. title = "_Box";
  636. break;
  637. case CursorVisibility.BoxFix:
  638. title = " B_ox Fix";
  639. break;
  640. }
  641. foreach (var menuItem in menuItems) {
  642. menuItem.Checked = menuItem.Title.Equals (title) && visibility == _textView.DesiredCursorVisibility;
  643. }
  644. }
  645. return menuItems.ToArray ();
  646. }
  647. private void CreateFindReplace (bool isFind = true)
  648. {
  649. if (_winDialog != null) {
  650. _winDialog.SetFocus ();
  651. return;
  652. }
  653. _winDialog = new Window () {
  654. Title = isFind ? "Find" : "Replace",
  655. X = Win.Bounds.Width / 2 - 30,
  656. Y = Win.Bounds.Height / 2 - 10,
  657. ColorScheme = Colors.TopLevel
  658. };
  659. _tabView = new TabView () {
  660. X = 0,
  661. Y = 0,
  662. Width = Dim.Fill (),
  663. Height = Dim.Fill ()
  664. };
  665. _tabView.AddTab (new Tab ("Find", FindTab ()), isFind);
  666. var replace = ReplaceTab ();
  667. _tabView.AddTab (new Tab ("Replace", replace), !isFind);
  668. _tabView.SelectedTabChanged += (s, e) => _tabView.SelectedTab.View.FocusFirst ();
  669. _winDialog.Add (_tabView);
  670. Win.Add (_winDialog);
  671. _winDialog.Width = replace.Width + 4;
  672. _winDialog.Height = replace.Height + 4;
  673. _winDialog.SuperView.BringSubviewToFront (_winDialog);
  674. _winDialog.SetFocus ();
  675. }
  676. private void SetFindText ()
  677. {
  678. _textToFind = !string.IsNullOrEmpty(_textView.SelectedText)
  679. ? _textView.SelectedText
  680. : string.IsNullOrEmpty (_textToFind) ? "" : _textToFind;
  681. _textToReplace = string.IsNullOrEmpty (_textToReplace) ? "" : _textToReplace;
  682. }
  683. private View FindTab ()
  684. {
  685. var d = new View ();
  686. d.DrawContent += (s, e) => {
  687. foreach (var v in d.Subviews) {
  688. v.SetNeedsDisplay ();
  689. }
  690. };
  691. var lblWidth = "Replace:".Length;
  692. var label = new Label ("Find:") {
  693. Y = 1,
  694. Width = lblWidth,
  695. TextAlignment = TextAlignment.Right,
  696. AutoSize = false
  697. };
  698. d.Add (label);
  699. SetFindText ();
  700. var txtToFind = new TextField (_textToFind) {
  701. X = Pos.Right (label) + 1,
  702. Y = Pos.Top (label),
  703. Width = 20
  704. };
  705. txtToFind.Enter += (s, e) => txtToFind.Text = _textToFind;
  706. d.Add (txtToFind);
  707. var btnFindNext = new Button ("Find _Next") {
  708. X = Pos.Right (txtToFind) + 1,
  709. Y = Pos.Top (label),
  710. Width = 20,
  711. Enabled = !string.IsNullOrEmpty(txtToFind.Text),
  712. TextAlignment = TextAlignment.Centered,
  713. IsDefault = true,
  714. AutoSize = false
  715. };
  716. btnFindNext.Clicked += (s, e) => FindNext ();
  717. d.Add (btnFindNext);
  718. var btnFindPrevious = new Button ("Find _Previous") {
  719. X = Pos.Right (txtToFind) + 1,
  720. Y = Pos.Top (btnFindNext) + 1,
  721. Width = 20,
  722. Enabled = !string.IsNullOrEmpty(txtToFind.Text),
  723. TextAlignment = TextAlignment.Centered,
  724. AutoSize = false
  725. };
  726. btnFindPrevious.Clicked += (s, e) => FindPrevious ();
  727. d.Add (btnFindPrevious);
  728. txtToFind.TextChanged += (s, e) => {
  729. _textToFind = txtToFind.Text;
  730. _textView.FindTextChanged ();
  731. btnFindNext.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
  732. btnFindPrevious.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
  733. };
  734. var btnCancel = new Button ("Cancel") {
  735. X = Pos.Right (txtToFind) + 1,
  736. Y = Pos.Top (btnFindPrevious) + 2,
  737. Width = 20,
  738. TextAlignment = TextAlignment.Centered,
  739. AutoSize = false
  740. };
  741. btnCancel.Clicked += (s, e) => {
  742. DisposeWinDialog ();
  743. };
  744. d.Add (btnCancel);
  745. var ckbMatchCase = new CheckBox ("Match c_ase") {
  746. X = 0,
  747. Y = Pos.Top (txtToFind) + 2,
  748. Checked = _matchCase
  749. };
  750. ckbMatchCase.Toggled += (s, e) => _matchCase = (bool)ckbMatchCase.Checked;
  751. d.Add (ckbMatchCase);
  752. var ckbMatchWholeWord = new CheckBox ("Match _whole word") {
  753. X = 0,
  754. Y = Pos.Top (ckbMatchCase) + 1,
  755. Checked = _matchWholeWord
  756. };
  757. ckbMatchWholeWord.Toggled += (s, e) => _matchWholeWord = (bool)ckbMatchWholeWord.Checked;
  758. d.Add (ckbMatchWholeWord);
  759. d.Width = label.Width + txtToFind.Width + btnFindNext.Width + 2;
  760. d.Height = btnFindNext.Height + btnFindPrevious.Height + btnCancel.Height + 4;
  761. return d;
  762. }
  763. private View ReplaceTab ()
  764. {
  765. var d = new View ();
  766. d.DrawContent += (s, e) => {
  767. foreach (var v in d.Subviews) {
  768. v.SetNeedsDisplay ();
  769. }
  770. };
  771. var lblWidth = "Replace:".Length;
  772. var label = new Label ("Find:") {
  773. Y = 1,
  774. Width = lblWidth,
  775. TextAlignment = TextAlignment.Right,
  776. AutoSize = false
  777. };
  778. d.Add (label);
  779. SetFindText ();
  780. var txtToFind = new TextField (_textToFind) {
  781. X = Pos.Right (label) + 1,
  782. Y = Pos.Top (label),
  783. Width = 20
  784. };
  785. txtToFind.Enter += (s, e) => txtToFind.Text = _textToFind;
  786. d.Add (txtToFind);
  787. var btnFindNext = new Button ("Replace _Next") {
  788. X = Pos.Right (txtToFind) + 1,
  789. Y = Pos.Top (label),
  790. Width = 20,
  791. Enabled = !string.IsNullOrEmpty(txtToFind.Text),
  792. TextAlignment = TextAlignment.Centered,
  793. IsDefault = true,
  794. AutoSize = false
  795. };
  796. btnFindNext.Clicked += (s, e) => ReplaceNext ();
  797. d.Add (btnFindNext);
  798. label = new Label ("Replace:") {
  799. X = Pos.Left (label),
  800. Y = Pos.Top (label) + 1,
  801. Width = lblWidth,
  802. TextAlignment = TextAlignment.Right
  803. };
  804. d.Add (label);
  805. SetFindText ();
  806. var txtToReplace = new TextField (_textToReplace) {
  807. X = Pos.Right (label) + 1,
  808. Y = Pos.Top (label),
  809. Width = 20
  810. };
  811. txtToReplace.TextChanged += (s, e) => _textToReplace = txtToReplace.Text;
  812. d.Add (txtToReplace);
  813. var btnFindPrevious = new Button ("Replace _Previous") {
  814. X = Pos.Right (txtToFind) + 1,
  815. Y = Pos.Top (btnFindNext) + 1,
  816. Width = 20,
  817. Enabled = !string.IsNullOrEmpty(txtToFind.Text),
  818. TextAlignment = TextAlignment.Centered,
  819. AutoSize = false
  820. };
  821. btnFindPrevious.Clicked += (s, e) => ReplacePrevious ();
  822. d.Add (btnFindPrevious);
  823. var btnReplaceAll = new Button ("Replace _All") {
  824. X = Pos.Right (txtToFind) + 1,
  825. Y = Pos.Top (btnFindPrevious) + 1,
  826. Width = 20,
  827. Enabled = !string.IsNullOrEmpty(txtToFind.Text),
  828. TextAlignment = TextAlignment.Centered,
  829. AutoSize = false
  830. };
  831. btnReplaceAll.Clicked += (s, e) => ReplaceAll ();
  832. d.Add (btnReplaceAll);
  833. txtToFind.TextChanged += (s, e) => {
  834. _textToFind = txtToFind.Text;
  835. _textView.FindTextChanged ();
  836. btnFindNext.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
  837. btnFindPrevious.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
  838. btnReplaceAll.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
  839. };
  840. var btnCancel = new Button ("Cancel") {
  841. X = Pos.Right (txtToFind) + 1,
  842. Y = Pos.Top (btnReplaceAll) + 1,
  843. Width = 20,
  844. TextAlignment = TextAlignment.Centered,
  845. AutoSize = false
  846. };
  847. btnCancel.Clicked += (s, e) => {
  848. DisposeWinDialog ();
  849. };
  850. d.Add (btnCancel);
  851. var ckbMatchCase = new CheckBox ("Match c_ase") {
  852. X = 0,
  853. Y = Pos.Top (txtToFind) + 2,
  854. Checked = _matchCase
  855. };
  856. ckbMatchCase.Toggled += (s, e) => _matchCase = (bool)ckbMatchCase.Checked;
  857. d.Add (ckbMatchCase);
  858. var ckbMatchWholeWord = new CheckBox ("Match _whole word") {
  859. X = 0,
  860. Y = Pos.Top (ckbMatchCase) + 1,
  861. Checked = _matchWholeWord
  862. };
  863. ckbMatchWholeWord.Toggled += (s, e) => _matchWholeWord = (bool)ckbMatchWholeWord.Checked;
  864. d.Add (ckbMatchWholeWord);
  865. d.Width = lblWidth + txtToFind.Width + btnFindNext.Width + 2;
  866. d.Height = btnFindNext.Height + btnFindPrevious.Height + btnCancel.Height + 4;
  867. return d;
  868. }
  869. }
  870. }