1 // Permission is hereby granted, free of charge, to any person obtaining
\r
2 // a copy of this software and associated documentation files (the
\r
3 // "Software"), to deal in the Software without restriction, including
\r
4 // without limitation the rights to use, copy, modify, merge, publish,
\r
5 // distribute, sublicense, and/or sell copies of the Software, and to
\r
6 // permit persons to whom the Software is furnished to do so, subject to
\r
7 // the following conditions:
\r
9 // The above copyright notice and this permission notice shall be
\r
10 // included in all copies or substantial portions of the Software.
\r
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
\r
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
\r
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
\r
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
\r
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
\r
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
\r
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\r
20 // Copyright (c) 2004-2006 Novell, Inc. (http://www.novell.com)
\r
23 // Peter Bartok pbartok@novell.com
\r
28 using System.Collections;
\r
29 using System.Drawing;
\r
30 using System.Drawing.Text;
\r
33 namespace System.Windows.Forms
\r
35 internal class LineTag
\r
37 #region Local Variables
\r
39 private Font font; // System.Drawing.Font object for this tag
\r
40 private Color color; // The font color for this tag
\r
41 private Color back_color; // In 2.0 tags can have background colours.
\r
44 private int start; // start, in chars; index into Line.text
\r
47 private int height; // Height in pixels of the text this tag describes
\r
48 private int ascent; // Ascent of the font for this tag
\r
49 private int descent; // Descent of the font for this tag
\r
50 private int shift; // Shift down for this tag, to stay on baseline
\r
53 private Line line; // The line we're on
\r
54 private LineTag next; // Next tag on the same line
\r
55 private LineTag previous; // Previous tag on the same line
\r
58 #region Constructors
\r
59 public LineTag (Line line, int start)
\r
64 #endregion // Constructors
\r
66 #region Public Properties
\r
68 get { return ascent; }
\r
71 public Color BackColor {
\r
72 get { return back_color; }
\r
73 set { back_color = value; }
\r
76 public Color Color {
\r
77 get { return color; }
\r
78 set { color = value; }
\r
81 public int Descent {
\r
82 get { return descent; }
\r
86 get { return start + Length; }
\r
90 get { return font; }
\r
92 if (font != value) {
\r
94 height = font.Height;
\r
95 XplatUI.GetFontMetrics (Hwnd.bmp_g, font, out ascent, out descent);
\r
101 public int Height {
\r
102 get { return height; }
\r
103 set { height = value; }
\r
106 public virtual bool IsTextTag {
\r
107 get { return true; }
\r
110 public int Length {
\r
114 res = next.start - start;
\r
116 res = line.text.Length - (start - 1);
\r
118 return res > 0 ? res : 0;
\r
123 get { return line; }
\r
124 set { line = value; }
\r
127 public LineTag Next {
\r
128 get { return next; }
\r
129 set { next = value; }
\r
132 public LineTag Previous {
\r
133 get { return previous; }
\r
134 set { previous = value; }
\r
138 get { return shift; }
\r
139 set { shift = value; }
\r
143 get { return start; }
\r
144 set { start = value; }
\r
147 public int TextEnd {
\r
148 get { return start + TextLength; }
\r
151 public int TextLength {
\r
155 res = next.start - start;
\r
157 res = line.TextLengthWithoutEnding () - (start - 1);
\r
159 return res > 0 ? res : 0;
\r
163 public float Width {
\r
167 return line.widths [start + Length - 1] - (start != 0 ? line.widths [start - 1] : 0);
\r
175 return line.X + line.widths [start - 1];
\r
180 #region Public Methods
\r
181 ///<summary>Break a tag into two with identical attributes; pos is 1-based; returns tag starting at >pos< or null if end-of-line</summary>
\r
182 public LineTag Break (int pos)
\r
187 if (pos == this.start)
\r
189 else if (pos >= (start + Length))
\r
192 new_tag = new LineTag(line, pos);
\r
193 new_tag.CopyFormattingFrom (this);
\r
195 new_tag.next = this.next;
\r
196 this.next = new_tag;
\r
197 new_tag.previous = this;
\r
199 if (new_tag.next != null)
\r
200 new_tag.next.previous = new_tag;
\r
205 /// <summary>Combines 'this' tag with 'other' tag</summary>
\r
206 public bool Combine (LineTag other)
\r
208 if (!this.Equals (other))
\r
211 this.next = other.next;
\r
213 if (this.next != null)
\r
214 this.next.previous = this;
\r
219 public void CopyFormattingFrom (LineTag other)
\r
222 color = other.color;
\r
223 back_color = other.back_color;
\r
226 public void Delete ()
\r
228 // If we are the only tag, we can't be deleted
\r
229 if (previous == null && next == null)
\r
232 // If we are the last tag, deletion is easy
\r
233 if (next == null) {
\r
234 previous.next = null;
\r
238 // Easy cases gone, little tougher, delete ourself
\r
239 // Update links, and start
\r
240 next.previous = null;
\r
242 LineTag loop = next;
\r
244 while (loop != null) {
\r
245 loop.Start -= Length;
\r
252 public virtual void Draw (Graphics dc, Color color, float x, float y, int start, int end)
\r
254 TextBoxTextRenderer.DrawText (dc, line.text.ToString (start, end).Replace ("\r", string.Empty), font, color, x, y, false);
\r
257 public virtual void Draw (Graphics dc, Color color, float xoff, float y, int start, int end, string text)
\r
259 while (start < end) {
\r
260 int tab_index = text.IndexOf ("\t", start);
\r
261 if (tab_index == -1)
\r
264 TextBoxTextRenderer.DrawText (dc, text.Substring (start, tab_index - start).Replace ("\r", string.Empty), font, color, xoff + line.widths[start], y, false);
\r
266 // non multilines get the unknown char
\r
267 if (!line.document.multiline && tab_index != end)
\r
268 TextBoxTextRenderer.DrawText (dc, "\u0013", font, color, xoff + line.widths[tab_index], y, true);
\r
270 start = tab_index + 1;
\r
274 /// <summary>Checks if 'this' tag describes the same formatting options as 'obj'</summary>
\r
275 public override bool Equals (object obj)
\r
282 if (!(obj is LineTag))
\r
288 other = (LineTag)obj;
\r
290 if (other.IsTextTag != IsTextTag)
\r
293 if (this.font.Equals (other.font) && this.color.Equals (other.color)) // FIXME add checking for things like link or type later
\r
299 /// <summary>Finds the tag that describes the character at position 'pos' on 'line'</summary>
\r
300 public static LineTag FindTag (Line line, int pos)
\r
302 LineTag tag = line.tags;
\r
304 // Beginning of line is a bit special
\r
306 return tag; // Not sure if we should get the final tag here
\r
308 while (tag != null) {
\r
309 if ((tag.start <= pos) && (pos <= tag.End))
\r
310 return GetFinalTag (tag);
\r
318 /// <summary>Applies 'font' and 'brush' to characters starting at 'start' for 'length' chars;
\r
319 /// Removes any previous tags overlapping the same area;
\r
320 /// returns true if lineheight has changed</summary>
\r
321 /// <param name="start">1-based character position on line</param>
\r
322 public static bool FormatText (Line line, int start, int length, Font font, Color color, Color back_color, FormatSpecified specified)
\r
328 bool retval = false; // Assume line-height doesn't change
\r
331 if (((FormatSpecified.Font & specified) == FormatSpecified.Font) && font.Height != line.height)
\r
334 line.recalc = true; // This forces recalculation of the line in RecalculateDocument
\r
336 // A little sanity, not sure if it's needed, might be able to remove for speed
\r
337 if (length > line.text.Length)
\r
338 length = line.text.Length;
\r
341 end = start + length;
\r
343 // Common special case
\r
344 if ((start == 1) && (length == tag.Length)) {
\r
346 SetFormat (tag, font, color, back_color, specified);
\r
350 start_tag = FindTag (line, start);
\r
351 tag = start_tag.Break (start);
\r
353 while (tag != null && tag.End <= end) {
\r
354 SetFormat (tag, font, color, back_color, specified);
\r
358 if (tag != null && tag.End == end)
\r
361 /// Now do the last tag
\r
362 end_tag = FindTag (line, end);
\r
364 if (end_tag != null) {
\r
365 end_tag.Break (end);
\r
366 SetFormat (end_tag, font, color, back_color, specified);
\r
372 // Gets the character at the x-coordinate. Index is based from the
\r
373 // line, not the start of the tag.
\r
374 public int GetCharIndex (int x)
\r
377 int high = low + Length;
\r
382 if (x < line.widths[low])
\r
385 if (x > line.widths[line.TextLengthWithoutEnding ()])
\r
386 return line.TextWithoutEnding ().Length;
\r
388 while (low < high - 1) {
\r
389 int mid = (high + low) / 2;
\r
390 float width = line.widths[mid];
\r
398 float char_width = line.widths[high] - line.widths[low];
\r
400 if ((x - line.widths[low]) >= (char_width / 2))
\r
406 // There can be multiple tags at the same position, we want to make
\r
407 // sure we are using the very last tag at the given position
\r
408 public static LineTag GetFinalTag (LineTag tag)
\r
412 while (res.Length == 0 && res.next != null && res.next.Length == 0)
\r
418 public override int GetHashCode ()
\r
420 return base.GetHashCode ();
\r
423 internal virtual int MaxHeight ()
\r
425 return font.Height;
\r
428 private static void SetFormat (LineTag tag, Font font, Color color, Color back_color, FormatSpecified specified)
\r
430 if ((FormatSpecified.Font & specified) == FormatSpecified.Font)
\r
432 if ((FormatSpecified.Color & specified) == FormatSpecified.Color)
\r
434 if ((FormatSpecified.BackColor & specified) == FormatSpecified.BackColor) {
\r
435 tag.back_color = back_color;
\r
437 // Console.WriteLine ("setting format: {0} {1} new color {2}", color.Color, specified, tag.color.Color);
\r
440 public virtual SizeF SizeOfPosition (Graphics dc, int pos)
\r
442 if (pos >= line.TextLengthWithoutEnding () && line.document.multiline)
\r
443 return SizeF.Empty;
\r
445 string text = line.text.ToString (pos, 1);
\r
446 switch ((int) text [0]) {
\r
448 if (!line.document.multiline)
\r
450 SizeF res = TextBoxTextRenderer.MeasureText (dc, " ", font);
\r
455 return TextBoxTextRenderer.MeasureText (dc, "\u000D", font);
\r
458 return TextBoxTextRenderer.MeasureText (dc, text, font);
\r
461 public virtual string Text ()
\r
463 return line.text.ToString (start - 1, Length);
\r
466 public override string ToString ()
\r
469 return string.Format ("{0} Tag starts at index: {1}, length: {2}, text: {3}, font: {4}", GetType (), start, Length, Text (), font.ToString ());
\r
471 return string.Format ("Zero Length tag at index: {0}", start);
\r
473 #endregion // Internal Methods
\r