+
+ /// <summary>
+ /// Scans the next paragraph for http:/ ftp:/ www. https:/ etc and marks the tags
+ /// as links.
+ /// </summary>
+ /// <param name="start_line">The line to start on</param>
+ /// <param name="link_changed">marks as true if something is changed</param>
+ private void ScanForLinks (Line start_line, ref bool link_changed)
+ {
+ Line current_line = start_line;
+ StringBuilder line_no_breaks = new StringBuilder ();
+ StringBuilder line_link_record = new StringBuilder ();
+ ArrayList cumulative_length_list = new ArrayList ();
+ bool update_caret_tag = false;
+
+ cumulative_length_list.Add (0);
+
+ while (current_line != null) {
+ line_no_breaks.Append (current_line.text);
+
+ if (link_changed == false)
+ current_line.LinkRecord (line_link_record);
+
+ current_line.ClearLinks ();
+
+ cumulative_length_list.Add (line_no_breaks.Length);
+
+ if (current_line.ending == LineEnding.Wrap)
+ current_line = GetLine (current_line.LineNo + 1);
+ else
+ break;
+ }
+
+ // search for protocols.. make sure www. is first!
+ string [] search_terms = new string [] { "www.", "http:/", "ftp:/", "https:/" };
+ int search_found = 0;
+ int index_found = 0;
+ string line_no_breaks_string = line_no_breaks.ToString ();
+ int line_no_breaks_index = 0;
+ int link_end = 0;
+
+ while (true) {
+ if (line_no_breaks_index >= line_no_breaks_string.Length)
+ break;
+
+ index_found = FirstIndexOfAny (line_no_breaks_string, search_terms, line_no_breaks_index, out search_found);
+
+ //no links found on this line
+ if (index_found == -1)
+ break;
+
+ if (search_found == 0) {
+ // if we are at the end of the line to analyse and the end of the line
+ // is "www." then there are no links here
+ if (line_no_breaks_string.Length == index_found + search_terms [0].Length)
+ break;
+
+ // if after www. we don't have a letter a digit or a @ or - or /
+ // then it is not a web address, we should continue searching
+ if (char.IsLetterOrDigit (line_no_breaks_string [index_found + search_terms [0].Length]) == false &&
+ "@/~".IndexOf (line_no_breaks_string [index_found + search_terms [0].Length].ToString ()) == -1) {
+ line_no_breaks_index = index_found + search_terms [0].Length;
+ continue;
+ }
+ }
+
+ link_end = line_no_breaks_string.Length - 1;
+ line_no_breaks_index = line_no_breaks_string.Length;
+
+ // we've found a link, we just need to find where it ends now
+ for (int i = index_found + search_terms [search_found].Length; i < line_no_breaks_string.Length; i++) {
+ if (line_no_breaks_string [i - 1] == '.') {
+ if (char.IsLetterOrDigit (line_no_breaks_string [i]) == false &&
+ "@/~".IndexOf (line_no_breaks_string [i].ToString ()) == -1) {
+ link_end = i - 1;
+ line_no_breaks_index = i;
+ break;
+ }
+ } else {
+ if (char.IsLetterOrDigit (line_no_breaks_string [i]) == false &&
+ "@-/:~.?=_&".IndexOf (line_no_breaks_string [i].ToString ()) == -1) {
+ link_end = i - 1;
+ line_no_breaks_index = i;
+ break;
+ }
+ }
+ }
+
+ string link_text = line_no_breaks_string.Substring (index_found, link_end - index_found + 1);
+ int current_cumulative = 0;
+
+ // we've found a link - index_found -> link_end
+ // now we just make all the tags as containing link and
+ // point them to the text for the whole link
+
+ current_line = start_line;
+
+ //find the line we start on
+ for (current_cumulative = 1; current_cumulative < cumulative_length_list.Count; current_cumulative++)
+ if ((int)cumulative_length_list [current_cumulative] > index_found)
+ break;
+
+ current_line = GetLine (start_line.LineNo + current_cumulative - 1);
+
+ // find the tag we start on
+ LineTag current_tag = current_line.FindTag (index_found - (int)cumulative_length_list [current_cumulative - 1] + 1);
+
+ if (current_tag.Start != (index_found - (int)cumulative_length_list [current_cumulative - 1]) + 1) {
+ if (current_tag == CaretTag)
+ update_caret_tag = true;
+
+ current_tag = current_tag.Break ((index_found - (int)cumulative_length_list [current_cumulative - 1]) + 1);
+ }
+
+ // set the tag
+ current_tag.IsLink = true;
+ current_tag.LinkText = link_text;
+
+ //go through each character
+ // find the tag we are in
+ // skip the number of characters in the tag
+ for (int i = 1; i < link_text.Length; i++) {
+ // on to a new word-wrapped line
+ if ((int)cumulative_length_list [current_cumulative] <= index_found + i) {
+
+ current_line = GetLine (start_line.LineNo + current_cumulative++);
+ current_tag = current_line.FindTag (index_found + i - (int)cumulative_length_list [current_cumulative - 1] + 1);
+
+ current_tag.IsLink = true;
+ current_tag.LinkText = link_text;
+
+ continue;
+ }
+
+ if (current_tag.End < index_found + 1 + i - (int)cumulative_length_list [current_cumulative - 1]) {
+ // skip empty tags in the middle of the URL
+ do {
+ current_tag = current_tag.Next;
+ } while (current_tag.Length == 0);
+
+ current_tag.IsLink = true;
+ current_tag.LinkText = link_text;
+ }
+ }
+
+ //if there are characters left in the tag after the link
+ // split the tag
+ // make the second part a non link
+ if (current_tag.End > (index_found + link_text.Length + 1) - (int)cumulative_length_list [current_cumulative - 1]) {
+ if (current_tag == CaretTag)
+ update_caret_tag = true;
+
+ current_tag.Break ((index_found + link_text.Length + 1) - (int)cumulative_length_list [current_cumulative - 1]);
+ }
+ }
+
+ if (update_caret_tag) {
+ CaretTag = LineTag.FindTag (CaretLine, CaretPosition);
+ link_changed = true;
+ } else {
+ if (link_changed == false) {
+ current_line = start_line;
+ StringBuilder new_link_record = new StringBuilder ();
+
+ while (current_line != null) {
+ current_line.LinkRecord (new_link_record);
+
+ if (current_line.ending == LineEnding.Wrap)
+ current_line = GetLine (current_line.LineNo + 1);
+ else
+ break;
+ }
+
+ if (new_link_record.Equals (line_link_record) == false)
+ link_changed = true;
+ }
+ }
+ }
+
+ private int FirstIndexOfAny (string haystack, string [] needles, int start_index, out int term_found)
+ {
+ term_found = -1;
+ int best_index = -1;
+
+ for (int i = 0; i < needles.Length; i++) {
+ int index = haystack.IndexOf (needles [i], start_index, StringComparison.InvariantCultureIgnoreCase);
+
+ if (index > -1) {
+ if (term_found > -1) {
+ if (index < best_index) {
+ best_index = index;
+ term_found = i;
+ }
+ } else {
+ best_index = index;
+ term_found = i;
+ }
+ }
+ }
+
+ return best_index;
+ }
+
+
+
+ private void InvalidateLinks (Rectangle clip)
+ {
+ for (int i = (owner.list_links.Count - 1); i >= 0; i--) {
+ TextBoxBase.LinkRectangle link = (TextBoxBase.LinkRectangle) owner.list_links [i];
+
+ if (clip.IntersectsWith (link.LinkAreaRectangle))
+ owner.list_links.RemoveAt (i);
+ }
+ }