diff --git a/src/MarkPad/Document/DocumentView.xaml.cs b/src/MarkPad/Document/DocumentView.xaml.cs index f7d0e957..d5d2ab95 100644 --- a/src/MarkPad/Document/DocumentView.xaml.cs +++ b/src/MarkPad/Document/DocumentView.xaml.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.ObjectModel; +using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; @@ -61,7 +61,7 @@ void TextView_VisualLinesChanged(object sender, EventArgs e) { DoSpellCheck(); } - + void DocumentViewSizeChanged(object sender, SizeChangedEventArgs e) { // Hide web browser when the window is too small for it to make much sense @@ -117,7 +117,7 @@ private void DoSpellCheck() { this.spellCheckRenderer.ErrorSegments.Clear(); - ReadOnlyCollection visualLines = Editor.TextArea.TextView.VisualLines; + IEnumerable visualLines = Editor.TextArea.TextView.VisualLines.AsParallel(); foreach (VisualLine currentLine in visualLines) { diff --git a/src/MarkPad/Document/DocumentViewModel.cs b/src/MarkPad/Document/DocumentViewModel.cs index 3590c168..23ac9093 100644 --- a/src/MarkPad/Document/DocumentViewModel.cs +++ b/src/MarkPad/Document/DocumentViewModel.cs @@ -1,247 +1,262 @@ -using System; -using System.IO; -using System.Net; -using System.Windows.Threading; -using Caliburn.Micro; -using CookComputing.XmlRpc; -using ICSharpCode.AvalonEdit.Document; -using MarkPad.HyperlinkEditor; -using MarkPad.Metaweblog; -using MarkPad.Services.Interfaces; -using MarkPad.Settings; -using Ookii.Dialogs.Wpf; - -namespace MarkPad.Document -{ - internal class DocumentViewModel : Screen - { - private readonly IDialogService dialogService; - private readonly ISettingsService settings; - private readonly IWindowManager windowManager; - - private readonly TimeSpan delay = TimeSpan.FromSeconds(0.5); - private readonly DispatcherTimer timer; - - private string title; - private string filename; - - public DocumentViewModel(IDialogService dialogService, ISettingsService settings, IWindowManager windowManager) - { - this.dialogService = dialogService; - this.settings = settings; - this.windowManager = windowManager; - - title = "New Document"; - Original = ""; - Document = new TextDocument(); - Post = new Post(); - timer = new DispatcherTimer(); - timer.Tick += TimerTick; - timer.Interval = delay; - } - - private void TimerTick(object sender, EventArgs e) - { - timer.Stop(); - NotifyOfPropertyChange(() => Render); - } - - public void Open(string path) - { - filename = path; - title = new FileInfo(path).Name; - - var text = File.ReadAllText(path); - Document.Text = text; - Original = text; - } - - public void OpenFromWeb(Post post) - { - Post = post; - title = post.permalink; - Document.Text = post.description; - Original = post.description; - } - - public Post Post { get; private set; } - - public void Update() - { - timer.Stop(); - timer.Start(); - NotifyOfPropertyChange(() => HasChanges); - NotifyOfPropertyChange(() => DisplayName); - } - - public bool Save() - { - if (!HasChanges) - return true; - - if (string.IsNullOrEmpty(filename)) - { - var path = dialogService.GetFileSavePath("Choose a location to save the document.", "*.md", Constants.ExtensionFilter + "|All Files (*.*)|*.*"); - - if (string.IsNullOrEmpty(path)) - return false; - - filename = path; - title = new FileInfo(filename).Name; - NotifyOfPropertyChange(() => DisplayName); - } - - File.WriteAllText(filename, Document.Text); - Original = Document.Text; - - return true; - } - - public TextDocument Document { get; set; } - - public string Original { get; set; } - - public string Render - { - get { return DocumentParser.Parse(Document.Text); } - } - - public string RenderBody - { - get { return DocumentParser.GetBodyContents(Document.Text); } - } - - public bool HasChanges - { - get { return Original != Document.Text; } - } - - public override string DisplayName - { - get { return title; } - } - - public override void CanClose(Action callback) - { - DocumentView view = (DocumentView)this.GetView(); - - if (!HasChanges) - { - view.wb.Close(); - callback(true); - return; - } - - var saveResult = dialogService.ShowConfirmationWithCancel("MarkPad", "Save modifications.", "Do you want to save your changes to '" + title + "'?", - new ButtonExtras(ButtonType.Yes, "Save", - string.IsNullOrEmpty(filename) ? "The file has not been saved yet" : "The file will be saved to " + Path.GetFullPath(filename)), - new ButtonExtras(ButtonType.No, "Don't Save", "Close the document without saving the modifications"), - new ButtonExtras(ButtonType.Cancel, "Cancel", "Don't close the document") - ); - var result = false; - - // true = Yes - switch (saveResult) - { - case true: - result = Save(); - break; - case false: - result = true; - break; - } - - // Close browser if tab is being closed - if (result == true) - { - view.wb.Close(); - } - - callback(result); - } - - public void Print() - { - var view = GetView() as DocumentView; - if (view != null) - { - view.wb.Print(); - } - } - - public bool DistractionFree { get; set; } - - public void Publish(string postid, string postTitle, string[] categories, BlogSetting blog) - { - if (categories == null) categories = new string[0]; - - var proxy = new MetaWeblog(blog.WebAPI); - - var newpost = new Post(); - try - { - if (string.IsNullOrWhiteSpace(postid)) - { - var permalink = DisplayName.Split('.')[0] == "New Document" - ? postTitle - : DisplayName.Split('.')[0]; - - newpost = new Post - { - permalink = permalink, - title = postTitle, - dateCreated = DateTime.Now, - description = blog.Language == "HTML" ? RenderBody : Document.Text, - categories = categories - }; - newpost.postid = proxy.NewPost(blog.BlogInfo.blogid, blog.Username, blog.Password, newpost, true); - - settings.Set(newpost.permalink, newpost); - settings.Save(); - } - else - { - newpost = proxy.GetPost(postid, blog.Username, blog.Password); - newpost.title = postTitle; - newpost.description = blog.Language == "HTML" ? RenderBody : Document.Text; - newpost.categories = categories; - newpost.format = blog.Language; - - proxy.EditPost(postid, blog.Username, blog.Password, newpost, true); - - //Not sure what this is doing?? - settings.Set(newpost.permalink, newpost); - settings.Save(); - } - } - catch (WebException ex) - { - dialogService.ShowError("Error Publishing", ex.Message, ""); - } - catch (XmlRpcException ex) - { - dialogService.ShowError("Error Publishing", ex.Message, ""); - } - catch (XmlRpcFaultException ex) - { - dialogService.ShowError("Error Publishing", ex.Message, ""); - } - - Post = newpost; - Original = Document.Text; - title = postTitle; - NotifyOfPropertyChange(() => DisplayName); - } - - public MarkPadHyperlink GetHyperlink(MarkPadHyperlink hyperlink) - { - var viewModel = new HyperlinkEditorViewModel(hyperlink.Text, hyperlink.Url); - windowManager.ShowDialog(viewModel); - if (!viewModel.WasCancelled) - { - hyperlink.Set(viewModel.Text, viewModel.Url); - } - return hyperlink; - } - } -} +using System; +using System.IO; +using System.Net; +using System.Threading.Tasks; +using System.Windows.Threading; +using Caliburn.Micro; +using CookComputing.XmlRpc; +using ICSharpCode.AvalonEdit.Document; +using MarkPad.HyperlinkEditor; +using MarkPad.Metaweblog; +using MarkPad.Services.Interfaces; +using MarkPad.Settings; +using Ookii.Dialogs.Wpf; + +namespace MarkPad.Document +{ + internal class DocumentViewModel : Screen + { + private static ILog Log = LogManager.GetLog(typeof(DocumentViewModel)); + + private readonly IDialogService dialogService; + private readonly ISettingsService settings; + private readonly IWindowManager windowManager; + + private readonly TimeSpan delay = TimeSpan.FromSeconds(0.5); + private readonly DispatcherTimer timer; + + private string title; + private string filename; + + public DocumentViewModel(IDialogService dialogService, ISettingsService settings, IWindowManager windowManager) + { + this.dialogService = dialogService; + this.settings = settings; + this.windowManager = windowManager; + + title = "New Document"; + Original = ""; + Document = new TextDocument(); + Post = new Post(); + timer = new DispatcherTimer(); + timer.Tick += TimerTick; + timer.Interval = delay; + } + + private void TimerTick(object sender, EventArgs e) + { + timer.Stop(); + + Task.Factory.StartNew(text => + { + return DocumentParser.Parse(text.ToString()); + }, Document.Text) + .ContinueWith(s => + { + if (s.IsFaulted) + { + Log.Error(s.Exception); + return; + } + + this.Render = s.Result; + }, TaskScheduler.FromCurrentSynchronizationContext()); + } + + public void Open(string path) + { + filename = path; + title = new FileInfo(path).Name; + + var text = File.ReadAllText(path); + Document.Text = text; + Original = text; + + Update(); + } + + public void OpenFromWeb(Post post) + { + Post = post; + title = post.permalink; + Document.Text = post.description; + Original = post.description; + + Update(); + } + + public Post Post { get; private set; } + + public void Update() + { + timer.Stop(); + timer.Start(); + NotifyOfPropertyChange(() => HasChanges); + NotifyOfPropertyChange(() => DisplayName); + } + + public bool Save() + { + if (!HasChanges) + return true; + + if (string.IsNullOrEmpty(filename)) + { + var path = dialogService.GetFileSavePath("Choose a location to save the document.", "*.md", Constants.ExtensionFilter + "|All Files (*.*)|*.*"); + + if (string.IsNullOrEmpty(path)) + return false; + + filename = path; + title = new FileInfo(filename).Name; + NotifyOfPropertyChange(() => DisplayName); + } + + File.WriteAllText(filename, Document.Text); + Original = Document.Text; + + return true; + } + + public TextDocument Document { get; set; } + + public string Original { get; set; } + + public string Render { get; private set; } + + public bool HasChanges + { + get { return Original != Document.Text; } + } + + public override string DisplayName + { + get { return title; } + } + + public override void CanClose(Action callback) + { + DocumentView view = (DocumentView)this.GetView(); + + if (!HasChanges) + { + view.wb.Close(); + callback(true); + return; + } + + var saveResult = dialogService.ShowConfirmationWithCancel("MarkPad", "Save modifications.", "Do you want to save your changes to '" + title + "'?", + new ButtonExtras(ButtonType.Yes, "Save", + string.IsNullOrEmpty(filename) ? "The file has not been saved yet" : "The file will be saved to " + Path.GetFullPath(filename)), + new ButtonExtras(ButtonType.No, "Don't Save", "Close the document without saving the modifications"), + new ButtonExtras(ButtonType.Cancel, "Cancel", "Don't close the document") + ); + var result = false; + + // true = Yes + switch (saveResult) + { + case true: + result = Save(); + break; + case false: + result = true; + break; + } + + // Close browser if tab is being closed + if (result == true) + { + view.wb.Close(); + } + + callback(result); + } + + public void Print() + { + var view = GetView() as DocumentView; + if (view != null) + { + view.wb.Print(); + } + } + + public bool DistractionFree { get; set; } + + public void Publish(string postid, string postTitle, string[] categories, BlogSetting blog) + { + if (categories == null) categories = new string[0]; + + var proxy = new MetaWeblog(blog.WebAPI); + + var newpost = new Post(); + try + { + var renderBody = DocumentParser.GetBodyContents(Document.Text); + + if (string.IsNullOrWhiteSpace(postid)) + { + var permalink = DisplayName.Split('.')[0] == "New Document" + ? postTitle + : DisplayName.Split('.')[0]; + + newpost = new Post + { + permalink = permalink, + title = postTitle, + dateCreated = DateTime.Now, + description = blog.Language == "HTML" ? renderBody : Document.Text, + categories = categories + }; + newpost.postid = proxy.NewPost(blog.BlogInfo.blogid, blog.Username, blog.Password, newpost, true); + + settings.Set(newpost.permalink, newpost); + settings.Save(); + } + else + { + newpost = proxy.GetPost(postid, blog.Username, blog.Password); + newpost.title = postTitle; + newpost.description = blog.Language == "HTML" ? renderBody : Document.Text; + newpost.categories = categories; + newpost.format = blog.Language; + + proxy.EditPost(postid, blog.Username, blog.Password, newpost, true); + + //Not sure what this is doing?? + settings.Set(newpost.permalink, newpost); + settings.Save(); + } + } + catch (WebException ex) + { + dialogService.ShowError("Error Publishing", ex.Message, ""); + } + catch (XmlRpcException ex) + { + dialogService.ShowError("Error Publishing", ex.Message, ""); + } + catch (XmlRpcFaultException ex) + { + dialogService.ShowError("Error Publishing", ex.Message, ""); + } + + Post = newpost; + Original = Document.Text; + title = postTitle; + NotifyOfPropertyChange(() => DisplayName); + } + + public MarkPadHyperlink GetHyperlink(MarkPadHyperlink hyperlink) + { + var viewModel = new HyperlinkEditorViewModel(hyperlink.Text, hyperlink.Url); + windowManager.ShowDialog(viewModel); + if (!viewModel.WasCancelled) + { + hyperlink.Set(viewModel.Text, viewModel.Url); + } + return hyperlink; + } + } +} diff --git a/src/MarkPad/Document/ParsedDocument.cs b/src/MarkPad/Document/ParsedDocument.cs index e5b41d6d..8f6c194f 100644 --- a/src/MarkPad/Document/ParsedDocument.cs +++ b/src/MarkPad/Document/ParsedDocument.cs @@ -1,88 +1,93 @@ -using System; -using System.Text.RegularExpressions; -using MarkdownSharp; - -namespace MarkPad.Document -{ - static class DocumentParser - { - private static readonly Markdown MarkdownParser = new Markdown(); - - public static string Parse(string source) - { - const string delimiter = "---"; - - var components = source.Split(new[] { delimiter }, 2, StringSplitOptions.RemoveEmptyEntries); - - string header; - string contents; - - if (components.Length == 0) - { - header = ""; - contents = ""; - } - else if (components.Length == 2) - { - header = components[0]; - contents = components[1]; - } - else - { - header = ""; - contents = components[0]; - } - - return ToHtml(header, contents); - } - - public static string GetBodyContents(string source) - { - const string delimiter = "---"; - - var components = source.Split(new[] { delimiter }, 2, StringSplitOptions.RemoveEmptyEntries); - - string contents; - - if (components.Length == 0) - { - contents = ""; - } - else if (components.Length == 2) - { - contents = components[1]; - } - else - { - contents = components[0]; - } - - return MarkdownParser.Transform(contents); - } - - private static string ToHtml(string header, string contents) - { - var body = MarkdownParser.Transform(contents); - - string themeName; - string head = ""; - - if (TryGetHeaderValue(header, "theme", out themeName)) - head = String.Format(@"", themeName); - - var document = String.Format("\r\n\r\n{0}\r\n\r\n\r\n{1}\r\n\r\n", head, body); - - return document; - } - - private static bool TryGetHeaderValue(string header, string key, out string value) - { - // TODO: Cache these? - var match = Regex.Match(header, "^" + key + "\\s*:\\s*(.*)$", RegexOptions.Multiline | RegexOptions.IgnoreCase); - - value = match.Success ? match.Result("$1").Trim() : String.Empty; - - return match.Success; - } - } -} +using System; +using System.Text.RegularExpressions; +using MarkdownDeep; + +namespace MarkPad.Document +{ + static class DocumentParser + { + private static readonly Markdown markdown = new Markdown(); + + public static string Parse(string source) + { + const string delimiter = "---"; + + var components = source.Split(new[] { delimiter }, 2, StringSplitOptions.RemoveEmptyEntries); + + string header; + string contents; + + if (components.Length == 0) + { + header = ""; + contents = ""; + } + else if (components.Length == 2) + { + header = components[0]; + contents = components[1]; + } + else + { + header = ""; + contents = components[0]; + } + + return ToHtml(header, contents); + } + + public static string GetBodyContents(string source) + { + const string delimiter = "---"; + + var components = source.Split(new[] { delimiter }, 2, StringSplitOptions.RemoveEmptyEntries); + + string contents; + + if (components.Length == 0) + { + contents = ""; + } + else if (components.Length == 2) + { + contents = components[1]; + } + else + { + contents = components[0]; + } + + return MarkdownConvert(contents); + } + + private static string MarkdownConvert(string contents) + { + return markdown.Transform(contents); + } + + private static string ToHtml(string header, string contents) + { + var body = MarkdownConvert(contents); + + string themeName; + string head = ""; + + if (TryGetHeaderValue(header, "theme", out themeName)) + head = String.Format(@"", themeName); + + var document = String.Format("\r\n\r\n{0}\r\n\r\n\r\n{1}\r\n\r\n", head, body); + + return document; + } + + private static bool TryGetHeaderValue(string header, string key, out string value) + { + // TODO: Cache these? + var match = Regex.Match(header, "^" + key + "\\s*:\\s*(.*)$", RegexOptions.Multiline | RegexOptions.IgnoreCase); + + value = match.Success ? match.Result("$1").Trim() : String.Empty; + + return match.Success; + } + } +} diff --git a/src/MarkPad/MarkPad.csproj b/src/MarkPad/MarkPad.csproj index 98b6d727..df836c03 100644 --- a/src/MarkPad/MarkPad.csproj +++ b/src/MarkPad/MarkPad.csproj @@ -90,8 +90,8 @@ ..\..\packages\MahApps.Metro.0.4.0.17\lib\net40\MahApps.Metro.dll - - ..\..\packages\MarkdownSharp.1.13.0.0\lib\35\MarkdownSharp.dll + + ..\..\packages\MarkdownDeep.NET.1.4\lib\.NetFramework 3.5\MarkdownDeep.dll ..\..\lib\Hunspell.0.9.6.0\lib\net20\NHunspell.dll diff --git a/src/MarkPad/packages.config b/src/MarkPad/packages.config index acaa2bc9..21ccac12 100644 --- a/src/MarkPad/packages.config +++ b/src/MarkPad/packages.config @@ -5,7 +5,7 @@ - + \ No newline at end of file