PageRenderTime 53ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/GitUI/Editor/FileViewer.cs

https://github.com/vbjay/gitextensions
C# | 988 lines | 832 code | 146 blank | 10 comment | 132 complexity | 6604a5a2d0de5b8e236c1dc3738a152f MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Drawing;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10. using GitCommands;
  11. using GitUI.Hotkey;
  12. using ICSharpCode.TextEditor.Util;
  13. using PatchApply;
  14. namespace GitUI.Editor
  15. {
  16. [DefaultEvent("SelectedLineChanged")]
  17. public partial class FileViewer : GitModuleControl
  18. {
  19. private readonly AsyncLoader _async;
  20. private int _currentScrollPos = -1;
  21. private bool _currentViewIsPatch;
  22. private readonly IFileViewer _internalFileViewer;
  23. public FileViewer()
  24. {
  25. TreatAllFilesAsText = false;
  26. ShowEntireFile = false;
  27. NumberOfVisibleLines = 3;
  28. InitializeComponent();
  29. Translate();
  30. GitUICommandsSourceSet += FileViewer_GitUICommandsSourceSet;
  31. if (GitCommands.Utils.EnvUtils.RunningOnWindows())
  32. _internalFileViewer = new FileViewerWindows();
  33. else
  34. _internalFileViewer = new FileViewerMono();
  35. _internalFileViewer.MouseEnter += _internalFileViewer_MouseEnter;
  36. _internalFileViewer.MouseLeave += _internalFileViewer_MouseLeave;
  37. _internalFileViewer.MouseMove += _internalFileViewer_MouseMove;
  38. var internalFileViewerControl = (Control)_internalFileViewer;
  39. internalFileViewerControl.Dock = DockStyle.Fill;
  40. Controls.Add(internalFileViewerControl);
  41. _async = new AsyncLoader();
  42. _async.LoadingError +=
  43. (sender, args) =>
  44. {
  45. ResetForText(null);
  46. _internalFileViewer.SetText("Unsupported file: \n\n" + args.Exception.Message);
  47. if (TextLoaded != null)
  48. TextLoaded(this, null);
  49. };
  50. IgnoreWhitespaceChanges = false;
  51. IsReadOnly = true;
  52. this.encodingToolStripComboBox.Items.AddRange(new Object[]
  53. {
  54. "Default (" + Encoding.Default.HeaderName + ")", "ASCII",
  55. "Unicode", "UTF7", "UTF8", "UTF32"
  56. });
  57. _internalFileViewer.MouseMove += TextAreaMouseMove;
  58. _internalFileViewer.MouseLeave += TextAreaMouseLeave;
  59. _internalFileViewer.TextChanged += TextEditor_TextChanged;
  60. _internalFileViewer.ScrollPosChanged += _internalFileViewer_ScrollPosChanged;
  61. _internalFileViewer.SelectedLineChanged += _internalFileViewer_SelectedLineChanged;
  62. _internalFileViewer.DoubleClick += (sender, args) => OnRequestDiffView(EventArgs.Empty);
  63. this.HotkeysEnabled = true;
  64. if (RunTime() && ContextMenuStrip == null)
  65. ContextMenuStrip = contextMenu;
  66. contextMenu.Opening += ContextMenu_Opening;
  67. }
  68. void FileViewer_GitUICommandsSourceSet(object sender, IGitUICommandsSource uiCommandsSource)
  69. {
  70. UICommandsSource.GitUICommandsChanged += WorkingDirChanged;
  71. WorkingDirChanged(UICommandsSource, null);
  72. }
  73. protected override void DisposeUICommandsSource()
  74. {
  75. UICommandsSource.GitUICommandsChanged -= WorkingDirChanged;
  76. base.DisposeUICommandsSource();
  77. }
  78. private bool RunTime()
  79. {
  80. return (System.Diagnostics.Process.GetCurrentProcess().ProcessName != "devenv");
  81. }
  82. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  83. [Browsable(false)]
  84. public new Font Font
  85. {
  86. get { return _internalFileViewer.Font; }
  87. set { _internalFileViewer.Font = value; }
  88. }
  89. [Description("Ignore changes in amount of whitespace. This ignores whitespace at line end, and considers all other sequences of one or more whitespace characters to be equivalent.")]
  90. [DefaultValue(false)]
  91. public bool IgnoreWhitespaceChanges { get; set; }
  92. [Description("Show diffs with <n> lines of context.")]
  93. [DefaultValue(3)]
  94. public int NumberOfVisibleLines { get; set; }
  95. [Description("Show diffs with entire file.")]
  96. [DefaultValue(false)]
  97. public bool ShowEntireFile { get; set; }
  98. [Description("Treat all files as text.")]
  99. [DefaultValue(false)]
  100. public bool TreatAllFilesAsText { get; set; }
  101. [DefaultValue(true)]
  102. [Category("Behavior")]
  103. public bool IsReadOnly
  104. {
  105. get { return _internalFileViewer.IsReadOnly; }
  106. set { _internalFileViewer.IsReadOnly = value; }
  107. }
  108. [DefaultValue(true)]
  109. [Description("If true line numbers are shown in the textarea")]
  110. [Category("Appearance")]
  111. public bool ShowLineNumbers
  112. {
  113. get { return _internalFileViewer.ShowLineNumbers; }
  114. set { _internalFileViewer.ShowLineNumbers = value; }
  115. }
  116. private Encoding _Encoding;
  117. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  118. [Browsable(false)]
  119. public Encoding Encoding
  120. {
  121. get
  122. {
  123. if (_Encoding == null)
  124. _Encoding = Module.FilesEncoding;
  125. return _Encoding;
  126. }
  127. set
  128. {
  129. _Encoding = value;
  130. }
  131. }
  132. [DefaultValue(0)]
  133. [Browsable(false)]
  134. public int ScrollPos
  135. {
  136. get { return _internalFileViewer.ScrollPos; }
  137. set { _internalFileViewer.ScrollPos = value; }
  138. }
  139. private void WorkingDirChanged(IGitUICommandsSource source, GitUICommands old)
  140. {
  141. this.Encoding = null;
  142. }
  143. protected override void OnRuntimeLoad(EventArgs e)
  144. {
  145. this.Hotkeys = HotkeySettingsManager.LoadHotkeys(HotkeySettingsName);
  146. Font = AppSettings.DiffFont;
  147. }
  148. void ContextMenu_Opening(object sender, System.ComponentModel.CancelEventArgs e)
  149. {
  150. copyToolStripMenuItem.Enabled = (_internalFileViewer.GetSelectionLength() > 0);
  151. if (ContextMenuOpening != null)
  152. ContextMenuOpening(sender, e);
  153. }
  154. void _internalFileViewer_MouseMove(object sender, MouseEventArgs e)
  155. {
  156. this.OnMouseMove(e);
  157. }
  158. void _internalFileViewer_MouseEnter(object sender, EventArgs e)
  159. {
  160. this.OnMouseEnter(e);
  161. }
  162. void _internalFileViewer_MouseLeave(object sender, EventArgs e)
  163. {
  164. this.OnMouseLeave(e);
  165. }
  166. void _internalFileViewer_SelectedLineChanged(object sender, int selectedLine)
  167. {
  168. if (SelectedLineChanged != null)
  169. SelectedLineChanged(sender, selectedLine);
  170. }
  171. public event SelectedLineChangedEventHandler SelectedLineChanged;
  172. public event EventHandler ScrollPosChanged;
  173. public event EventHandler RequestDiffView;
  174. public new event EventHandler TextChanged;
  175. public event EventHandler TextLoaded;
  176. public event CancelEventHandler ContextMenuOpening;
  177. public ToolStripSeparator AddContextMenuSeparator()
  178. {
  179. ToolStripSeparator separator = new ToolStripSeparator();
  180. contextMenu.Items.Add(separator);
  181. return separator;
  182. }
  183. public ToolStripMenuItem AddContextMenuEntry(string text, EventHandler toolStripItem_Click)
  184. {
  185. ToolStripMenuItem toolStripItem = new ToolStripMenuItem(text);
  186. contextMenu.Items.Add(toolStripItem);
  187. toolStripItem.Click += toolStripItem_Click;
  188. return toolStripItem;
  189. }
  190. protected virtual void OnRequestDiffView(EventArgs args)
  191. {
  192. var handler = RequestDiffView;
  193. if (handler != null)
  194. {
  195. handler(this, args);
  196. }
  197. }
  198. void _internalFileViewer_ScrollPosChanged(object sender, EventArgs e)
  199. {
  200. if (ScrollPosChanged != null)
  201. ScrollPosChanged(sender, e);
  202. }
  203. public void EnableScrollBars(bool enable)
  204. {
  205. _internalFileViewer.EnableScrollBars(enable);
  206. }
  207. void TextEditor_TextChanged(object sender, EventArgs e)
  208. {
  209. if (patchHighlighting)
  210. _internalFileViewer.AddPatchHighlighting();
  211. if (TextChanged != null)
  212. TextChanged(sender, e);
  213. }
  214. private void UpdateEncodingCombo()
  215. {
  216. if (this.Encoding.GetType() == typeof(ASCIIEncoding))
  217. this.encodingToolStripComboBox.Text = "ASCII";
  218. else if (this.Encoding.GetType() == typeof(UnicodeEncoding))
  219. this.encodingToolStripComboBox.Text = "Unicode";
  220. else if (this.Encoding.GetType() == typeof(UTF7Encoding))
  221. this.encodingToolStripComboBox.Text = "UTF7";
  222. else if (this.Encoding.GetType() == typeof(UTF8Encoding))
  223. this.encodingToolStripComboBox.Text = "UTF8";
  224. else if (this.Encoding.GetType() == typeof(UTF32Encoding))
  225. this.encodingToolStripComboBox.Text = "UTF32";
  226. else if (this.Encoding == Encoding.Default)
  227. this.encodingToolStripComboBox.Text = "Default (" + Encoding.Default.HeaderName + ")";
  228. }
  229. public event EventHandler<EventArgs> ExtraDiffArgumentsChanged;
  230. public void SetVisibilityDiffContextMenu(bool visible)
  231. {
  232. _currentViewIsPatch = visible;
  233. ignoreWhitespaceChangesToolStripMenuItem.Visible = visible;
  234. increaseNumberOfLinesToolStripMenuItem.Visible = visible;
  235. descreaseNumberOfLinesToolStripMenuItem.Visible = visible;
  236. showEntireFileToolStripMenuItem.Visible = visible;
  237. toolStripSeparator2.Visible = visible;
  238. treatAllFilesAsTextToolStripMenuItem.Visible = visible;
  239. }
  240. private void OnExtraDiffArgumentsChanged()
  241. {
  242. if (ExtraDiffArgumentsChanged != null)
  243. ExtraDiffArgumentsChanged(this, new EventArgs());
  244. }
  245. public string GetExtraDiffArguments()
  246. {
  247. var diffArguments = new StringBuilder();
  248. if (IgnoreWhitespaceChanges)
  249. diffArguments.Append(" --ignore-space-change");
  250. if (ShowEntireFile)
  251. diffArguments.AppendFormat(" --inter-hunk-context=9000 --unified=9000");
  252. else
  253. diffArguments.AppendFormat(" --unified={0}", NumberOfVisibleLines);
  254. if (TreatAllFilesAsText)
  255. diffArguments.Append(" --text");
  256. return diffArguments.ToString();
  257. }
  258. private void TextAreaMouseMove(object sender, MouseEventArgs e)
  259. {
  260. if (_currentViewIsPatch && !fileviewerToolbar.Visible)
  261. {
  262. fileviewerToolbar.Visible = true;
  263. fileviewerToolbar.Location = new Point(this.Width - fileviewerToolbar.Width - 40, 0);
  264. fileviewerToolbar.BringToFront();
  265. }
  266. }
  267. private void TextAreaMouseLeave(object sender, EventArgs e)
  268. {
  269. if (GetChildAtPoint(PointToClient(MousePosition)) != fileviewerToolbar &&
  270. fileviewerToolbar != null)
  271. fileviewerToolbar.Visible = false;
  272. }
  273. public void SaveCurrentScrollPos()
  274. {
  275. _currentScrollPos = ScrollPos;
  276. }
  277. public void ResetCurrentScrollPos()
  278. {
  279. _currentScrollPos = 0;
  280. }
  281. private void RestoreCurrentScrollPos()
  282. {
  283. if (_currentScrollPos < 0)
  284. return;
  285. ScrollPos = _currentScrollPos;
  286. ResetCurrentScrollPos();
  287. }
  288. public void ViewFile(string fileName)
  289. {
  290. ViewItem(fileName, () => GetImage(fileName), () => GetFileText(fileName),
  291. () => GitCommandHelpers.GetSubmoduleText(Module, fileName.TrimEnd('/'), ""));
  292. }
  293. public string GetText()
  294. {
  295. return _internalFileViewer.GetText();
  296. }
  297. public void ViewCurrentChanges(GitItemStatus item)
  298. {
  299. ViewCurrentChanges(item.Name, item.OldName, item.IsStaged, item.IsSubmodule, item.SubmoduleStatus);
  300. }
  301. public void ViewCurrentChanges(GitItemStatus item, bool isStaged)
  302. {
  303. ViewCurrentChanges(item.Name, item.OldName, isStaged, item.IsSubmodule, item.SubmoduleStatus);
  304. }
  305. public void ViewCurrentChanges(string fileName, string oldFileName, bool staged,
  306. bool isSubmodule, Task<GitSubmoduleStatus> status)
  307. {
  308. if (!isSubmodule)
  309. _async.Load(() => Module.GetCurrentChanges(fileName, oldFileName, staged, GetExtraDiffArguments(), Encoding),
  310. ViewStagingPatch);
  311. else if (status != null)
  312. _async.Load(() =>
  313. {
  314. if (status.Result == null)
  315. return string.Format("Submodule \"{0}\" has unresolved conflicts", fileName);
  316. return GitCommandHelpers.ProcessSubmoduleStatus(Module, status.Result);
  317. }, ViewPatch);
  318. else
  319. _async.Load(() => GitCommandHelpers.ProcessSubmodulePatch(Module, fileName,
  320. Module.GetCurrentChanges(fileName, oldFileName, staged, GetExtraDiffArguments(), Encoding)), ViewPatch);
  321. }
  322. public void ViewStagingPatch(Patch patch)
  323. {
  324. ViewPatch(patch);
  325. Reset(true, true, true);
  326. }
  327. public void ViewPatch(Patch patch)
  328. {
  329. string text = patch != null ? patch.Text : "";
  330. ViewPatch(text);
  331. }
  332. public void ViewPatch(string text)
  333. {
  334. ResetForDiff();
  335. _internalFileViewer.SetText(text);
  336. if (TextLoaded != null)
  337. TextLoaded(this, null);
  338. RestoreCurrentScrollPos();
  339. }
  340. public void ViewStagingPatch(Func<string> loadPatchText)
  341. {
  342. ViewPatch(loadPatchText);
  343. Reset(true, true, true);
  344. }
  345. public void ViewPatch(Func<string> loadPatchText)
  346. {
  347. _async.Load(loadPatchText, ViewPatch);
  348. }
  349. public void ViewText(string fileName, string text)
  350. {
  351. ResetForText(fileName);
  352. //Check for binary file.
  353. if (FileHelper.IsBinaryFileAccordingToContent(text))
  354. {
  355. _internalFileViewer.SetText("Binary file: " + fileName + " (Detected)");
  356. if (TextLoaded != null)
  357. TextLoaded(this, null);
  358. return;
  359. }
  360. _internalFileViewer.SetText(text);
  361. if (TextLoaded != null)
  362. TextLoaded(this, null);
  363. RestoreCurrentScrollPos();
  364. }
  365. public void ViewGitItemRevision(string fileName, string guid)
  366. {
  367. if (guid == GitRevision.UnstagedGuid) //working directory changes
  368. {
  369. ViewFile(fileName);
  370. }
  371. else
  372. {
  373. string blob = Module.GetFileBlobHash(fileName, guid);
  374. ViewGitItem(fileName, blob);
  375. }
  376. }
  377. public void ViewGitItem(string fileName, string guid)
  378. {
  379. ViewItem(fileName, () => GetImage(fileName, guid), () => Module.GetFileText(guid, Encoding),
  380. () => GitCommandHelpers.GetSubmoduleText(Module, fileName.TrimEnd('/'), guid));
  381. }
  382. private void ViewItem(string fileName, Func<Image> getImage, Func<string> getFileText, Func<string> getSubmoduleText)
  383. {
  384. string fullPath = Path.GetFullPath(Path.Combine(Module.WorkingDir, fileName));
  385. if (fileName.EndsWith("/") || Directory.Exists(fullPath))
  386. {
  387. if (GitModule.IsValidGitWorkingDir(fullPath))
  388. {
  389. _async.Load(getSubmoduleText, text => ViewText(fileName, text));
  390. }
  391. else
  392. {
  393. ViewText(null, "Directory: " + fileName);
  394. }
  395. }
  396. else if (IsImage(fileName))
  397. {
  398. _async.Load(getImage,
  399. image =>
  400. {
  401. ResetForImage();
  402. if (image != null)
  403. {
  404. if (image.Size.Height > PictureBox.Size.Height ||
  405. image.Size.Width > PictureBox.Size.Width)
  406. {
  407. PictureBox.SizeMode = PictureBoxSizeMode.Zoom;
  408. }
  409. else
  410. {
  411. PictureBox.SizeMode = PictureBoxSizeMode.CenterImage;
  412. }
  413. }
  414. PictureBox.Image = image;
  415. });
  416. }
  417. else if (IsBinaryFile(fileName))
  418. {
  419. ViewText(null, "Binary file: " + fileName);
  420. }
  421. else
  422. {
  423. _async.Load(getFileText, text => ViewText(fileName, text));
  424. }
  425. }
  426. private bool IsBinaryFile(string fileName)
  427. {
  428. return FileHelper.IsBinaryFile(Module, fileName);
  429. }
  430. private static bool IsImage(string fileName)
  431. {
  432. return FileHelper.IsImage(fileName);
  433. }
  434. private static bool IsIcon(string fileName)
  435. {
  436. return fileName.EndsWith(".ico", StringComparison.CurrentCultureIgnoreCase);
  437. }
  438. private Image GetImage(string fileName, string guid)
  439. {
  440. try
  441. {
  442. using (var stream = Module.GetFileStream(guid))
  443. {
  444. return CreateImage(fileName, stream);
  445. }
  446. }
  447. catch
  448. {
  449. return null;
  450. }
  451. }
  452. private Image GetImage(string fileName)
  453. {
  454. try
  455. {
  456. using (Stream stream = File.OpenRead(Path.Combine(Module.WorkingDir, fileName)))
  457. {
  458. return CreateImage(fileName, stream);
  459. }
  460. }
  461. catch
  462. {
  463. return null;
  464. }
  465. }
  466. private static Image CreateImage(string fileName, Stream stream)
  467. {
  468. if (IsIcon(fileName))
  469. {
  470. using (var icon = new Icon(stream))
  471. {
  472. return icon.ToBitmap();
  473. }
  474. }
  475. return new Bitmap(CreateCopy(stream));
  476. }
  477. private static MemoryStream CreateCopy(Stream stream)
  478. {
  479. return new MemoryStream(new BinaryReader(stream).ReadBytes((int)stream.Length));
  480. }
  481. private string GetFileText(string fileName)
  482. {
  483. string path;
  484. if (File.Exists(fileName))
  485. path = fileName;
  486. else
  487. path = Path.Combine(Module.WorkingDir, fileName);
  488. return !File.Exists(path) ? null : FileReader.ReadFileContent(path, Module.FilesEncoding);
  489. }
  490. private void ResetForImage()
  491. {
  492. Reset(false, false);
  493. _internalFileViewer.SetHighlighting("Default");
  494. }
  495. private void ResetForText(string fileName)
  496. {
  497. Reset(false, true);
  498. if (fileName == null)
  499. _internalFileViewer.SetHighlighting("Default");
  500. else
  501. _internalFileViewer.SetHighlightingForFile(fileName);
  502. if (!string.IsNullOrEmpty(fileName) &&
  503. (fileName.EndsWith(".diff", StringComparison.OrdinalIgnoreCase) ||
  504. fileName.EndsWith(".patch", StringComparison.OrdinalIgnoreCase)))
  505. {
  506. ResetForDiff();
  507. }
  508. }
  509. private bool patchHighlighting;
  510. private void ResetForDiff()
  511. {
  512. Reset(true, true);
  513. _internalFileViewer.SetHighlighting("");
  514. patchHighlighting = true;
  515. }
  516. private void Reset(bool diff, bool text)
  517. {
  518. Reset(diff, text, false);
  519. }
  520. private void Reset(bool diff, bool text, bool staging_diff)
  521. {
  522. patchHighlighting = diff;
  523. SetVisibilityDiffContextMenu(diff);
  524. ClearImage();
  525. PictureBox.Visible = !text;
  526. _internalFileViewer.Visible = text;
  527. }
  528. private void ClearImage()
  529. {
  530. PictureBox.ImageLocation = "";
  531. if (PictureBox.Image == null) return;
  532. PictureBox.Image.Dispose();
  533. PictureBox.Image = null;
  534. }
  535. private void IgnoreWhitespaceChangesToolStripMenuItemClick(object sender, EventArgs e)
  536. {
  537. IgnoreWhitespaceChanges = !IgnoreWhitespaceChanges;
  538. ignoreWhiteSpaces.Checked = IgnoreWhitespaceChanges;
  539. ignoreWhitespaceChangesToolStripMenuItem.Checked = IgnoreWhitespaceChanges;
  540. OnExtraDiffArgumentsChanged();
  541. }
  542. private void IncreaseNumberOfLinesToolStripMenuItemClick(object sender, EventArgs e)
  543. {
  544. NumberOfVisibleLines++;
  545. OnExtraDiffArgumentsChanged();
  546. }
  547. private void DescreaseNumberOfLinesToolStripMenuItemClick(object sender, EventArgs e)
  548. {
  549. if (NumberOfVisibleLines > 0)
  550. NumberOfVisibleLines--;
  551. else
  552. NumberOfVisibleLines = 0;
  553. OnExtraDiffArgumentsChanged();
  554. }
  555. private void ShowEntireFileToolStripMenuItemClick(object sender, EventArgs e)
  556. {
  557. showEntireFileToolStripMenuItem.Checked = !showEntireFileToolStripMenuItem.Checked;
  558. showEntireFileButton.Checked = showEntireFileToolStripMenuItem.Checked;
  559. ShowEntireFile = showEntireFileToolStripMenuItem.Checked;
  560. OnExtraDiffArgumentsChanged();
  561. }
  562. private void TreatAllFilesAsTextToolStripMenuItemClick(object sender, EventArgs e)
  563. {
  564. treatAllFilesAsTextToolStripMenuItem.Checked = !treatAllFilesAsTextToolStripMenuItem.Checked;
  565. TreatAllFilesAsText = treatAllFilesAsTextToolStripMenuItem.Checked;
  566. OnExtraDiffArgumentsChanged();
  567. }
  568. private void CopyToolStripMenuItemClick(object sender, EventArgs e)
  569. {
  570. string code = _internalFileViewer.GetSelectedText();
  571. if (string.IsNullOrEmpty(code))
  572. return;
  573. if (_currentViewIsPatch)
  574. {
  575. //add artificail space if selected text is not starting from line begining, it will be removed later
  576. int pos = _internalFileViewer.GetSelectionPosition();
  577. string fileText = _internalFileViewer.GetText();
  578. int hpos = fileText.IndexOf("\n@@");
  579. //if header is selected then don't remove diff extra chars
  580. if (hpos <= pos)
  581. {
  582. if (pos > 0)
  583. if (fileText[pos - 1] != '\n')
  584. code = " " + code;
  585. string[] lines = code.Split('\n');
  586. char[] specials = new char[] { ' ', '-', '+' };
  587. lines.Transform(s => s.Length > 0 && specials.Any(c => c == s[0]) ? s.Substring(1) : s);
  588. code = string.Join("\n", lines);
  589. }
  590. }
  591. Clipboard.SetText(code);
  592. }
  593. private void CopyPatchToolStripMenuItemClick(object sender, EventArgs e)
  594. {
  595. var selectedText = _internalFileViewer.GetSelectedText();
  596. if (!string.IsNullOrEmpty(selectedText))
  597. {
  598. Clipboard.SetText(selectedText);
  599. return;
  600. }
  601. var text = _internalFileViewer.GetText();
  602. if (!text.IsNullOrEmpty())
  603. Clipboard.SetText(text);
  604. }
  605. public int GetSelectionPosition()
  606. {
  607. return _internalFileViewer.GetSelectionPosition();
  608. }
  609. public int GetSelectionLength()
  610. {
  611. return _internalFileViewer.GetSelectionLength();
  612. }
  613. public void GoToLine(int line)
  614. {
  615. _internalFileViewer.GoToLine(line);
  616. }
  617. public int GetLineFromVisualPosY(int visualPosY)
  618. {
  619. return _internalFileViewer.GetLineFromVisualPosY(visualPosY);
  620. }
  621. public string GetLineText(int line)
  622. {
  623. return _internalFileViewer.GetLineText(line);
  624. }
  625. public void HighlightLine(int line, Color color)
  626. {
  627. _internalFileViewer.HighlightLine(line, color);
  628. }
  629. public void HighlightLines(int startLine, int endLine, Color color)
  630. {
  631. _internalFileViewer.HighlightLines(startLine, endLine, color);
  632. }
  633. public void ClearHighlighting()
  634. {
  635. _internalFileViewer.ClearHighlighting();
  636. }
  637. private void NextChangeButtonClick(object sender, EventArgs e)
  638. {
  639. Focus();
  640. var firstVisibleLine = _internalFileViewer.LineAtCaret;
  641. var totalNumberOfLines = _internalFileViewer.TotalNumberOfLines;
  642. var emptyLineCheck = false;
  643. for (var line = firstVisibleLine + 1; line < totalNumberOfLines; line++)
  644. {
  645. var lineContent = _internalFileViewer.GetLineText(line);
  646. if (lineContent.StartsWithAny(new string[] { "+", "-" })
  647. && !lineContent.StartsWithAny(new string[] { "++", "--" }))
  648. {
  649. if (emptyLineCheck)
  650. {
  651. _internalFileViewer.FirstVisibleLine = Math.Max(line - 4, 0);
  652. GoToLine(line);
  653. return;
  654. }
  655. }
  656. else
  657. {
  658. emptyLineCheck = true;
  659. }
  660. }
  661. //Do not go to the end of the file if no change is found
  662. //TextEditor.ActiveTextAreaControl.TextArea.TextView.FirstVisibleLine = totalNumberOfLines - TextEditor.ActiveTextAreaControl.TextArea.TextView.VisibleLineCount;
  663. }
  664. private void PreviousChangeButtonClick(object sender, EventArgs e)
  665. {
  666. Focus();
  667. var firstVisibleLine = _internalFileViewer.LineAtCaret;
  668. var emptyLineCheck = false;
  669. //go to the top of change block
  670. while (firstVisibleLine > 0 &&
  671. _internalFileViewer.GetLineText(firstVisibleLine).StartsWithAny(new string[] { "+", "-" }))
  672. firstVisibleLine--;
  673. for (var line = firstVisibleLine; line > 0; line--)
  674. {
  675. var lineContent = _internalFileViewer.GetLineText(line);
  676. if (lineContent.StartsWithAny(new string[] { "+", "-" })
  677. && !lineContent.StartsWithAny(new string[] { "++", "--" }))
  678. {
  679. emptyLineCheck = true;
  680. }
  681. else
  682. {
  683. if (emptyLineCheck)
  684. {
  685. _internalFileViewer.FirstVisibleLine = Math.Max(0, line - 3);
  686. GoToLine(line + 1);
  687. return;
  688. }
  689. }
  690. }
  691. //Do not go to the start of the file if no change is found
  692. //TextEditor.ActiveTextAreaControl.TextArea.TextView.FirstVisibleLine = 0;
  693. }
  694. private void IncreaseNumberOfLinesClick(object sender, EventArgs e)
  695. {
  696. IncreaseNumberOfLinesToolStripMenuItemClick(null, null);
  697. }
  698. private void DecreaseNumberOfLinesClick(object sender, EventArgs e)
  699. {
  700. DescreaseNumberOfLinesToolStripMenuItemClick(null, null);
  701. }
  702. private void ShowEntireFileButtonClick(object sender, EventArgs e)
  703. {
  704. ShowEntireFileToolStripMenuItemClick(null, null);
  705. }
  706. private void ShowNonPrintCharsClick(object sender, EventArgs e)
  707. {
  708. ShowNonprintableCharactersToolStripMenuItemClick(null, null);
  709. }
  710. private void ShowNonprintableCharactersToolStripMenuItemClick(object sender, EventArgs e)
  711. {
  712. showNonprintableCharactersToolStripMenuItem.Checked = !showNonprintableCharactersToolStripMenuItem.Checked;
  713. showNonPrintChars.Checked = showNonprintableCharactersToolStripMenuItem.Checked;
  714. _internalFileViewer.ShowEOLMarkers = showNonprintableCharactersToolStripMenuItem.Checked;
  715. _internalFileViewer.ShowSpaces = showNonprintableCharactersToolStripMenuItem.Checked;
  716. _internalFileViewer.ShowTabs = showNonprintableCharactersToolStripMenuItem.Checked;
  717. }
  718. private void FindToolStripMenuItemClick(object sender, EventArgs e)
  719. {
  720. _internalFileViewer.Find();
  721. }
  722. private void ignoreWhiteSpaces_Click(object sender, EventArgs e)
  723. {
  724. IgnoreWhitespaceChangesToolStripMenuItemClick(null, null);
  725. }
  726. #region Hotkey commands
  727. public const string HotkeySettingsName = "FileViewer";
  728. internal enum Commands
  729. {
  730. Find,
  731. GoToLine,
  732. IncreaseNumberOfVisibleLines,
  733. DecreaseNumberOfVisibleLines,
  734. ShowEntireFile,
  735. TreatFileAsText,
  736. NextChange,
  737. PreviousChange
  738. }
  739. protected override bool ExecuteCommand(int cmd)
  740. {
  741. Commands command = (Commands)cmd;
  742. switch (command)
  743. {
  744. case Commands.Find: this.FindToolStripMenuItemClick(null, null); break;
  745. case Commands.GoToLine: this.goToLineToolStripMenuItem_Click(null, null); break;
  746. case Commands.IncreaseNumberOfVisibleLines: this.IncreaseNumberOfLinesToolStripMenuItemClick(null, null); break;
  747. case Commands.DecreaseNumberOfVisibleLines: this.DescreaseNumberOfLinesToolStripMenuItemClick(null, null); break;
  748. case Commands.ShowEntireFile: this.ShowEntireFileToolStripMenuItemClick(null, null); break;
  749. case Commands.TreatFileAsText: this.TreatAllFilesAsTextToolStripMenuItemClick(null, null); break;
  750. case Commands.NextChange: this.NextChangeButtonClick(null, null); break;
  751. case Commands.PreviousChange: this.PreviousChangeButtonClick(null, null); break;
  752. default: return base.ExecuteCommand(cmd);
  753. }
  754. return true;
  755. }
  756. #endregion
  757. public void Clear()
  758. {
  759. ViewText("", "");
  760. }
  761. public bool HasAnyPatches()
  762. {
  763. return (_internalFileViewer.GetText() != null && _internalFileViewer.GetText().Contains("@@"));
  764. }
  765. public void SetFileLoader(Func<bool, Tuple<int, string>> fileLoader){
  766. _internalFileViewer.SetFileLoader(fileLoader);
  767. }
  768. private void encodingToolStripComboBox_SelectedIndexChanged(object sender, EventArgs e)
  769. {
  770. Encoding encod = null;
  771. if (string.IsNullOrEmpty(encodingToolStripComboBox.Text))
  772. encod = Module.FilesEncoding;
  773. else if (encodingToolStripComboBox.Text.StartsWith("Default", StringComparison.CurrentCultureIgnoreCase))
  774. encod = Encoding.Default;
  775. else if (encodingToolStripComboBox.Text.Equals("ASCII", StringComparison.CurrentCultureIgnoreCase))
  776. encod = new ASCIIEncoding();
  777. else if (encodingToolStripComboBox.Text.Equals("Unicode", StringComparison.CurrentCultureIgnoreCase))
  778. encod = new UnicodeEncoding();
  779. else if (encodingToolStripComboBox.Text.Equals("UTF7", StringComparison.CurrentCultureIgnoreCase))
  780. encod = new UTF7Encoding();
  781. else if (encodingToolStripComboBox.Text.Equals("UTF8", StringComparison.CurrentCultureIgnoreCase))
  782. encod = new UTF8Encoding(false);
  783. else if (encodingToolStripComboBox.Text.Equals("UTF32", StringComparison.CurrentCultureIgnoreCase))
  784. encod = new UTF32Encoding(true, false);
  785. else
  786. encod = Module.FilesEncoding;
  787. if (!encod.Equals(this.Encoding))
  788. {
  789. this.Encoding = encod;
  790. this.OnExtraDiffArgumentsChanged();
  791. }
  792. }
  793. private void fileviewerToolbar_VisibleChanged(object sender, EventArgs e)
  794. {
  795. if (fileviewerToolbar.Visible)
  796. UpdateEncodingCombo();
  797. }
  798. private void goToLineToolStripMenuItem_Click(object sender, EventArgs e)
  799. {
  800. using (FormGoToLine formGoToLine = new FormGoToLine())
  801. {
  802. formGoToLine.SetMaxLineNumber(_internalFileViewer.TotalNumberOfLines);
  803. if (formGoToLine.ShowDialog(this) == DialogResult.OK)
  804. GoToLine(formGoToLine.GetLineNumber() - 1);
  805. }
  806. }
  807. private void CopyNotStartingWith(char startChar)
  808. {
  809. string code = _internalFileViewer.GetSelectedText();
  810. if (string.IsNullOrEmpty(code))
  811. return;
  812. if (_currentViewIsPatch)
  813. {
  814. //add artificail space if selected text is not starting from line begining, it will be removed later
  815. int pos = _internalFileViewer.GetSelectionPosition();
  816. string fileText = _internalFileViewer.GetText();
  817. if (pos > 0)
  818. if (fileText[pos - 1] != '\n')
  819. code = " " + code;
  820. IEnumerable<string> lines = code.Split('\n');
  821. lines = lines.Where(s => s.Length == 0 || s[0] != startChar);
  822. int hpos = fileText.IndexOf("\n@@");
  823. //if header is selected then don't remove diff extra chars
  824. if (hpos <= pos)
  825. {
  826. char[] specials = new char[] { ' ', '-', '+' };
  827. lines = lines.Select(s => s.Length > 0 && specials.Any(c => c == s[0]) ? s.Substring(1) : s);
  828. }
  829. code = string.Join("\n", lines);
  830. }
  831. Clipboard.SetText(code);
  832. }
  833. private void copyNewVersionToolStripMenuItem_Click(object sender, EventArgs e)
  834. {
  835. CopyNotStartingWith('-');
  836. }
  837. private void copyOldVersionToolStripMenuItem_Click(object sender, EventArgs e)
  838. {
  839. CopyNotStartingWith('+');
  840. }
  841. }
  842. }