public static void ParseUrls(MessageModel msg) { // clone MessageParts IList <MessagePartModel> parts = new List <MessagePartModel>(msg.MessageParts); foreach (MessagePartModel part in parts) { if (part is UrlMessagePartModel) { // no need to reparse URL parts continue; } if (!(part is TextMessagePartModel)) { continue; } TextMessagePartModel textPart = (TextMessagePartModel)part; Match urlMatch = UrlRegex.Match(textPart.Text); // OPT: fast regex scan if (!urlMatch.Success) { // no URLs in this MessagePart, nothing to do continue; } // found URL(s) // remove current MessagePartModel as we need to split it int idx = msg.MessageParts.IndexOf(part); msg.MessageParts.RemoveAt(idx); string[] textPartParts = textPart.Text.Split(new char[] { ' ' }); for (int i = 0; i < textPartParts.Length; i++) { string textPartPart = textPartParts[i]; urlMatch = UrlRegex.Match(textPartPart); if (urlMatch.Success) { UrlMessagePartModel urlPart = new UrlMessagePartModel(textPartPart); //urlPart.ForegroundColor = new TextColor(); msg.MessageParts.Insert(idx++, urlPart); msg.MessageParts.Insert(idx++, new TextMessagePartModel(" ")); } else { // FIXME: we put each text part into it's own object, instead of combining them (the smart way) TextMessagePartModel notUrlPart = new TextMessagePartModel(textPartPart + " "); // restore formatting / colors from the original text part notUrlPart.IsHighlight = textPart.IsHighlight; notUrlPart.ForegroundColor = textPart.ForegroundColor; notUrlPart.BackgroundColor = textPart.BackgroundColor; notUrlPart.Bold = textPart.Bold; notUrlPart.Italic = textPart.Italic; notUrlPart.Underline = textPart.Underline; msg.MessageParts.Insert(idx++, notUrlPart); } } } }
public void ToMarkup() { MessageModel testmodel = new MessageModel(); testmodel.IsCompactable = false; TextMessagePartModel textmodel; UrlMessagePartModel urlmodel; textmodel = new TextMessagePartModel("normal"); testmodel.MessageParts.Add(textmodel); textmodel = new TextMessagePartModel("blue"); textmodel.ForegroundColor = TextColor.Parse("0000FF"); testmodel.MessageParts.Add(textmodel); textmodel = new TextMessagePartModel("bold"); textmodel.Bold = true; testmodel.MessageParts.Add(textmodel); textmodel = new TextMessagePartModel("bold2"); textmodel.Bold = true; testmodel.MessageParts.Add(textmodel); textmodel = new TextMessagePartModel("normal"); testmodel.MessageParts.Add(textmodel); textmodel = new TextMessagePartModel("underline"); textmodel.Underline = true; testmodel.MessageParts.Add(textmodel); textmodel = new TextMessagePartModel("combined"); textmodel.Underline = true; textmodel.Bold = true; textmodel.Italic = true; textmodel.ForegroundColor = TextColor.Parse("00FF00"); textmodel.BackgroundColor = TextColor.Parse("0000FF"); testmodel.MessageParts.Add(textmodel); urlmodel = new UrlMessagePartModel("http://www.smuxi.org"); testmodel.MessageParts.Add(urlmodel); textmodel = new TextMessagePartModel("normal"); testmodel.MessageParts.Add(textmodel); string expected = "normal<span color='#0000FF'>blue</span>" + "<b>bold</b><b>bold2</b>normal<u>underline</u>" + "<span color='#00FF00'><u><b><i>combined</i></b></u></span>" + "<span color='#00008B'><u>http://www.smuxi.org</u></span>normal"; string tested = PangoTools.ToMarkup(testmodel); Assert.AreEqual(expected, tested); }
void ParseHtml(XmlNode node, TextMessagePartModel model) { TextMessagePartModel submodel; string nodetype = node.Name.ToLower(); if (model is UrlMessagePartModel) { submodel = new UrlMessagePartModel(model); } else if (nodetype == "a") { submodel = new UrlMessagePartModel(model); (submodel as UrlMessagePartModel).Url = node.Attributes.GetNamedItem("href").Value; } else { submodel = new TextMessagePartModel(model); } switch (nodetype) { case "b": case "strong": submodel.Bold = true; break; case "i": case "em": submodel.Italic = true; break; case "u": submodel.Underline = true; break; default: break; } if (node.Attributes != null) { ParseStyle(node.Attributes.GetNamedItem("style"), submodel); } if (node.HasChildNodes) { foreach (XmlNode child in node.ChildNodes) { // clone this model TextMessagePartModel nextmodel; if (submodel is UrlMessagePartModel) { nextmodel = new UrlMessagePartModel(submodel); } else { nextmodel = new TextMessagePartModel(submodel); } ParseHtml(child, nextmodel); } } else { // final node if (nodetype == "br") { AppendText("\n"); } else if (nodetype == "img") { AppendUrl(node.Attributes.GetNamedItem("src").Value, "[image placeholder - UNIMPLEMENTED]"); } else { model.Text = HttpUtility.HtmlDecode(node.Value); AppendText(model); } } }
public static IList <MessagePartModel> ParsePatterns(TextMessagePartModel textPart, List <MessagePatternModel> patterns) { if (textPart == null) { throw new ArgumentNullException("textPart"); } if (patterns == null) { throw new ArgumentNullException("patterns"); } var msgParts = new List <MessagePartModel>(); if (patterns.Count == 0) { // all patterns have been tried -> this text is PURE text msgParts.Add(textPart); return(msgParts); } var remainingPatterns = new List <MessagePatternModel>(patterns); var pattern = remainingPatterns.First(); remainingPatterns.Remove(pattern); var match = pattern.MessagePartPattern.Match(textPart.Text); if (!match.Success) { // no matches in this MessagePart, try other smartlinks return(ParsePatterns(textPart, remainingPatterns)); } int lastindex = 0; do { var groupValues = new string[match.Groups.Count]; int i = 0; foreach (Group @group in match.Groups) { groupValues[i++] = @group.Value; } string url; if (String.IsNullOrEmpty(pattern.LinkFormat)) { url = match.Value; } else { url = String.Format(pattern.LinkFormat, groupValues); } string text; if (String.IsNullOrEmpty(pattern.TextFormat)) { text = match.Value; } else { text = String.Format(pattern.TextFormat, groupValues); } if (lastindex != match.Index) { // there were some non-matching-chars before the match // copy that to a TextMessagePartModel var notMatchPart = new TextMessagePartModel(textPart); // only take the proper chunk of text notMatchPart.Text = textPart.Text.Substring(lastindex, match.Index - lastindex); // and try other patterns on this part var parts = ParsePatterns(notMatchPart, remainingPatterns); foreach (var part in parts) { msgParts.Add(part); } } MessagePartModel msgPart; if (pattern.MessagePartType == typeof(UrlMessagePartModel)) { // no need to set URL and text if they are the same text = text == url ? null : text; msgPart = new UrlMessagePartModel(url, text); } else if (pattern.MessagePartType == typeof(ImageMessagePartModel)) { msgPart = new ImageMessagePartModel(url, text); } else { msgPart = new TextMessagePartModel(text); } msgParts.Add(msgPart); lastindex = match.Index + match.Length; match = match.NextMatch(); } while (match.Success); if (lastindex != textPart.Text.Length) { // there were some non-url-chars before this url // copy TextMessagePartModel var notMatchPart = new TextMessagePartModel(textPart); // only take the proper chunk of text notMatchPart.Text = textPart.Text.Substring(lastindex); // and try other smartlinks on this part var parts = ParsePatterns(notMatchPart, remainingPatterns); foreach (var part in parts) { msgParts.Add(part); } } return(msgParts); }
public static void ParseUrls(MessageModel msg) { string urlRegex; //urlRegex = "((([a-zA-Z][0-9a-zA-Z+\\-\\.]*:)?/{0,2}[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?(#[0-9a-zA-Z;/?:@&=+$\\.\\-_!~*'()%]+)?)"); // It was constructed according to the BNF grammar given in RFC 2396 (http://www.ietf.org/rfc/rfc2396.txt). /* urlRegex = @"^(?<s1>(?<s0>[^:/\?#]+):)?(?<a1>" + @"//(?<a0>[^/\?#]*))?(?<p0>[^\?#]*)" + @"(?<q1>\?(?<q0>[^#]*))?" + @"(?<f1>#(?<f0>.*))?"); */ urlRegex = @"(^| )(((https?|ftp):\/\/)|www\.)(([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)|localhost|([a-zA-Z0-9\-]+\.)*[a-zA-Z0-9\-]+\.(com|net|org|info|biz|gov|name|edu|[a-zA-Z][a-zA-Z]))(:[0-9]+)?((\/|\?)[^ ""]*[^ ,;\.:"">)])?"; Regex reg = new Regex(urlRegex, RegexOptions.IgnoreCase); // clone MessageParts IList<MessagePartModel> parts = new List<MessagePartModel>(msg.MessageParts); foreach (MessagePartModel part in parts) { if (part is UrlMessagePartModel) { // no need to reparse URL parts continue; } if (!(part is TextMessagePartModel)) { continue; } TextMessagePartModel textPart = (TextMessagePartModel) part; Match urlMatch = reg.Match(textPart.Text); // OPT: fast regex scan if (!urlMatch.Success) { // no URLs in this MessagePart, nothing to do continue; } // found URL(s) // remove current MessagePartModel as we need to split it int idx = msg.MessageParts.IndexOf(part); msg.MessageParts.RemoveAt(idx); string[] textPartParts = textPart.Text.Split(new char[] {' '}); for (int i = 0; i < textPartParts.Length; i++) { string textPartPart = textPartParts[i]; urlMatch = reg.Match(textPartPart); if (urlMatch.Success) { UrlMessagePartModel urlPart = new UrlMessagePartModel(textPartPart); //urlPart.ForegroundColor = new TextColor(); msg.MessageParts.Insert(idx++, urlPart); msg.MessageParts.Insert(idx++, new TextMessagePartModel(" ")); } else { // FIXME: we put each text part into it's own object, instead of combining them (the smart way) TextMessagePartModel notUrlPart = new TextMessagePartModel(textPartPart + " "); // restore formatting / colors from the original text part notUrlPart.IsHighlight = textPart.IsHighlight; notUrlPart.ForegroundColor = textPart.ForegroundColor; notUrlPart.BackgroundColor = textPart.BackgroundColor; notUrlPart.Bold = textPart.Bold; notUrlPart.Italic = textPart.Italic; notUrlPart.Underline = textPart.Underline; msg.MessageParts.Insert(idx++, notUrlPart); } } } }
private void InsertToBuffer(Gtk.TextBuffer buffer, ref Gtk.TextIter iter, UrlMessagePartModel urlPart) { var linkText = urlPart.Text ?? urlPart.Url; Uri uri; try { uri = new Uri(urlPart.Url); } catch (UriFormatException ex) { #if LOG4NET _Logger.Error("AddMessage(): Invalid URL: " + urlPart.Url, ex); #endif buffer.Insert(ref iter, linkText); return; } var tags = new List<Gtk.TextTag>(); // link URI tag var linkTag = new LinkTag(uri); linkTag.TextEvent += OnLinkTagTextEvent; _MessageTextTagTable.Add(linkTag); tags.Add(linkTag); // link style tag tags.Add(LinkTag); buffer.InsertWithTags(ref iter, linkText, tags.ToArray()); }
void ParseHtml(XmlNode node, TextMessagePartModel model) { TextMessagePartModel submodel; string nodetype = node.Name.ToLower(); if (model is UrlMessagePartModel) { submodel = new UrlMessagePartModel(model); } else if (nodetype == "a") { submodel = new UrlMessagePartModel(model); (submodel as UrlMessagePartModel).Url = node.Attributes.GetNamedItem("href").Value; } else { submodel = new TextMessagePartModel(model); } switch (nodetype) { case "b": case "strong": submodel.Bold = true; break; case "i": case "em": submodel.Italic = true; break; case "u": submodel.Underline = true; break; default: break; } if (node.Attributes != null) { ParseStyle(node.Attributes.GetNamedItem("style"), submodel); } if (node.HasChildNodes) { foreach (XmlNode child in node.ChildNodes) { // clone this model TextMessagePartModel nextmodel; if (submodel is UrlMessagePartModel) { nextmodel = new UrlMessagePartModel(submodel); } else { nextmodel = new TextMessagePartModel(submodel); } ParseHtml(child, nextmodel); } } else { // final node if (nodetype == "br") { AppendText("\n"); } else if (nodetype == "img") { AppendUrl(node.Attributes.GetNamedItem("src").Value, "[image placeholder - UNIMPLEMENTED]"); } else { model.Text = HttpUtility.HtmlDecode(node.Value); AppendText(model); } } }
public static IList<MessagePartModel> ParsePatterns(TextMessagePartModel textPart, List<MessagePatternModel> patterns) { if (textPart == null) { throw new ArgumentNullException("textPart"); } if (patterns == null) { throw new ArgumentNullException("patterns"); } var msgParts = new List<MessagePartModel>(); if (patterns.Count == 0) { // all patterns have been tried -> this text is PURE text msgParts.Add(textPart); return msgParts; } var remainingPatterns = new List<MessagePatternModel>(patterns); var pattern = remainingPatterns.First(); remainingPatterns.Remove(pattern); var match = pattern.MessagePartPattern.Match(textPart.Text); if (!match.Success) { // no matches in this MessagePart, try other smartlinks return ParsePatterns(textPart, remainingPatterns); } int lastindex = 0; do { var groupValues = new string[match.Groups.Count]; int i = 0; foreach (Group @group in match.Groups) { groupValues[i++] = @group.Value; } string url; if (String.IsNullOrEmpty(pattern.LinkFormat)) { url = match.Value; } else { url = String.Format(pattern.LinkFormat, groupValues); } string text; if (String.IsNullOrEmpty(pattern.TextFormat)) { text = match.Value; } else { text = String.Format(pattern.TextFormat, groupValues); } if (lastindex != match.Index) { // there were some non-matching-chars before the match // copy that to a TextMessagePartModel var notMatchPart = new TextMessagePartModel(textPart); // only take the proper chunk of text notMatchPart.Text = textPart.Text.Substring(lastindex, match.Index - lastindex); // and try other patterns on this part var parts = ParsePatterns(notMatchPart, remainingPatterns); foreach (var part in parts) { msgParts.Add(part); } } MessagePartModel msgPart; if (pattern.MessagePartType == typeof(UrlMessagePartModel)) { // no need to set URL and text if they are the same text = text == url ? null : text; msgPart = new UrlMessagePartModel(url, text); } else if (pattern.MessagePartType == typeof(ImageMessagePartModel)) { msgPart = new ImageMessagePartModel(url, text); } else { msgPart = new TextMessagePartModel(text); } msgParts.Add(msgPart); lastindex = match.Index + match.Length; match = match.NextMatch(); } while (match.Success); if (lastindex != textPart.Text.Length) { // there were some non-url-chars before this url // copy TextMessagePartModel var notMatchPart = new TextMessagePartModel(textPart); // only take the proper chunk of text notMatchPart.Text = textPart.Text.Substring(lastindex); // and try other smartlinks on this part var parts = ParsePatterns(notMatchPart, remainingPatterns); foreach (var part in parts) { msgParts.Add(part); } } return msgParts; }
public static IList <MessagePartModel> ParsePatterns(TextMessagePartModel textPart, List <MessagePatternModel> patterns) { if (textPart == null) { throw new ArgumentNullException("textPart"); } if (patterns == null) { throw new ArgumentNullException("patterns"); } var msgParts = new List <MessagePartModel>(); if (patterns.Count == 0) { // all patterns have been tried -> this text is PURE text msgParts.Add(textPart); return(msgParts); } var remainingPatterns = new List <MessagePatternModel>(patterns); var pattern = remainingPatterns.First(); remainingPatterns.Remove(pattern); var match = pattern.MessagePartPattern.Match(textPart.Text); if (!match.Success) { // no matches in this MessagePart, try other smartlinks return(ParsePatterns(textPart, remainingPatterns)); } int lastindex = 0; do { var startDelimiterLength = 0; var regexDelimiterForStartOfPatternValue = match.Groups[MessageBuilderSettings.StartDelimiterGroupName]; if (regexDelimiterForStartOfPatternValue != null) { startDelimiterLength = regexDelimiterForStartOfPatternValue.Value.Length; } var endDelimiterLength = 0; var regexDelimiterForEndOfPatternValue = match.Groups[MessageBuilderSettings.EndDelimiterGroupName]; if (regexDelimiterForEndOfPatternValue != null) { endDelimiterLength = regexDelimiterForEndOfPatternValue.Value.Length; } var groupValues = match.Groups.Cast <Group>() // don't get the delimiter because it only determines // the start or end of pattern, which is not part of the pattern .Where(g => g != regexDelimiterForStartOfPatternValue && g != regexDelimiterForEndOfPatternValue) .Select(g => g.Value).ToArray(); string url; if (String.IsNullOrEmpty(pattern.LinkFormat)) { url = match.Value; url = url.Substring(0 + startDelimiterLength, url.Length - (startDelimiterLength - endDelimiterLength)); } else { url = String.Format(pattern.LinkFormat, groupValues); } string text; if (String.IsNullOrEmpty(pattern.TextFormat)) { text = match.Value; } else { text = String.Format(pattern.TextFormat, groupValues); } text = text.Substring(0 + startDelimiterLength, text.Length - (startDelimiterLength + endDelimiterLength)); if (lastindex != match.Index) { // there were some non-matching-chars before the match // copy that to a TextMessagePartModel var notMatchPart = new TextMessagePartModel(textPart); // only take the proper chunk of text notMatchPart.Text = textPart.Text.Substring(lastindex, match.Index + startDelimiterLength - lastindex); // and try other patterns on this part var parts = ParsePatterns(notMatchPart, remainingPatterns); foreach (var part in parts) { msgParts.Add(part); } } MessagePartModel msgPart; if (pattern.MessagePartType == typeof(UrlMessagePartModel)) { // no need to set URL and text if they are the same text = text == url ? null : text; msgPart = new UrlMessagePartModel(url, text); } else if (pattern.MessagePartType == typeof(ImageMessagePartModel)) { msgPart = new ImageMessagePartModel(url, text); } else { msgPart = new TextMessagePartModel(text); } msgParts.Add(msgPart); lastindex = match.Index + match.Length - endDelimiterLength; match = match.NextMatch(); } while (match.Success); if (lastindex != textPart.Text.Length) { // there were some non-matching-chars after the last match // copy TextMessagePartModel var notMatchPart = new TextMessagePartModel(textPart); // only take the proper chunk of text notMatchPart.Text = textPart.Text.Substring(lastindex); // and try other smartlinks on this part var parts = ParsePatterns(notMatchPart, remainingPatterns); foreach (var part in parts) { msgParts.Add(part); } } return(msgParts); }
public static void ParseUrls(MessageModel msg) { // clone MessageParts IList<MessagePartModel> parts = new List<MessagePartModel>(msg.MessageParts); foreach (MessagePartModel part in parts) { if (part is UrlMessagePartModel) { // no need to reparse URL parts continue; } if (!(part is TextMessagePartModel)) { continue; } TextMessagePartModel textPart = (TextMessagePartModel) part; Match urlMatch = UrlRegex.Match(textPart.Text); // OPT: fast regex scan if (!urlMatch.Success) { // no URLs in this MessagePart, nothing to do continue; } // found URL(s) // remove current MessagePartModel as we need to split it int idx = msg.MessageParts.IndexOf(part); msg.MessageParts.RemoveAt(idx); string[] textPartParts = textPart.Text.Split(new char[] {' '}); for (int i = 0; i < textPartParts.Length; i++) { string textPartPart = textPartParts[i]; urlMatch = UrlRegex.Match(textPartPart); if (urlMatch.Success) { UrlMessagePartModel urlPart = new UrlMessagePartModel(textPartPart); //urlPart.ForegroundColor = new TextColor(); msg.MessageParts.Insert(idx++, urlPart); msg.MessageParts.Insert(idx++, new TextMessagePartModel(" ")); } else { // FIXME: we put each text part into it's own object, instead of combining them (the smart way) TextMessagePartModel notUrlPart = new TextMessagePartModel(textPartPart + " "); // restore formatting / colors from the original text part notUrlPart.IsHighlight = textPart.IsHighlight; notUrlPart.ForegroundColor = textPart.ForegroundColor; notUrlPart.BackgroundColor = textPart.BackgroundColor; notUrlPart.Bold = textPart.Bold; notUrlPart.Italic = textPart.Italic; notUrlPart.Underline = textPart.Underline; msg.MessageParts.Insert(idx++, notUrlPart); } } } }
public static IList<MessagePartModel> ParsePatterns(TextMessagePartModel textPart, List<MessagePatternModel> patterns) { if (textPart == null) { throw new ArgumentNullException("textPart"); } if (patterns == null) { throw new ArgumentNullException("patterns"); } var msgParts = new List<MessagePartModel>(); if (patterns.Count == 0) { // all patterns have been tried -> this text is PURE text msgParts.Add(textPart); return msgParts; } var remainingPatterns = new List<MessagePatternModel>(patterns); var pattern = remainingPatterns.First(); remainingPatterns.Remove(pattern); var match = pattern.MessagePartPattern.Match(textPart.Text); if (!match.Success) { // no matches in this MessagePart, try other smartlinks return ParsePatterns(textPart, remainingPatterns); } int lastindex = 0; do { var delimiterLength = 0; var regexDelimiterForEndOfPatternValue = match.Groups[MessageBuilderSettings.EndDelimiterGroupName]; if (regexDelimiterForEndOfPatternValue != null) { delimiterLength = regexDelimiterForEndOfPatternValue.Value.Length; } var groupValues = match.Groups.Cast<Group>() // don't get the delimiter because it only determines // the end of pattern, which is not part of the pattern .Where(g => g != regexDelimiterForEndOfPatternValue) .Select(g => g.Value).ToArray(); string url; if (String.IsNullOrEmpty(pattern.LinkFormat)) { url = match.Value; } else { url = String.Format(pattern.LinkFormat, groupValues); } url = url.Substring(0, url.Length - delimiterLength); string text; if (String.IsNullOrEmpty(pattern.TextFormat)) { text = match.Value; } else { text = String.Format(pattern.TextFormat, groupValues); } text = text.Substring(0, text.Length - delimiterLength); if (lastindex != match.Index) { // there were some non-matching-chars before the match // copy that to a TextMessagePartModel var notMatchPart = new TextMessagePartModel(textPart); // only take the proper chunk of text notMatchPart.Text = textPart.Text.Substring(lastindex, match.Index - lastindex); // and try other patterns on this part var parts = ParsePatterns(notMatchPart, remainingPatterns); foreach (var part in parts) { msgParts.Add(part); } } MessagePartModel msgPart; if (pattern.MessagePartType == typeof(UrlMessagePartModel)) { // no need to set URL and text if they are the same text = text == url ? null : text; msgPart = new UrlMessagePartModel(url, text); } else if (pattern.MessagePartType == typeof(ImageMessagePartModel)) { msgPart = new ImageMessagePartModel(url, text); } else { msgPart = new TextMessagePartModel(text); } msgParts.Add(msgPart); lastindex = match.Index + match.Length - delimiterLength; match = match.NextMatch(); } while (match.Success); if (lastindex != textPart.Text.Length) { // there were some non-url-chars before this url // copy TextMessagePartModel var notMatchPart = new TextMessagePartModel(textPart); // only take the proper chunk of text notMatchPart.Text = textPart.Text.Substring(lastindex); // and try other smartlinks on this part var parts = ParsePatterns(notMatchPart, remainingPatterns); foreach (var part in parts) { msgParts.Add(part); } } return msgParts; }