예제 #1
0
        ///<summary>Break a tag into two with identical attributes; pos is 1-based; returns tag starting at &gt;pos&lt; or null if end-of-line</summary>
        public LineTag Break(int pos)
        {
            LineTag new_tag;

#if DEBUG
            // Sanity
            if (pos < this.Start)
            {
                throw new Exception("Breaking at a negative point");
            }
#endif

#if DEBUG
            if (pos > End)
            {
                throw new Exception("Breaking past the end of a line");
            }
#endif

            new_tag = new LineTag(line, pos);
            new_tag.CopyFormattingFrom(this);

            new_tag.next     = this.next;
            this.next        = new_tag;
            new_tag.previous = this;

            if (new_tag.next != null)
            {
                new_tag.next.previous = new_tag;
            }

            return(new_tag);
        }
예제 #2
0
        public void Delete()
        {
            // If we are the only tag, we can't be deleted
            if (previous == null && next == null)
            {
                return;
            }

            // If we are the last tag, deletion is easy
            if (next == null)
            {
                previous.next = null;
                return;
            }

            // Easy cases gone, little tougher, delete ourself
            // Update links, and start
            next.previous = null;

            LineTag loop = next;

            while (loop != null)
            {
                loop.Start -= Length;
                loop        = loop.next;
            }

            return;
        }
예제 #3
0
        // This doesn't do exactly what you would think, it just pulls off the \n part of the ending
        internal void DrawEnding(Graphics dc, float y)
        {
            if (document.multiline)
            {
                return;
            }
            LineTag last = tags;

            while (last.Next != null)
            {
                last = last.Next;
            }

            string end_str = null;

            switch (document.LineEndingLength(ending))
            {
            case 0:
                return;

            case 1:
                end_str = "\u0013";
                break;

            case 2:
                end_str = "\u0013\u0013";
                break;

            case 3:
                end_str = "\u0013\u0013\u0013";
                break;
            }

            TextBoxTextRenderer.DrawText(dc, end_str, last.Font, last.Color, X + widths [TextLengthWithoutEnding()] - document.viewport_x + document.OffsetX, y, true);
        }
예제 #4
0
        // Inserts a string at the given position
        public void InsertString(int pos, string s, LineTag tag)
        {
            int len = s.Length;

            // Insert the text into the StringBuilder
            text.Insert(pos, s);

            // Check that tag is still in use in the line. If not, then we choose the last tag at that position.
            LineTag t = tags;

            while (t != null)
            {
                if (((t.Start - 1) <= pos) && (pos < (t.End - 1) || (pos == t.End - 1 && t.Length == 0)))
                {
                    // found the location
                    bool foundTag = false;
                    while (pos < (t.Start + t.Length - 1))
                    {
                        if (t == tag)
                        {
                            foundTag = true;
                            break;
                        }
                        if (t.Next == null)
                        {
                            break;
                        }
                        t = t.Next;
                    }
                    if (!foundTag)
                    {
                        if (pos < (t.Start + t.Length - 1))
                        {
                            tag = t.Previous;
                        }
                        else
                        {
                            tag = t;
                        }
                    }
                    break;
                }
                t = t.Next;
            }

            // Update the start position of every tag after this one
            tag = tag.Next;

            while (tag != null)
            {
                tag.Start += len;
                tag        = tag.Next;
            }

            // Make sure we have room in the widths array
            Grow(len);

            // This line needs to be recalculated
            recalc = true;
        }
예제 #5
0
        /// <summary> Find the tag on a line based on the character position, pos is 0-based</summary>
        internal LineTag FindTag(int pos)
        {
            LineTag tag;

            if (pos == 0)
            {
                return(tags);
            }

            tag = this.tags;

            if (pos >= text.Length)
            {
                pos = text.Length - 1;
            }

            while (tag != null)
            {
                if (((tag.Start - 1) <= pos) && (pos <= (tag.Start + tag.Length - 1)))
                {
                    return(LineTag.GetFinalTag(tag));
                }

                tag = tag.Next;
            }

            return(null);
        }
예제 #6
0
        internal Line(Document document, int LineNo, string Text, HorizontalAlignment align, Font font, Color color,
                      Color back_color, TextPositioning text_position, float char_offset, float left_indent, float hanging_indent,
                      float right_indent, float spacing_before, float spacing_after, float line_spacing, bool line_spacing_multiple,
                      TabStopCollection tab_stops, bool visible, LineEnding ending) : this(document, ending)
        {
            space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length + 1 : DEFAULT_TEXT_LEN;

            text                       = new StringBuilder(Text, space);
            line_no                    = LineNo;
            this.ending                = ending;
            alignment                  = align;
            indent                     = left_indent;
            HangingIndent              = hanging_indent;
            this.right_indent          = right_indent;
            this.spacing_before        = spacing_before;
            this.spacing_after         = spacing_after;
            this.tab_stops             = tab_stops;
            this.line_spacing          = line_spacing;
            this.line_spacing_multiple = line_spacing_multiple;

            widths = new float[space + 1];


            tags              = new LineTag(this, 1);
            tags.Font         = font;
            tags.Color        = color;
            tags.BackColor    = back_color;
            tags.TextPosition = text_position;
            tags.CharOffset   = char_offset;
            tags.Visible      = visible;
        }
예제 #7
0
        // Get the tag that contains this x coordinate
        public LineTag GetTag(int x)
        {
            LineTag tag = tags;

            // Coord is to the left of the first character
            if (x < tag.X)
            {
                return(LineTag.GetFinalTag(tag));
            }

            // All we have is a linked-list of tags, so we have
            // to do a linear search.  But there shouldn't be
            // too many tags per line in general.
            while (true)
            {
                if (x >= tag.X && x < (tag.X + tag.Width))
                {
                    return(tag);
                }

                if (tag.Next != null)
                {
                    tag = tag.Next;
                }
                else
                {
                    return(LineTag.GetFinalTag(tag));
                }
            }
        }
예제 #8
0
 private static void SetFormat(LineTag tag, Font font, Color color, Color back_color, TextPositioning text_position,
                               float char_offset, bool visible, FormatSpecified specified)
 {
     if ((FormatSpecified.Font & specified) == FormatSpecified.Font)
     {
         tag.Font = font;
     }
     if ((FormatSpecified.Color & specified) == FormatSpecified.Color)
     {
         tag.color = color;
     }
     if ((FormatSpecified.BackColor & specified) == FormatSpecified.BackColor)
     {
         tag.back_color = back_color;
     }
     if ((FormatSpecified.TextPosition & specified) == FormatSpecified.TextPosition)
     {
         tag.TextPosition = text_position;
     }
     if ((FormatSpecified.CharOffset & specified) == FormatSpecified.CharOffset)
     {
         tag.CharOffset = char_offset;
     }
     if ((FormatSpecified.Visibility & specified) == FormatSpecified.Visibility)
     {
         tag.Visible = visible;
     }
     // Console.WriteLine ("setting format:   {0}  {1}   new color {2}", color.Color, specified, tag.color.Color);
 }
예제 #9
0
        /// <summary>Finds the tag that describes the character at position 'pos' (0 based) on 'line'</summary>
        public static LineTag FindTag(Line line, int pos)
        {
            LineTag tag = line.tags;

            // Beginning of line is a bit special
            if (pos == 0)
            {
                return(tag);                    // Not sure if we should get the final tag here
            }
            while (tag != null)
            {
                // [H  e][l][l  o  _  W][o  r]  Text
                // [1  2][3][4  5  6  7][8  9]  Start
                //     3  4           8     10  End
                // 0 1  2  3  4  5  6  7  8  9   Pos
                if ((tag.start <= pos) && (pos < tag.End))
                {
                    return(GetFinalTag(tag));
                }

                tag = tag.next;
            }

            return(null);
        }
예제 #10
0
 public void CopyFormattingFrom(LineTag other)
 {
     Font         = other.font;
     color        = other.color;
     back_color   = other.back_color;
     TextPosition = other.text_position;
     CharOffset   = other.CharOffset;
     Visible      = other.Visible;
 }
예제 #11
0
        /// <summary>
        ///  Clears all link properties from tags
        /// </summary>
        internal void ClearLinks()
        {
            LineTag tag = tags;

            while (tag != null)
            {
                tag.IsLink = false;
                tag        = tag.Next;
            }
        }
예제 #12
0
        internal void Streamline(int lines)
        {
            LineTag current;
            LineTag next;

            current = this.tags;
            next    = current.Next;

            //
            // Catch what the loop below wont; eliminate 0 length
            // tags, but only if there are other tags after us
            // We only eliminate text tags if there is another text tag
            // after it.  Otherwise we wind up trying to type on picture tags
            //
            while ((current.Length == 0) && (next != null) && (next.IsTextTag))
            {
                tags          = next;
                tags.Previous = null;
                current       = next;
                next          = current.Next;
            }


            if (next == null)
            {
                return;
            }

            while (next != null)
            {
                // Take out 0 length tags unless it's the last tag in the document
                if (current.IsTextTag && next.Length == 0 && next.IsTextTag)
                {
                    if ((next.Next != null) || (line_no != lines))
                    {
                        current.Next = next.Next;
                        if (current.Next != null)
                        {
                            current.Next.Previous = current;
                        }
                        next = current.Next;
                        continue;
                    }
                }

                if (current.Combine(next))
                {
                    next = current.Next;
                    continue;
                }

                current = current.Next;
                next    = current.Next;
            }
        }
예제 #13
0
        internal Line(Document document, int LineNo, string Text, LineTag tag, LineEnding ending) : this(document, ending)
        {
            space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length + 1 : DEFAULT_TEXT_LEN;

            text        = new StringBuilder(Text, space);
            this.ending = ending;
            line_no     = LineNo;

            widths = new float[space + 1];
            tags   = tag;
        }
예제 #14
0
        // There can be multiple tags at the same position, we want to make
        // sure we are using the very last tag at the given position
        // Empty tags are necessary if style is set at a position with
        // no length.
        public static LineTag GetFinalTag(LineTag tag)
        {
            LineTag res = tag;

            while (res.Length == 0 && res.next != null && res.next.Length == 0)
            {
                res = res.next;
            }

            return(res);
        }
예제 #15
0
        internal Line(Document document, int LineNo, string Text, Font font, Color color, LineEnding ending) : this(document, ending)
        {
            space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length + 1 : DEFAULT_TEXT_LEN;

            text        = new StringBuilder(Text, space);
            line_no     = LineNo;
            this.ending = ending;

            widths = new float[space + 1];


            tags       = new LineTag(this, 1);
            tags.Font  = font;
            tags.Color = color;
        }
예제 #16
0
 private static void SetFormat(LineTag tag, Font font, Color_ color, Color_ back_color, FormatSpecified specified)
 {
     if ((FormatSpecified.Font & specified) == FormatSpecified.Font)
     {
         tag.Font = font;
     }
     if ((FormatSpecified.Color & specified) == FormatSpecified.Color)
     {
         tag.color = color;
     }
     if ((FormatSpecified.BackColor & specified) == FormatSpecified.BackColor)
     {
         tag.back_color = back_color;
     }
     // Console.WriteLine ("setting format:   {0}  {1}   new Color_ {2}", color.Color, specified, tag.color.Color);
 }
예제 #17
0
        /// <summary>Combines 'this' tag with 'other' tag</summary>
        public bool Combine(LineTag other)
        {
            if (!this.Equals(other))
            {
                return(false);
            }

            this.next = other.next;

            if (this.next != null)
            {
                this.next.previous = this;
            }

            return(true);
        }
예제 #18
0
        internal Line(Document document, int LineNo, string Text, HorizontalAlignment align, Font font, Color color, LineEnding ending) : this(document, ending)
        {
            space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length + 1 : DEFAULT_TEXT_LEN;

            text        = new StringBuilder(Text, space);
            line_no     = LineNo;
            this.ending = ending;
            alignment   = align;
            tab_stops   = new TabStopCollection();

            widths = new float[space + 1];


            tags       = new LineTag(this, 1);
            tags.Font  = font;
            tags.Color = color;
        }
예제 #19
0
        /// <summary>
        ///  Builds a simple code to record which tags are links and how many tags
        ///  used to compare lines before and after to see if the scan for links
        ///  process has changed anything.
        /// </summary>
        internal void LinkRecord(StringBuilder linkRecord)
        {
            LineTag tag = tags;

            while (tag != null)
            {
                if (tag.IsLink)
                {
                    linkRecord.Append("L");
                }
                else
                {
                    linkRecord.Append("N");
                }

                tag = tag.Next;
            }
        }
예제 #20
0
        // Inserts a string at the given position
        public void InsertString(int pos, string s, LineTag tag)
        {
            int len = s.Length;

            // Insert the text into the StringBuilder
            text.Insert(pos, s);

            // Update the start position of every tag after this one
            tag = tag.Next;

            while (tag != null)
            {
                tag.Start += len;
                tag        = tag.Next;
            }

            // Make sure we have room in the widths array
            Grow(len);

            // This line needs to be recalculated
            recalc = true;
        }
예제 #21
0
		/// <summary>Combines 'this' tag with 'other' tag</summary>
		public bool Combine (LineTag other)
		{
			if (!this.Equals (other))
				return false;

			this.next = other.next;
			
			if (this.next != null)
				this.next.previous = this;

			return true;
		}
예제 #22
0
		private void PointToTagPos(Point pt, out LineTag tag, out int pos) {
			Point p;

			p = pt;

			if (p.X >= document.ViewPortWidth) {
				p.X = document.ViewPortWidth - 1;
			} else if (p.X < 0) {
				p.X = 0;
			}

			if (p.Y >= document.ViewPortHeight) {
				p.Y = document.ViewPortHeight - 1;
			} else if (p.Y < 0) {
				p.Y = 0;
			}

			tag = document.FindCursor(p.X + document.ViewPortX, p.Y + document.ViewPortY, out pos);
		}
예제 #23
0
		// start_pos = 1-based
		// end_pos = 1-based
		public Line Duplicate(Line start_line, int start_pos, Line end_line, int end_pos)
		{
			Line	ret;
			Line	line;
			Line	current;
			LineTag	tag;
			LineTag	current_tag;
			int	start;
			int	end;
			int	tag_start;

			line = new Line (start_line.document, start_line.ending);
			ret = line;

			for (int i = start_line.line_no; i <= end_line.line_no; i++) {
				current = document.GetLine(i);

				if (start_line.line_no == i) {
					start = start_pos;
				} else {
					start = 0;
				}

				if (end_line.line_no == i) {
					end = end_pos;
				} else {
					end = current.text.Length;
				}

				if (end_pos == 0)
					continue;

				// Text for the tag
				line.text = new StringBuilder (current.text.ToString (start, end - start));

				// Copy tags from start to start+length onto new line
				current_tag = current.FindTag (start + 1);
				while ((current_tag != null) && (current_tag.Start <= end)) {
					if ((current_tag.Start <= start) && (start < (current_tag.Start + current_tag.Length))) {
						// start tag is within this tag
						tag_start = start;
					} else {
						tag_start = current_tag.Start;
					}

					tag = new LineTag(line, tag_start - start + 1);
					tag.CopyFormattingFrom (current_tag);

					current_tag = current_tag.Next;

					// Add the new tag to the line
					if (line.tags == null) {
						line.tags = tag;
					} else {
						LineTag tail;
						tail = line.tags;

						while (tail.Next != null) {
							tail = tail.Next;
						}
						tail.Next = tag;
						tag.Previous = tail;
					}
				}

				if ((i + 1) <= end_line.line_no) {
					line.ending = current.ending;

					// Chain them (we use right/left as next/previous)
					line.right = new Line (start_line.document, start_line.ending);
					line.right.left = line;
					line = line.right;
				}
			}

			return ret;
		}
예제 #24
0
		/// <summary>Retrieve the next tag; walks line boundaries</summary>
		internal LineTag NextTag(LineTag tag) {
			Line l;

			if (tag.Next != null) {
				return tag.Next;
			}

			// Next line
			l = GetLine(tag.Line.line_no + 1);
			if (l != null) {
				return l.tags;
			}

			return null;
		}
예제 #25
0
		internal Line (Document document, int LineNo, string Text, LineTag tag, LineEnding ending) : this(document, ending)
		{
			space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;

			text = new StringBuilder (Text, space);
			this.ending = ending;
			line_no = LineNo;

			widths = new float[space + 1];
			tags = tag;
		}
예제 #26
0
        /// <summary>
        /// Go through all tags on a line and recalculate all size-related values;
        /// returns true if lineheight changed
        /// </summary>
        internal bool RecalculateLine(Graphics g, Document doc)
        {
            LineTag tag;
            int     pos;
            int     len;
            Font    currentFont;
            int     currentFontStart;
            SizeF   size;
            float   w;
            int     prev_offset;
            bool    retval;
            bool    wrapped;
            bool    first_in_para;
            Line    line;
            int     wrap_pos;
            int     prev_wrap_pos;
            int     prev_height;
            int     prev_ascent;
            float   prev_spacing_before;
            int     max_above_baseline;
            int     max_below_baseline;
            int     total_ascent;
            int     total_descent;
            TabStop lastTab;
            int     lastTabPos;
            char    c;
            bool    handleKerning;
            float   right_indent;

            pos                 = 0;
            len                 = this.text.Length;
            currentFont         = tags.FontToDisplay;
            currentFontStart    = 0;
            tag                 = this.tags;
            prev_offset         = this.offset;          // For drawing optimization calculations
            prev_height         = this.height;
            prev_ascent         = this.ascent;
            prev_spacing_before = this.SpacingBefore;
            max_above_baseline  = 0;
            max_below_baseline  = 0;
            total_ascent        = 0;
            total_descent       = 0;
            lastTab             = null;
            lastTabPos          = 0;
            this.height         = 0;                              // Reset line height
            this.ascent         = 0;                              // Reset the ascent for the line
            tag.Shift           = 0;                              // Reset shift (which should be stored as pixels, not as points)
            right_indent        = Math.Max(this.right_indent, 0); // Ignore any negative right indent.

            if (line_no > 0)
            {
                line          = doc.GetLine(LineNo - 1);
                first_in_para = line != null && line.ending != LineEnding.Wrap;
            }
            else
            {
                first_in_para = true;
            }

            if (first_in_para)
            {
                widths [0] = indent;
            }
            else
            {
                widths [0] = indent + hanging_indent;
            }

            if (widths [0] < 0)
            {
                widths [0] = 0;                 // Don't allow a negative indent to take the line to a negative position.
            }
            widths [0] += document.left_margin;

            this.recalc = false;
            retval      = false;
            wrapped     = false;

            wrap_pos      = 0;
            prev_wrap_pos = 0;

            handleKerning = kerning_fonts.ContainsKey(currentFont.GetHashCode());

            while (pos < len)
            {
                while (tag.Length == 0)                         // We should always have tags after a tag.length==0 unless len==0
                //tag.Ascent = 0;
                {
                    tag.Shift = (tag.Line.ascent - tag.Ascent);                     // / 72;
                    tag       = tag.Next;
                    if (tag.Length != 0 && tag.FontToDisplay != currentFont)
                    {
                        CheckKerning(g, currentFont, currentFontStart, pos - currentFontStart);
                        currentFont      = tag.FontToDisplay;
                        currentFontStart = pos;
                        handleKerning    = kerning_fonts.ContainsKey(currentFont.GetHashCode());
                    }
                }

                c = text [pos];

                // kerning is a problem.  The original code in this method assumed that the
                // width of a string equals the sum of the widths of its characters.  This is
                // not true when kerning takes place during the display process.  Since it's
                // impossible to find out easily whether a font does kerning, and with which
                // characters, we just detect that kerning must have happened and use a slower
                // (but accurate) measurement for those fonts henceforth.  Without handling
                // kerning, many fonts for English become unreadable during typing for many
                // input strings, and text in many other languages is even worse trying to
                // type in TextBoxes.
                // See https://bugzilla.xamarin.com/show_bug.cgi?id=26478 for details.
                float newWidth;
                if (handleKerning && !Char.IsWhiteSpace(text[pos]))
                {
                    // MeasureText doesn't measure trailing spaces, so we do the best we can for those
                    // in the else branch.
                    // It doesn't measure /t characters either, we need to add it manually with add_width.
                    size     = TextBoxTextRenderer.MeasureText(g, text.ToString(currentFontStart, pos + 1 - currentFontStart), currentFont);
                    newWidth = widths [currentFontStart] + size.Width;
                }
                else if (c != '\t')
                {
                    size     = tag.SizeOfPosition(g, pos);
                    w        = size.Width;
                    newWidth = widths[pos] + w;
                }
                else
                {
                    CheckKerning(g, currentFont, currentFontStart, pos - currentFontStart);
                    currentFontStart = pos + 1;                     // Don't try handling the tab along with kerned text.

                    if (lastTab != null)
                    {
                        ProcessLastTab(lastTab, lastTabPos, pos);
                        lastTab = null;
                    }

                    float l = widths [pos];
                    w = -1;
                    for (int i = 0; i < tab_stops.Count; i++)
                    {
                        if (tab_stops [i].Position > l)
                        {
                            lastTab    = tab_stops [i];
                            lastTabPos = pos;
                            w          = lastTab.GetInitialWidth(this, pos);
                            break;
                        }
                    }

                    if (w < 0)
                    {
                        w = tag.SizeOfPosition(g, pos).Width;
                    }

                    newWidth = widths [pos] + w;
                }

                if (doc.Wrap)
                {
                    // FIXME: Technically there are multiple no-break spaces, not just the main one.
                    if ((Char.IsWhiteSpace(c) && c != '\u00A0') || c == '-' || c == '\u2013' || c == '\u2014')
                    {
                        // Primarily break on dashes or whitespace other than a no-break space.
                        prev_wrap_pos = wrap_pos;
                        if (c == '\t')
                        {
                            wrap_pos = pos;                             // Wrap before tabs for some reason.
                        }
                        else
                        {
                            wrap_pos = pos + 1;
                        }
                    }

                    if (newWidth > (doc.viewport_width - this.right_indent))
                    {
                        LineTag split_tag = null;
                        if (wrap_pos > 0)
                        {
                            // Make sure to set the last width of the line before wrapping
                            widths [pos + 1] = newWidth;

                            if (Char.IsWhiteSpace(c))
                            {
                                if (wrap_pos > pos)
                                {
                                    while (wrap_pos < text.Length && Char.IsWhiteSpace(text [wrap_pos]) && text [wrap_pos] != '\t')
                                    {
                                        wrap_pos++;
                                    }
                                    pos++;
                                    wrapped = true;
                                    // don't try pulling more into this line, but keep looping to deal with the rest of the widths and tags
                                }
                            }
                            else
                            {
                                if (wrap_pos > pos && pos > 0)
                                {
                                    // We're at a dash (otherwise we'd be above), but don't have room to fit it in.
                                    // Wrap at the previous wrap point if possible.
                                    wrap_pos = prev_wrap_pos > 0 ? prev_wrap_pos : pos;
                                }
                                split_tag = tag;
                                pos       = wrap_pos;
                            }
                        }
                        else if (pos > 0)
                        {
                            // No suitable wrap position was found so break right in the middle of a word

                            // Make sure to set the last width of the line before wrapping
                            widths [pos + 1] = newWidth;

                            split_tag = tag;
                        }                         // Else don't wrap -- pos == 0, so we'd infinite loop adding blank lines before this.

                        if (split_tag != null)
                        {
                            if (lastTab != null)
                            {
                                ProcessLastTab(lastTab, lastTabPos, pos);
                                lastTab = null;
                            }

                            while (pos < split_tag.Start)
                            {
                                split_tag = split_tag.Previous;
                            }
                            // We have to pass Split the correct tag, and that can change if pos
                            // is set somewhere before the tag change (e.g. by wrap_pos).

                            doc.Split(this, split_tag, pos);
                            ending = LineEnding.Wrap;
                            len    = this.text.Length;

                            retval  = true;
                            wrapped = true;
                        }
                    }
                }

                // Contract all wrapped lines that follow back into our line
                if (!wrapped)
                {
                    pos++;

                    widths[pos] = newWidth;

                    if (pos == len)
                    {
                        line = doc.GetLine(this.line_no + 1);
                        do
                        {
                            if ((line != null) && (ending == LineEnding.Wrap || ending == LineEnding.None) &&
                                (widths[pos] < (doc.viewport_width - this.right_indent) || line.text.Length == 0))
                            {
                                // Pull the two lines together
                                // Only do this if the line isn't already full, or the next line is empty.
                                var h = this.height;                             // Back up h, because Combine sets it to zero.
                                doc.Combine(this, line);
                                this.height = h;                                 // And restore it. There's no point starting at the start again.
                                // Document.Combine() called Line.Streamline(), so it is possible tag points a tag that got removed.
                                tag    = FindTag(pos - 1);                       // So make sure we've got the correct tag.
                                len    = this.text.Length;
                                line   = doc.GetLine(this.line_no + 1);
                                retval = true;
                            }
                        } while ((ending == LineEnding.Wrap || ending == LineEnding.None) && line != null && line.text.Length == 0);
                        // If the next line is empty, do it again (if possible).
                        // The amount of room on this line doesn't matter when there's no text being added...
                    }
                }

                if (pos == (tag.Start - 1 + tag.Length))
                {
                    // We just found the end of our current tag
                    tag.Height = tag.MaxHeight();

                    /* line.ascent is the highest point above the baseline.
                     * total_ascent will equal the maximum distance of the tag above the baseline.
                     * total_descent is needed to calculate the line height.
                     * tag.Shift does not include tag.CharOffset, because Shift puts the tag
                     * on the baseline, while CharOffset moves the baseline.
                     * However, we move the normal baseline when CharOffset is trying to push
                     * stuff off the top.
                     */
                    total_ascent  = tag.Ascent + (int)tag.CharOffset;
                    total_descent = tag.Descent - (int)tag.CharOffset;                     // gets bigger as CharOffset gets smaller
                    if (total_ascent > max_above_baseline)
                    {
                        int moveBy = total_ascent - max_above_baseline;
                        max_above_baseline = total_ascent;

                        LineTag t = tags;
                        while (t != null && t != tag)
                        {
                            t.Shift += moveBy;
                            t        = t.Next;
                        }

                        tag.Shift   = (int)tag.CharOffset;
                        this.ascent = max_above_baseline;
                    }
                    else
                    {
                        tag.Shift = (this.ascent - tag.Ascent);
                    }

                    if (total_descent > max_below_baseline)
                    {
                        max_below_baseline = total_descent;
                    }

                    if (this.height < max_above_baseline + max_below_baseline + tag.Height - tag.Ascent - tag.Descent)
                    {
                        this.height = max_above_baseline + max_below_baseline + tag.Height - tag.Ascent - tag.Descent;
                    }

                    tag = tag.Next;
                    if (tag != null)
                    {
                        if (tag.Length != 0 && tag.FontToDisplay != currentFont)
                        {
                            CheckKerning(g, currentFont, currentFontStart, pos - currentFontStart);
                            currentFont      = tag.FontToDisplay;
                            currentFontStart = pos;
                            handleKerning    = kerning_fonts.ContainsKey(currentFont.GetHashCode());
                        }
                        tag.Shift = 0;
                        // We can't just wrap on tag boundaries -- e.g. if the first letter of the word has a different colour / font.
                    }
                }
            }

            if (pos != currentFontStart)
            {
                CheckKerning(g, currentFont, currentFontStart, pos - currentFontStart);
            }

            if (lastTab != null)
            {
                ProcessLastTab(lastTab, lastTabPos, pos);
                lastTab = null;
            }

            while (tag != null)
            {
                tag.Shift = (tag.Line.ascent - tag.Ascent);                 // / 72;
                tag       = tag.Next;
            }

            if (this.height == 0)
            {
                this.height = tags.Font.Height;
                tags.Height = this.height;
                tags.Shift  = 0;
            }

            this.textHeight = this.height;
            this.height     = (int)(this.LineSpacing + this.TotalParagraphSpacing);

            if (prev_offset != offset || prev_height != this.height || prev_ascent != this.ascent ||
                Math.Abs(prev_spacing_before - this.SpacingBefore) > document.Dpi / 1440f)
            {
                retval = true;
            }

            return(retval);
        }
예제 #27
0
        public void DeleteCharacters(int pos, int count)
        {
            LineTag tag;
            bool    streamline = false;

            // Can't delete more than the line has
            if (pos >= text.Length)
            {
                return;
            }

            // Find the first tag that we are deleting from
            tag = FindTag(pos + 1);

            // Remove the characters from the line
            text.Remove(pos, count);

            if (tag == null)
            {
                return;
            }

            // Check if we're crossing tag boundaries
            if ((pos + count) > (tag.Start + tag.Length - 1))
            {
                int left;

                // We have to delete cross tag boundaries
                streamline = true;
                left       = count;

                left -= tag.Start + tag.Length - pos - 1;
                tag   = tag.Next;

                // Update the start of each tag
                while ((tag != null) && (left > 0))
                {
                    // Cache tag.Length as is will be indireclty modified
                    // by changes to tag.Start
                    int tag_length = tag.Length;
                    tag.Start -= count - left;

                    if (tag_length > left)
                    {
                        left = 0;
                    }
                    else
                    {
                        left -= tag_length;
                        tag   = tag.Next;
                    }
                }
            }
            else
            {
                // We got off easy, same tag

                if (tag.Length == 0)
                {
                    streamline = true;
                }
            }

            // Delete empty orphaned tags at the end
            LineTag walk = tag;

            while (walk != null && walk.Next != null && walk.Next.Length == 0)
            {
                LineTag t = walk;
                walk.Next = walk.Next.Next;
                if (walk.Next != null)
                {
                    walk.Next.Previous = t;
                }
                walk = walk.Next;
            }

            // Adjust the start point of any tags following
            if (tag != null)
            {
                tag = tag.Next;
                while (tag != null)
                {
                    tag.Start -= count;
                    tag        = tag.Next;
                }
            }

            recalc = true;

            if (streamline)
            {
                Streamline(document.Lines);
            }
        }
예제 #28
0
			// This is for future use, right now Document.Split does it by hand, with some added shortcut logic
			public void Split(Line move_to_line, int split_at) {
				line = move_to_line;
				pos -= split_at;
				tag = LineTag.FindTag(line, pos);
			}
예제 #29
0
			public void Combine(Line move_to_line, int move_to_line_length) {
				line = move_to_line;
				pos += move_to_line_length;
				tag = LineTag.FindTag(line, pos);
			}
예제 #30
0
		internal Line (Document document, int LineNo, string Text, HorizontalAlignment align, Font font, Color color, LineEnding ending) : this(document, ending)
		{
			space = Text.Length > DEFAULT_TEXT_LEN ? Text.Length+1 : DEFAULT_TEXT_LEN;

			text = new StringBuilder (Text, space);
			line_no = LineNo;
			this.ending = ending;
			alignment = align;

			widths = new float[space + 1];

			
			tags = new LineTag(this, 1);
			tags.Font = font;
			tags.Color = color;
		}
예제 #31
0
 private static void SetFormat(LineTag tag, Font font, Color color, Color back_color, FormatSpecified specified)
 {
     SetFormat(tag, font, color, back_color, TextPositioning.Normal, 0, true, specified);
 }
예제 #32
0
		// There can be multiple tags at the same position, we want to make
		// sure we are using the very last tag at the given position
		// Empty tags are necessary if style is set at a position with
		// no length.
		public static LineTag GetFinalTag (LineTag tag)
		{
			LineTag res = tag;

			while (res.Length == 0 && res.next != null && res.next.Length == 0)
				res = res.next;

			return res;
		}
예제 #33
0
		public void CopyFormattingFrom (LineTag other)
		{
			Font = other.font;
			color = other.color;
			back_color = other.back_color;
		}
예제 #34
0
 public void CopyFormattingFrom(LineTag other)
 {
     Font       = other.font;
     color      = other.color;
     back_color = other.back_color;
 }
예제 #35
0
		internal void CharIndexToLineTag(int index, out Line line_out, out LineTag tag_out, out int pos) {
			Line	line;
			LineTag	tag;
			int	i;
			int	chars;
			int	start;

			chars = 0;

			for (i = 1; i <= lines; i++) {
				line = GetLine(i);

				start = chars;
				chars += line.text.Length;

				if (index <= chars) {
					// we found the line
					tag = line.tags;

					while (tag != null) {
						if (index < (start + tag.Start + tag.Length - 1)) {
							line_out = line;
							tag_out = LineTag.GetFinalTag (tag);
							pos = index - start;
							return;
						}
						if (tag.Next == null) {
							Line	next_line;

							next_line = GetLine(line.line_no + 1);

							if (next_line != null) {
								line_out = next_line;
								tag_out = LineTag.GetFinalTag (next_line.tags);
								pos = 0;
								return;
							} else {
								line_out = line;
								tag_out = LineTag.GetFinalTag (tag);
								pos = line_out.text.Length;
								return;
							}
						}
						tag = tag.Next;
					}
				}
			}

			line_out = GetLine(lines);
			tag = line_out.tags;
			while (tag.Next != null) {
				tag = tag.Next;
			}
			tag_out = tag;
			pos = line_out.text.Length;
		}
예제 #36
0
		///<summary>Break a tag into two with identical attributes; pos is 1-based; returns tag starting at &gt;pos&lt; or null if end-of-line</summary>
		public LineTag Break (int pos)
		{
			LineTag	new_tag;

#if DEBUG
			// Sanity
			if (pos < this.Start)
				throw new Exception ("Breaking at a negative point");
#endif

#if DEBUG
			if (pos > End)
				throw new Exception ("Breaking past the end of a line");
#endif

			new_tag = new LineTag(line, pos);
			new_tag.CopyFormattingFrom (this);

			new_tag.next = this.next;
			this.next = new_tag;
			new_tag.previous = this;

			if (new_tag.next != null)
				new_tag.next.previous = new_tag;

			return new_tag;
		}
예제 #37
0
		/// <summary>Retrieve the previous tag; walks line boundaries</summary>
		internal LineTag PreviousTag(LineTag tag) {
			Line l; 

			if (tag.Previous != null) {
				return tag.Previous;
			}

			// Next line 
			if (tag.Line.line_no == 1) {
				return null;
			}

			l = GetLine(tag.Line.line_no - 1);
			if (l != null) {
				LineTag t;

				t = l.tags;
				while (t.Next != null) {
					t = t.Next;
				}
				return t;
			}

			return null;
		}
예제 #38
0
		// Insert text at the given position; use formatting at insertion point for inserted text
		internal void Insert (Line line, int pos, bool update_caret, string s, LineTag tag)
		{
			int break_index;
			int base_line;
			int old_line_count;
			int count = 1;
			LineEnding ending;
			Line split_line;
			
			// Don't recalculate while we mess around
			SuspendRecalc ();
			
			base_line = line.line_no;
			old_line_count = lines;

			// Discard chars after any possible -unlikely- end of file
			int eof_index = s.IndexOf ('\0');
			if (eof_index != -1)
				s = s.Substring (0, eof_index);

			break_index = GetLineEnding (s, 0, out ending, LineEnding.Hard | LineEnding.Rich);

			// There are no line feeds in our text to be pasted
			if (break_index == s.Length) {
				line.InsertString (pos, s, tag);
			} else {
				// Add up to the first line feed to our current position
				line.InsertString (pos, s.Substring (0, break_index + LineEndingLength (ending)), tag);
				
				// Split the rest of the original line to a new line
				Split (line, pos + (break_index + LineEndingLength (ending)));
				line.ending = ending;
				break_index += LineEndingLength (ending);
				split_line = GetLine (line.line_no + 1);
				
				// Insert brand new lines for any more line feeds in the inserted string
				while (true) {
					int next_break = GetLineEnding (s, break_index, out ending, LineEnding.Hard | LineEnding.Rich);
					
					if (next_break == s.Length)
						break;
						
					string line_text = s.Substring (break_index, next_break - break_index +
							LineEndingLength (ending));

					Add (base_line + count, line_text, line.alignment, tag.Font, tag.Color, ending);

					Line last = GetLine (base_line + count);
					last.ending = ending;

					count++;
					break_index = next_break + LineEndingLength (ending);
				}

				// Add the remainder of the insert text to the split
				// part of the original line
				split_line.InsertString (0, s.Substring (break_index));
			}
			
			// Allow the document to recalculate things
			ResumeRecalc (false);

			// Update our character count
			CharCount += s.Length;

			UpdateView (line, lines - old_line_count + 1, pos);

			// Move the caret to the end of the inserted text if requested
			if (update_caret) {
				Line l = GetLine (line.line_no + lines - old_line_count);
				PositionCaret (l, l.text.Length);
				DisplayCaret ();
			}
		}
예제 #39
0
		private static void SetFormat (LineTag tag, Font font, Color color, Color back_color, FormatSpecified specified)
		{
			if ((FormatSpecified.Font & specified) == FormatSpecified.Font) {
				tag.Font = font;
			}
			if ((FormatSpecified.Color & specified) == FormatSpecified.Color)
				tag.color = color;
			if ((FormatSpecified.BackColor & specified) == FormatSpecified.BackColor) {
				tag.back_color = back_color;
			}
			// Console.WriteLine ("setting format:   {0}  {1}   new color {2}", color.Color, specified, tag.color.Color);
		}
예제 #40
0
		// Inserts a string at the given position
		public void InsertString (int pos, string s, LineTag tag)
		{
			int len = s.Length;

			// Insert the text into the StringBuilder
			text.Insert (pos, s);

			// Update the start position of every tag after this one
			tag = tag.Next;

			while (tag != null) {
				tag.Start += len;
				tag = tag.Next;
			}

			// Make sure we have room in the widths array
			Grow (len);

			// This line needs to be recalculated
			recalc = true;
		}
예제 #41
0
		///<summary>Split line at given tag and position into two lines</summary>
		///if more space becomes available on previous line
		internal void Split(Line line, LineTag tag, int pos) {
			LineTag	new_tag;
			Line	new_line;
			bool	move_caret;
			bool	move_sel_start;
			bool	move_sel_end;

			move_caret = false;
			move_sel_start = false;
			move_sel_end = false;

#if DEBUG
			SanityCheck();

			if (tag.End < pos)
				throw new Exception ("Split called with the wrong tag");
#endif

			// Adjust selection and cursors
			if (caret.line == line && caret.pos >= pos) {
				move_caret = true;
			}
			if (selection_start.line == line && selection_start.pos > pos) {
				move_sel_start = true;
			}

			if (selection_end.line == line && selection_end.pos > pos) {
				move_sel_end = true;
			}

			// cover the easy case first
			if (pos == line.text.Length) {
				Add (line.line_no + 1, String.Empty, line.alignment, tag.Font, tag.Color, line.ending);

				new_line = GetLine (line.line_no + 1);
				
				if (move_caret) {
					caret.line = new_line;
					caret.tag = new_line.tags;
					caret.pos = 0;

					if (selection_visible == false) {
						SetSelectionToCaret (true);
					}
				}

				if (move_sel_start) {
					selection_start.line = new_line;
					selection_start.pos = 0;
					selection_start.tag = new_line.tags;
				}

				if (move_sel_end) {
					selection_end.line = new_line;
					selection_end.pos = 0;
					selection_end.tag = new_line.tags;
				}

#if DEBUG
				SanityCheck ();
#endif
				return;
			}

			// We need to move the rest of the text into the new line
			Add (line.line_no + 1, line.text.ToString (pos, line.text.Length - pos), line.alignment, tag.Font, tag.Color, line.ending);

			// Now transfer our tags from this line to the next
			new_line = GetLine(line.line_no + 1);

			line.recalc = true;
			new_line.recalc = true;

			//make sure that if we are at the end of a tag, we start on the begining
			//of a new one, if one exists... Stops us creating an empty tag and
			//make the operation easier.
			if (tag.Next != null && (tag.Next.Start - 1) == pos)
				tag = tag.Next;

			if ((tag.Start - 1) == pos) {
				int	shift;

				// We can simply break the chain and move the tag into the next line

				// if the tag we are moving is the first, create an empty tag
				// for the line we are leaving behind
				if (tag == line.tags) {
					new_tag = new LineTag(line, 1);
					new_tag.CopyFormattingFrom (tag);
					line.tags = new_tag;
				}

				if (tag.Previous != null) {
					tag.Previous.Next = null;
				}
				new_line.tags = tag;
				tag.Previous = null;
				tag.Line = new_line;

				// Walk the list and correct the start location of the tags we just bumped into the next line
				shift = tag.Start - 1;

				new_tag = tag;
				while (new_tag != null) {
					new_tag.Start -= shift;
					new_tag.Line = new_line;
					new_tag = new_tag.Next;
				}
			} else {
				int	shift;

				new_tag = new LineTag (new_line, 1);			
				new_tag.Next = tag.Next;
				new_tag.CopyFormattingFrom (tag);
				new_line.tags = new_tag;
				if (new_tag.Next != null) {
					new_tag.Next.Previous = new_tag;
				}
				tag.Next = null;

				shift = pos;
				new_tag = new_tag.Next;
				while (new_tag != null) {
					new_tag.Start -= shift;
					new_tag.Line = new_line;
					new_tag = new_tag.Next;

				}
			}

			if (move_caret) {
				caret.line = new_line;
				caret.pos = caret.pos - pos;
				caret.tag = caret.line.FindTag(caret.pos);

				if (selection_visible == false) {
					SetSelectionToCaret (true);
					move_sel_start = false;
					move_sel_end = false;
				}
			}

			if (move_sel_start) {
				selection_start.line = new_line;
				selection_start.pos = selection_start.pos - pos;
				if  (selection_start.Equals(selection_end))
					selection_start.tag = new_line.FindTag(selection_start.pos);
				else
					selection_start.tag = new_line.FindTag (selection_start.pos + 1);
			}

			if (move_sel_end) {
				selection_end.line = new_line;
				selection_end.pos = selection_end.pos - pos;
				selection_end.tag = new_line.FindTag(selection_end.pos);
			}

			CharCount -= line.text.Length - pos;
			line.text.Remove(pos, line.text.Length - pos);
#if DEBUG
			SanityCheck ();
#endif
		}
예제 #42
0
			public LinkRectangle (Rectangle rect)
			{
				link_tag = null;
				link_area_rectangle = rect;
			}
예제 #43
0
		internal void Streamline (int lines)
		{
			LineTag current;
			LineTag next;

			current = this.tags;
			next = current.Next;

			//
			// Catch what the loop below wont; eliminate 0 length 
			// tags, but only if there are other tags after us
			// We only eliminate text tags if there is another text tag
			// after it.  Otherwise we wind up trying to type on picture tags
			//
			while ((current.Length == 0) && (next != null) && (next.IsTextTag)) {
				tags = next;
				tags.Previous = null;
				current = next;
				next = current.Next;
			}


			if (next == null)
				return;

			while (next != null) {
				// Take out 0 length tags unless it's the last tag in the document
				if (current.IsTextTag && next.Length == 0 && next.IsTextTag) {
					if ((next.Next != null) || (line_no != lines)) {
						current.Next = next.Next;
						if (current.Next != null) {
							current.Next.Previous = current;
						}
						next = current.Next;
						continue;
					}
				}
				
				if (current.Combine (next)) {
					next = current.Next;
					continue;
				}

				current = current.Next;
				next = current.Next;
			}
		}