MarkdownViewer.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. using com.insanitydesign.MarkdownViewerPlusPlus.Forms;
  2. using com.insanitydesign.MarkdownViewerPlusPlus.Properties;
  3. using Kbg.NppPluginNET;
  4. using Kbg.NppPluginNET.PluginInfrastructure;
  5. using System;
  6. using System.IO;
  7. using System.Runtime.InteropServices;
  8. using System.Windows.Forms;
  9. using static com.insanitydesign.MarkdownViewerPlusPlus.MarkdownViewerConfiguration;
  10. using static Kbg.NppPluginNET.PluginInfrastructure.Win32;
  11. /// <summary>
  12. ///
  13. /// </summary>
  14. namespace com.insanitydesign.MarkdownViewerPlusPlus
  15. {
  16. /// <summary>
  17. ///
  18. /// </summary>
  19. public class MarkdownViewer
  20. {
  21. /// <summary>
  22. ///
  23. /// </summary>
  24. public struct FileInformation
  25. {
  26. /// <summary>
  27. ///
  28. /// </summary>
  29. public string FileDirectory { get; set; }
  30. /// <summary>
  31. ///
  32. /// </summary>
  33. private string fileName;
  34. /// <summary>
  35. ///
  36. /// </summary>
  37. public string FileName {
  38. get {
  39. return this.fileName;
  40. }
  41. set {
  42. this.fileName = value;
  43. this.FileExtension = Path.GetExtension(this.fileName);
  44. this.FileExtension = this.FileExtension.StartsWith(".") ? this.FileExtension.Substring(1, this.FileExtension.Length - 1) : this.FileExtension;
  45. }
  46. }
  47. /// <summary>
  48. ///
  49. /// </summary>
  50. public string FileExtension { get; private set; }
  51. }
  52. /// <summary>
  53. ///
  54. /// </summary>
  55. public IScintillaGateway Editor { get; protected set; }
  56. /// <summary>
  57. ///
  58. /// </summary>
  59. public INotepadPPGateway Notepad { get; protected set; }
  60. /// <summary>
  61. ///
  62. /// </summary>
  63. public FileInformation FileInfo { get; protected set; }
  64. /// <summary>
  65. ///
  66. /// </summary>
  67. protected AbstractRenderer renderer;
  68. /// <summary>
  69. ///
  70. /// </summary>
  71. protected bool rendererVisible = false;
  72. /// <summary>
  73. ///
  74. /// </summary>
  75. protected bool updateRenderer = true;
  76. /// <summary>
  77. ///
  78. /// </summary>
  79. public int commandId = 0;
  80. /// <summary>
  81. ///
  82. /// </summary>
  83. public int commandIdSynchronize = 2;
  84. /// <summary>
  85. ///
  86. /// </summary>
  87. public int commandIdOptions = 4;
  88. /// <summary>
  89. ///
  90. /// </summary>
  91. public int commandIdAbout = 5;
  92. /// <summary>
  93. ///
  94. /// </summary>
  95. protected MarkdownViewerConfiguration configuration;
  96. /// <summary>
  97. ///
  98. /// </summary>
  99. public Options Options {
  100. get {
  101. return this.configuration.options;
  102. }
  103. }
  104. /// <summary>
  105. ///
  106. /// </summary>
  107. public MarkdownViewer()
  108. {
  109. //Get some global references to the editor and Notepad++ engines
  110. this.Editor = new ScintillaGateway(PluginBase.GetCurrentScintilla());
  111. this.Notepad = new NotepadPPGateway();
  112. //Init the actual renderer
  113. this.renderer = new MarkdownViewerRenderer(this);
  114. }
  115. /// <summary>
  116. ///
  117. /// </summary>
  118. /// <param name="notification"></param>
  119. public void OnNotification(ScNotification notification)
  120. {
  121. //Listen to any UI update to get informed about all file changes, chars added/removed etc.
  122. if (this.renderer.Visible)
  123. {
  124. //Check for updates
  125. if (notification.Header.Code == (uint)SciMsg.SCN_UPDATEUI)
  126. {
  127. //Update the view
  128. Update((notification.Updated & (uint)SciMsg.SC_UPDATE_V_SCROLL) != 0);
  129. }
  130. else if (notification.Header.Code == (uint)NppMsg.NPPN_BUFFERACTIVATED)
  131. {
  132. //Update the scintilla handle in all cases to keep track of which instance is active
  133. UpdateEditorInformation();
  134. this.Editor.CurrentBufferID = notification.Header.IdFrom;
  135. Update(true, true);
  136. }
  137. else if (notification.Header.Code == (uint)SciMsg.SCN_MODIFIED && !this.updateRenderer)
  138. {
  139. bool isInsert = (notification.ModificationType & (uint)SciMsg.SC_MOD_INSERTTEXT) != 0;
  140. bool isDelete = (notification.ModificationType & (uint)SciMsg.SC_MOD_DELETETEXT) != 0;
  141. //Track if any text modifications have been made
  142. this.updateRenderer = isInsert || isDelete;
  143. }
  144. }
  145. }
  146. /// <summary>
  147. ///
  148. /// </summary>
  149. /// <param name="updateScrollBar"></param>
  150. /// <param name="updateRenderer"></param>
  151. public void Update(bool updateScrollBar = false, bool updateRenderer = false)
  152. {
  153. //Validate that the current file may be rendered
  154. if (this.configuration.ValidateFileExtension(this.FileInfo.FileExtension, this.FileInfo.FileName))
  155. {
  156. //Update the view
  157. this.updateRenderer = updateRenderer ? updateRenderer : this.updateRenderer;
  158. UpdateMarkdownViewer();
  159. //Update the scroll bar of the Viewer Panel only in case of vertical scrolls
  160. if (this.configuration.options.synchronizeScrolling && updateScrollBar)
  161. {
  162. UpdateScrollBar();
  163. }
  164. }
  165. else
  166. {
  167. this.renderer.Render($@"<p>
  168. Your configuration settings do not include the currently selected file extension.<br />
  169. The rendered file extensions are <b>'{this.configuration.options.fileExtensions}'</b>.<br />
  170. The current file is <i>'{this.FileInfo.FileName}'</i>.
  171. </p>", this.FileInfo);
  172. }
  173. }
  174. /// <summary>
  175. ///
  176. /// </summary>
  177. protected void UpdateScrollBar()
  178. {
  179. try
  180. {
  181. ScrollInfo scrollInfo = this.Editor.GetScrollInfo(ScrollInfoMask.SIF_RANGE | ScrollInfoMask.SIF_TRACKPOS | ScrollInfoMask.SIF_PAGE, ScrollInfoBar.SB_VERT);
  182. double totalRange = scrollInfo.nMax - scrollInfo.nMin + 1;
  183. double scrollRatio;
  184. // Is "Enable scrolling beyond last line" checked?
  185. if (Editor.GetEndAtLastLine() == false)
  186. {
  187. var actualThumbHeight = totalRange / (totalRange / scrollInfo.nPage - 1);
  188. var actualTrackPos = scrollInfo.nTrackPos * actualThumbHeight / scrollInfo.nPage;
  189. scrollRatio = Math.Min(1, actualTrackPos / (totalRange - actualThumbHeight));
  190. }
  191. else
  192. {
  193. scrollRatio = scrollInfo.nTrackPos / (totalRange - scrollInfo.nPage);
  194. }
  195. this.renderer.ScrollByRatioVertically(scrollRatio);
  196. }
  197. catch { }
  198. }
  199. /// <summary>
  200. ///
  201. /// </summary>
  202. public void CommandMenuInit()
  203. {
  204. this.configuration = new MarkdownViewerConfiguration();
  205. //Register our commands
  206. PluginBase.SetCommand(this.commandId, Main.PluginName, MarkdownViewerCommand, new ShortcutKey(true, false, true, System.Windows.Forms.Keys.M));
  207. //Separator
  208. PluginBase.SetCommand(this.commandId + 1, "---", null);
  209. //Synchronized scrolling
  210. PluginBase.SetCommand(this.commandIdSynchronize, "Synchronize scrolling (Editor -> Viewer)", SynchronizeScrollingCommand, this.configuration.options.synchronizeScrolling);
  211. //Separator
  212. PluginBase.SetCommand(this.commandIdSynchronize + 1, "---", null);
  213. //Options
  214. PluginBase.SetCommand(this.commandIdOptions, "Options", OptionsCommand);
  215. //About
  216. PluginBase.SetCommand(this.commandIdAbout, "About", AboutCommand);
  217. }
  218. /// <summary>
  219. ///
  220. /// </summary>
  221. public void OptionsCommand()
  222. {
  223. using (MarkdownViewerOptions options = new MarkdownViewerOptions(ref this.configuration))
  224. {
  225. if (options.ShowDialog(Control.FromHandle(PluginBase.GetCurrentScintilla())) == DialogResult.OK)
  226. {
  227. //Update after something potentially changed in the settings dialog
  228. Update(true, true);
  229. }
  230. }
  231. }
  232. /// <summary>
  233. /// Show the AboutDialog as modal to the Notepad++ window
  234. /// </summary>
  235. public void AboutCommand()
  236. {
  237. using (AboutDialog about = new AboutDialog())
  238. {
  239. about.ShowDialog(Control.FromHandle(PluginBase.GetCurrentScintilla()));
  240. }
  241. }
  242. /// <summary>
  243. /// Check/Uncheck the configuration item
  244. /// </summary>
  245. protected void SynchronizeScrollingCommand()
  246. {
  247. this.configuration.options.synchronizeScrolling = !this.configuration.options.synchronizeScrolling;
  248. Win32.CheckMenuItem(Win32.GetMenu(PluginBase.nppData._nppHandle), PluginBase._funcItems.Items[this.commandIdSynchronize]._cmdID, Win32.MF_BYCOMMAND | (this.configuration.options.synchronizeScrolling ? Win32.MF_CHECKED : Win32.MF_UNCHECKED));
  249. if (this.configuration.options.synchronizeScrolling)
  250. {
  251. UpdateScrollBar();
  252. }
  253. }
  254. /// <summary>
  255. ///
  256. /// </summary>
  257. public void SetToolBarIcon()
  258. {
  259. toolbarIcons toolbarIcons = new toolbarIcons();
  260. toolbarIcons.hToolbarBmp = Resources.markdown_16x16_solid.GetHbitmap();
  261. IntPtr pTbIcons = Marshal.AllocHGlobal(Marshal.SizeOf(toolbarIcons));
  262. Marshal.StructureToPtr(toolbarIcons, pTbIcons, false);
  263. Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_ADDTOOLBARICON, PluginBase._funcItems.Items[this.commandId]._cmdID, pTbIcons);
  264. Marshal.FreeHGlobal(pTbIcons);
  265. }
  266. /// <summary>
  267. ///
  268. /// </summary>
  269. public void MarkdownViewerCommand()
  270. {
  271. bool visibility = !this.renderer.Visible;
  272. ToggleToolbarIcon(visibility);
  273. //Show
  274. if (visibility)
  275. {
  276. UpdateEditorInformation();
  277. Update(true, true);
  278. Editor.SetFocus(true);
  279. }
  280. }
  281. /// <summary>
  282. ///
  283. /// </summary>
  284. protected void UpdateEditorInformation()
  285. {
  286. this.Editor.SetScintillaHandle(PluginBase.GetCurrentScintilla());
  287. this.FileInfo = new FileInformation() {
  288. FileName = this.Notepad.GetCurrentFileName(),
  289. FileDirectory = this.Notepad.GetCurrentDirectory()
  290. };
  291. }
  292. /// <summary>
  293. ///
  294. /// </summary>
  295. /// <param name="show"></param>
  296. /// <returns></returns>
  297. public void ToggleToolbarIcon(bool show = true)
  298. {
  299. //To show or not to show
  300. NppMsg msg = show ? NppMsg.NPPM_DMMSHOW : NppMsg.NPPM_DMMHIDE;
  301. //
  302. Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)msg, 0, this.renderer.Handle);
  303. Win32.SendMessage(PluginBase.nppData._nppHandle, (uint)NppMsg.NPPM_SETMENUITEMCHECK, PluginBase._funcItems.Items[this.commandId]._cmdID, show ? 1 : 0);
  304. //
  305. this.rendererVisible = this.renderer.Visible;
  306. }
  307. /// <summary>
  308. ///
  309. /// </summary>
  310. protected void UpdateMarkdownViewer()
  311. {
  312. try
  313. {
  314. if (this.updateRenderer)
  315. {
  316. this.updateRenderer = false;
  317. this.renderer.Render(this.Editor.GetText(this.Editor.GetLength() + 1), this.FileInfo);
  318. }
  319. }
  320. catch
  321. {
  322. this.renderer.Render("<p>Couldn't render the currently selected file!</p>", this.FileInfo);
  323. }
  324. }
  325. /// <summary>
  326. /// Save the configuration
  327. /// </summary>
  328. public void PluginCleanUp()
  329. {
  330. this.configuration.Save();
  331. }
  332. }
  333. }