Merge pull request #1275 from ranma42/fix-lib64
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / LinkLabel.cs
1 // Permission is hereby granted, free of charge, to any person obtaining
2 // a copy of this software and associated documentation files (the
3 // "Software"), to deal in the Software without restriction, including
4 // without limitation the rights to use, copy, modify, merge, publish,
5 // distribute, sublicense, and/or sell copies of the Software, and to
6 // permit persons to whom the Software is furnished to do so, subject to
7 // the following conditions:
8 //
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
11 //
12 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19 //
20 // Copyright (c) 2004-2005 Novell, Inc.
21 //
22 // Authors:
23 //      Jordi Mas i Hernandez, jordi@ximian.com
24 //      Chris Toshok  <toshok@ximian.com>
25 //      Everaldo Canuto  <ecanuto@novell.com>
26 //
27 // Based on work by:
28 //      Daniel Carrera, dcarrera@math.toronto.edu (stubbed out)
29 //      Jaak Simm (jaaksimm@firm.ee) (stubbed out)
30 //
31
32 using System.ComponentModel;
33 using System.Collections;
34 using System.Drawing;
35 using System.Drawing.Drawing2D;
36 using System.Runtime.InteropServices;
37 using System.Windows.Forms.Theming;
38
39 namespace System.Windows.Forms
40 {
41         [DefaultEvent("LinkClicked")]
42         [ClassInterface (ClassInterfaceType.AutoDispatch)]
43         [ComVisible (true)]
44         [ToolboxItem ("System.Windows.Forms.Design.AutoSizeToolboxItem," + Consts.AssemblySystem_Design)]
45         public class LinkLabel : Label, IButtonControl
46         {
47                 /* Encapsulates a piece of text (regular or link)*/
48                 internal class Piece
49                 {
50                         public string           text;
51                         public int              start;
52                         public int              length;
53                         public LinkLabel.Link   link;   // Empty link indicates regular text
54                         public Region           region;
55
56                         public Piece (int start, int length, string text, Link link)
57                         {
58                                 this.start = start;
59                                 this.length = length;
60                                 this.text = text;
61                                 this.link = link;
62                         }
63                 }
64
65                 private Color active_link_color;
66                 private Color disabled_link_color;
67                 private Color link_color;
68                 private Color visited_color;
69                 private LinkArea link_area;
70                 private LinkBehavior link_behavior;
71                 private LinkCollection link_collection;
72                 private ArrayList links = new ArrayList();
73                 internal Link[] sorted_links;
74                 private bool link_visited;
75                 internal Piece[] pieces;
76                 private Cursor override_cursor;
77                 private DialogResult dialog_result;
78
79                 private Link active_link;
80                 private Link hovered_link;
81                 /* this is an index instead of a Link because we have
82                  * to search through sorted links for the new one */
83                 private int focused_index;
84
85                 #region Events
86                 static object LinkClickedEvent = new object ();
87
88                 public event LinkLabelLinkClickedEventHandler LinkClicked {
89                         add { Events.AddHandler (LinkClickedEvent, value); }
90                         remove { Events.RemoveHandler (LinkClickedEvent, value); }
91                 }
92
93                 [Browsable (true)]
94                 [EditorBrowsable (EditorBrowsableState.Always)]
95                 public new event EventHandler TabStopChanged {
96                         add { base.TabStopChanged += value; }
97                         remove { base.TabStopChanged -= value; }
98                 }
99                 #endregion // Events
100
101                 public LinkLabel ()
102                 {
103                         LinkArea = new LinkArea (0, -1);
104                         link_behavior = LinkBehavior.SystemDefault;
105                         link_visited = false;
106                         pieces = null;
107                         focused_index = -1;
108
109                         string_format.FormatFlags |= StringFormatFlags.NoClip;
110                         
111                         ActiveLinkColor = Color.Red;
112                         DisabledLinkColor = ThemeEngine.Current.ColorGrayText;
113                         LinkColor = Color.FromArgb (255, 0, 0, 255);
114                         VisitedLinkColor = Color.FromArgb (255, 128, 0, 128);
115                         SetStyle (ControlStyles.Selectable, false);
116                         SetStyle (ControlStyles.ResizeRedraw | 
117                                 ControlStyles.UserPaint | 
118                                 ControlStyles.AllPaintingInWmPaint |
119                                 ControlStyles.SupportsTransparentBackColor | 
120                                 ControlStyles.Opaque |
121                                 ControlStyles.OptimizedDoubleBuffer
122                                 , true);
123                         CreateLinkPieces ();
124                 }
125
126                 #region Public Properties
127
128                 public Color ActiveLinkColor {
129                         get { return active_link_color; }
130                         set {
131                                 if (active_link_color == value)
132                                         return;
133
134                                 active_link_color = value;
135                                 Invalidate ();
136                         }
137                 }
138
139                 public Color DisabledLinkColor {
140
141                         get { return disabled_link_color; }
142                         set {
143                                 if (disabled_link_color == value)
144                                         return;
145
146                                 disabled_link_color = value;
147                                 Invalidate ();
148                         }
149                 }
150
151                 public Color LinkColor {
152                         get { return link_color; }
153                         set {
154                                 if (link_color == value)
155                                         return;
156
157                                 link_color = value;
158                                 Invalidate ();
159                         }
160                 }
161
162                 public Color VisitedLinkColor {
163                         get { return visited_color;}
164                         set {
165                                 if (visited_color == value)
166                                         return;
167
168                                 visited_color = value;
169                                 Invalidate ();
170                         }
171                 }
172
173                 [Localizable (true)]
174                 [Editor ("System.Windows.Forms.Design.LinkAreaEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]                           
175                 [RefreshProperties (RefreshProperties.Repaint)]
176                 public LinkArea LinkArea {
177                         get { return link_area;}
178                         set {
179
180                                 if (value.Start <0 || value.Length < -1)
181                                         throw new ArgumentException ();
182
183                                 Links.Clear ();
184
185                                 if (!value.IsEmpty) {
186                                         Links.Add (value.Start, value.Length);
187
188                                         link_area = value;
189                                         Invalidate ();
190                                 }
191                         }
192                 }
193                                 
194                 [DefaultValue (LinkBehavior.SystemDefault)]
195                 public LinkBehavior LinkBehavior {
196
197                         get { return link_behavior;}
198                         set {
199                                 if (link_behavior == value)
200                                         return;
201
202                                 link_behavior = value;
203                                 Invalidate ();
204                         }
205                 }
206         
207                 [Browsable (false)]
208                 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
209                 public LinkLabel.LinkCollection Links {
210                         get {
211                                 if (link_collection == null)
212                                         link_collection = new LinkCollection (this);
213                                 return link_collection;
214                         }
215                 }
216
217                 [DefaultValue (false)]
218                 public bool LinkVisited {
219                         get { return link_visited;}
220                         set {
221                                 if (link_visited == value)
222                                         return;
223
224                                 link_visited = value;
225                                 Invalidate ();
226                         }
227                 }
228                 
229                 protected Cursor OverrideCursor {
230                         get {
231                                 if (override_cursor == null)
232                                         override_cursor = Cursors.Hand;
233                                 return override_cursor;
234                         }
235                         set { override_cursor = value; }
236                 }
237
238                 [RefreshProperties(RefreshProperties.Repaint)]
239                 public override string Text {
240                         get { return base.Text; }
241                         set {
242                                 if (base.Text == value)
243                                         return;
244
245                                 base.Text = value;
246                                 CreateLinkPieces ();
247                         }
248                 }
249
250                 [Browsable (false)]
251                 [EditorBrowsable (EditorBrowsableState.Never)]
252                 public new FlatStyle FlatStyle {
253                         get { return base.FlatStyle; }
254                         set {
255                                 if (base.FlatStyle == value)
256                                         return;
257
258                                 base.FlatStyle = value;
259                         }
260                 }
261
262                 [RefreshProperties (RefreshProperties.Repaint)]
263                 public new Padding Padding {
264                         get { return base.Padding; }
265                         set {
266                                 if (base.Padding == value)
267                                         return;
268
269                                 base.Padding = value;
270                                 CreateLinkPieces ();
271                         }
272                 }
273
274                 #endregion // Public Properties
275
276                 DialogResult IButtonControl.DialogResult {
277                         get { return dialog_result; }
278                         set { dialog_result = value; }
279                 }
280
281
282                 void IButtonControl.NotifyDefault (bool value)
283                 {
284                 }
285
286                 void IButtonControl.PerformClick ()
287                 {
288                 }
289
290                 #region Public Methods
291                 protected override AccessibleObject CreateAccessibilityInstance ()
292                 {
293                         return base.CreateAccessibilityInstance();
294                 }
295
296                 protected override void CreateHandle ()
297                 {
298                         base.CreateHandle ();
299                         CreateLinkPieces ();
300                 }
301
302                 protected override void OnAutoSizeChanged (EventArgs e)
303                 {
304                         base.OnAutoSizeChanged (e);
305                 }
306
307                 protected override void OnEnabledChanged (EventArgs e)
308                 {
309                         base.OnEnabledChanged (e);
310                         Invalidate ();
311                 }
312
313                 protected override void OnFontChanged (EventArgs e)
314                 {
315                         base.OnFontChanged (e);
316                         CreateLinkPieces ();
317                 }
318
319                 protected override void OnGotFocus (EventArgs e)
320                 {
321                         base.OnGotFocus (e);
322                         
323                         // And yes it can actually be null..... arghh..
324                         if (sorted_links == null)
325                                 return;
326
327                         // Set focus to the first enabled link piece
328                         if (focused_index == -1) {
329                                 if ((Control.ModifierKeys & Keys.Shift) == 0) {
330                                         for (int i = 0; i < sorted_links.Length; i ++) {
331                                                 if (sorted_links[i].Enabled) {
332                                                         focused_index = i;
333                                                         break;
334                                                 }
335                                         }
336                                 } else {
337                                         if (focused_index == -1)
338                                                 focused_index = sorted_links.Length;
339
340                                         for (int n = focused_index - 1; n >= 0; n--) {
341                                                 if (sorted_links[n].Enabled) {
342                                                         sorted_links[n].Focused = true;
343                                                         focused_index = n;
344                                                         return;
345                                                 }
346                                         }
347                                 }
348                         }
349
350                         if (focused_index != -1)
351                                 sorted_links[focused_index].Focused = true;
352                 }
353
354                 protected override void OnKeyDown (KeyEventArgs e)
355                 {
356                         if (e.KeyCode == Keys.Return) {
357                                 if (focused_index != -1)
358                                         OnLinkClicked (new LinkLabelLinkClickedEventArgs (sorted_links[focused_index]));
359                         }
360
361                         base.OnKeyDown(e);
362                 }
363
364                 protected virtual void OnLinkClicked (LinkLabelLinkClickedEventArgs e)
365                 {
366                         LinkLabelLinkClickedEventHandler eh = (LinkLabelLinkClickedEventHandler)(Events [LinkClickedEvent]);
367                         if (eh != null)
368                                 eh (this, e);
369                 }
370
371                 protected override void OnLostFocus (EventArgs e)
372                 {
373                         base.OnLostFocus (e);
374                         
375                         // Clean focus in link pieces
376                         if (focused_index != -1)
377                                 sorted_links[focused_index].Focused = false;
378                 }
379
380                 protected override void OnMouseDown (MouseEventArgs e)
381                 {
382                         if (!Enabled) return;
383
384                         base.OnMouseDown (e);
385
386                         for (int i = 0; i < sorted_links.Length; i ++) {
387                                 if (sorted_links[i].Contains (e.X, e.Y) && sorted_links[i].Enabled) {
388                                         sorted_links[i].Active = true;
389                                         if (focused_index != -1)
390                                                 sorted_links[focused_index].Focused = false;
391                                         active_link = sorted_links[i];
392                                         focused_index = i;
393                                         sorted_links[focused_index].Focused = true;
394                                         break;
395                                 }
396                         }
397                 }
398
399                 protected override void OnMouseLeave(EventArgs e)
400                 {
401                         if (!Enabled) return;
402                         base.OnMouseLeave (e);
403                         UpdateHover (null);
404                 }
405
406                 protected override void OnPaddingChanged (EventArgs e)
407                 {
408                         base.OnPaddingChanged (e);
409                 }
410
411                 private void UpdateHover (Link link)
412                 {
413                         if (link == hovered_link)
414                                 return;
415
416                         if (hovered_link != null)
417                                 hovered_link.Hovered = false;
418
419                         hovered_link = link;
420
421                         if (hovered_link != null)
422                                 hovered_link.Hovered = true;
423
424                         Cursor = (hovered_link != null) ? OverrideCursor : Cursors.Default;
425
426                         /* XXX this shouldn't be here.  the
427                          * Link.Invalidate machinery should be enough,
428                          * but it seems the piece regions don't
429                          * contain the area with the underline.  this
430                          * can be seen easily when you click on a link
431                          * and the focus rectangle shows up (it's too
432                          * far up), and also the bottom few pixels of
433                          * a linklabel aren't active when it comes to
434                          * hovering */
435                         Invalidate ();
436                 }
437
438                 protected override void OnMouseMove (MouseEventArgs e)
439                 {
440                         UpdateHover (PointInLink (e.X, e.Y));
441                         base.OnMouseMove (e);
442                 }
443
444                 protected override void OnMouseUp (MouseEventArgs e)
445                 {
446                         if (!Enabled) return;
447
448                         base.OnMouseUp (e);
449
450                         if (active_link == null)
451                                 return;
452
453                         Link clicked_link = (PointInLink (e.X, e.Y) == active_link) ? active_link : null;
454
455                         active_link.Active = false;
456                         active_link = null;
457
458                         if (clicked_link != null)
459                                 OnLinkClicked (new LinkLabelLinkClickedEventArgs (clicked_link, e.Button));
460                 }
461
462                 protected override void OnClick (EventArgs e)
463                 {
464                         if (active_link != null && this.Capture) {
465                                 this.Capture = false;
466                         }
467                         base.OnClick (e);
468                 }
469
470                 protected override void OnPaint (PaintEventArgs e)
471                 {
472                         // We need to invoke paintbackground because control is opaque
473                         // and can have transparent colors.
474                         base.InvokePaintBackground (this, e);
475                         
476                         ThemeElements.LinkLabelPainter.Draw (e.Graphics, e.ClipRectangle, this);
477                         // Do not call base.OnPaint since it's the Label class 
478                 }
479
480                 protected override void OnPaintBackground (PaintEventArgs e)
481                 {
482                         base.OnPaintBackground (e);
483                 }
484
485                 protected override void OnTextAlignChanged (EventArgs e)
486                 {
487                         CreateLinkPieces ();
488                         base.OnTextAlignChanged (e);
489                 }
490
491                 protected override void OnTextChanged (EventArgs e)
492                 {
493                         CreateLinkPieces ();
494                         base.OnTextChanged (e);
495                 }
496                 
497                 protected Link PointInLink (int x, int y)
498                 {
499                         for (int i = 0; i < sorted_links.Length; i ++)
500                                 if (sorted_links[i].Contains (x, y))
501                                         return sorted_links[i];
502
503                         return null;
504                 }
505
506                 protected override bool ProcessDialogKey (Keys keyData)
507                 {
508                         if ((keyData & Keys.KeyCode) ==  Keys.Tab) {
509                                 Select (true, (keyData & Keys.Shift) == 0);
510                                 return true;
511                         }
512                         return base.ProcessDialogKey (keyData);
513                 }
514
515                 protected override void Select (bool directed, bool forward)
516                 {
517                         if (directed) {
518                                 if (focused_index != -1) {
519                                         sorted_links[focused_index].Focused = false;
520                                         focused_index = -1;
521                                 }
522
523                                 if (forward) {
524                                         for (int n = focused_index + 1; n < sorted_links.Length; n++) {
525                                                 if (sorted_links[n].Enabled) {
526                                                         sorted_links[n].Focused = true;
527                                                         focused_index = n;
528                                                         base.Select (directed, forward);
529                                                         return;
530                                                 }
531                                         }
532                                 } else {
533                                         if (focused_index == -1)
534                                                 focused_index = sorted_links.Length;
535
536                                         for (int n = focused_index - 1; n >= 0; n--) {
537                                                 if (sorted_links[n].Enabled) {
538                                                         sorted_links[n].Focused = true;
539                                                         focused_index = n;
540                                                         base.Select (directed, forward);
541                                                         return;
542                                                 }
543                                         }
544                                 }
545
546                                 focused_index = -1;
547
548                                 if (Parent != null)
549                                         Parent.SelectNextControl (this, forward, false, true, true);
550                         }
551                 }
552
553                 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
554                 {
555                         base.SetBoundsCore (x, y, width, height, specified);
556                         CreateLinkPieces();
557                 }
558
559                 protected override void WndProc (ref Message msg)
560                 {
561                         base.WndProc (ref msg);
562                 }
563
564                 #endregion //Public Methods
565
566                 #region Private Methods
567
568                 private ArrayList CreatePiecesFromText (int start, int len, Link link)
569                 {
570                         ArrayList rv = new ArrayList ();
571
572                         if (start + len > Text.Length)
573                                 len = Text.Length - start;
574                         if (len < 0)
575                                 return rv;
576
577                         string t = Text.Substring (start, len);
578
579                         int ps = 0;
580                         for (int i = 0; i < t.Length; i ++) {
581                                 if (t[i] == '\n') {
582                                         if (i != 0) {
583                                                 Piece p = new Piece (start + ps, i + 1 - ps, t.Substring (ps, i+1-ps), link);
584                                                 rv.Add (p);
585                                         }
586                                         ps = i+1;
587                                 }
588                         }
589                         if (ps < t.Length) {
590                                 Piece p = new Piece (start + ps, t.Length - ps, t.Substring (ps, t.Length-ps), link);
591                                 rv.Add (p);
592                         }
593
594                         return rv;
595                 }
596
597                 private void CreateLinkPieces ()
598                 {
599                         if (Text.Length == 0) {
600                                 SetStyle (ControlStyles.Selectable, false);
601                                 TabStop = false;
602                                 link_area.Start = 0;
603                                 link_area.Length = 0;
604                                 return;
605                         }
606                         
607                         if (Links.Count == 1 && Links[0].Start == 0 &&  Links[0].Length == -1)
608                                 Links[0].Length = Text.Length;
609
610                         SortLinks ();
611
612                         // Set the LinkArea values based on first link.
613                         if (Links.Count > 0) {
614                                 link_area.Start = Links[0].Start;
615                                 link_area.Length = Links[0].Length;
616                         } else {
617                                 link_area.Start = 0;
618                                 link_area.Length = 0;
619                         }
620
621                         TabStop = (LinkArea.Length > 0);
622                         SetStyle (ControlStyles.Selectable, TabStop);
623
624                         /* don't bother doing the rest if our handle hasn't been created */
625                         if (!IsHandleCreated)
626                                 return;
627
628                         ArrayList pieces_list = new ArrayList ();
629
630                         int current_end = 0;
631
632                         for (int l = 0; l < sorted_links.Length; l ++) {
633                                 int new_link_start = sorted_links[l].Start;
634
635                                 if (new_link_start > current_end) {
636                                         /* create/push a piece
637                                          * containing the text between
638                                          * the previous/new link */
639                                         ArrayList text_pieces = CreatePiecesFromText (current_end, new_link_start - current_end, null);
640                                         pieces_list.AddRange (text_pieces);
641                                 }
642
643                                 /* now create a group of pieces for
644                                  * the new link (split up by \n's) */
645                                 ArrayList link_pieces = CreatePiecesFromText (new_link_start, sorted_links[l].Length, sorted_links[l]);
646                                 pieces_list.AddRange (link_pieces);
647                                 sorted_links[l].pieces.AddRange (link_pieces);
648
649                                 current_end = sorted_links[l].Start + sorted_links[l].Length;
650                         }
651                         if (current_end < Text.Length) {
652                                 ArrayList text_pieces = CreatePiecesFromText (current_end, Text.Length - current_end, null);
653                                 pieces_list.AddRange (text_pieces);
654                         }
655
656                         pieces = new Piece[pieces_list.Count];
657                         pieces_list.CopyTo (pieces, 0);
658
659                         CharacterRange[] ranges = new CharacterRange[pieces.Length];
660
661                         for(int i = 0; i < pieces.Length; i++)
662                                 ranges[i] = new CharacterRange (pieces[i].start, pieces[i].length);
663
664                         string_format.SetMeasurableCharacterRanges (ranges);
665
666                         Region[] regions = TextRenderer.MeasureCharacterRanges (Text,
667                                                                                  ThemeEngine.Current.GetLinkFont (this),
668                                                                                  PaddingClientRectangle,
669                                                                                  string_format);
670
671                         for (int i = 0; i < pieces.Length; i ++) {
672                                 pieces[i].region = regions[i];
673                                 pieces[i].region.Translate (Padding.Left, Padding.Top);
674                         }
675
676                         Invalidate ();
677                 }
678
679                 private void SortLinks ()
680                 {
681                         if (sorted_links != null)
682                                 return;
683
684                         sorted_links = new Link [Links.Count];
685                         ((ICollection)Links).CopyTo (sorted_links, 0);
686
687                         Array.Sort (sorted_links, new LinkComparer ());
688                 }
689
690                 /* Check if the links overlap */
691                 private void CheckLinks ()
692                 {
693                         SortLinks ();
694
695                         int current_end = 0;
696
697                         for (int i = 0; i < sorted_links.Length; i++) {
698                                 if (sorted_links[i].Start < current_end)
699                                         throw new InvalidOperationException ("Overlapping link regions.");
700                                 current_end = sorted_links[i].Start + sorted_links[i].Length;
701                         }
702                 }
703                 
704                 #endregion // Private Methods
705
706                 //
707                 // System.Windows.Forms.LinkLabel.Link
708                 //
709                 [TypeConverter (typeof (LinkConverter))]
710                 public class Link
711                 {
712                         private bool enabled;
713                         internal int length;
714                         private object linkData;
715                         private int start;
716                         private bool visited;
717                         private LinkLabel owner;
718                         private bool hovered;
719                         internal ArrayList pieces;
720                         private bool focused;
721                         private bool active;
722                         private string description;
723                         private string name;
724                         private object tag;
725
726                         internal Link (LinkLabel owner)
727                         {
728                                 focused = false;
729                                 enabled = true;
730                                 visited = false;
731                                 length = start = 0;
732                                 linkData = null;
733                                 this.owner = owner;
734                                 pieces = new ArrayList ();
735                                 name = string.Empty;
736                         }
737
738                         public Link ()
739                         {
740                                 this.enabled = true;
741                                 this.name = string.Empty;
742                                 this.pieces = new ArrayList ();
743                         }
744
745                         public Link (int start, int length) : this ()
746                         {
747                                 this.start = start;
748                                 this.length = length;
749                         }
750
751                         public Link (int start, int length, Object linkData) : this (start, length)
752                         {
753                                 this.linkData = linkData;
754                         }
755
756                         #region Public Properties
757                         public string Description {
758                                 get { return this.description; }
759                                 set { this.description = value; }
760                         }
761                         
762                         [DefaultValue ("")]
763                         public string Name {
764                                 get { return this.name; }
765                                 set { this.name = value; }
766                         }
767                         
768                         [Bindable (true)]
769                         [Localizable (false)]
770                         [DefaultValue (null)]
771                         [TypeConverter (typeof (StringConverter))]
772                         public Object Tag {
773                                 get { return this.tag; }
774                                 set { this.tag = value; }
775                         }
776                                 
777                         [DefaultValue (true)]
778                         public bool Enabled {
779                                 get { return enabled; }
780                                 set {
781                                         if (enabled != value)
782                                                 Invalidate ();
783
784                                         enabled = value;
785                                 }
786                         }
787
788                         public int Length {
789                                 get { 
790                                         if (length == -1) {
791                                                 return owner.Text.Length;
792                                         }
793                                         
794                                         return length; 
795                                 }
796                                 set {
797                                         if (length == value)
798                                                 return;
799                                         
800                                         length = value;
801
802                                         owner.CreateLinkPieces ();
803                                 }
804                         }
805
806                         [DefaultValue (null)]
807                         public object LinkData {
808                                 get { return linkData; }
809                                 set { linkData = value; }
810                         }
811
812                         public int Start {
813                                 get { return start; }
814                                 set {
815                                         if (start == value)
816                                                 return;
817
818                                         start = value;
819
820                                         owner.sorted_links = null;
821                                         owner.CreateLinkPieces ();
822                                 }
823                         }
824
825                         [DefaultValue (false)]
826                         public bool Visited {
827                                 get { return visited; }
828                                 set {
829                                         if (visited != value)
830                                                 Invalidate ();
831
832                                         visited = value;
833                                 }
834                         }
835                         
836                         internal bool Hovered {
837                                 get { return hovered; }
838                                 set {
839                                         if (hovered != value)
840                                                 Invalidate ();
841                                         hovered = value;
842                                 }
843                         }
844
845                         internal bool Focused {
846                                 get { return focused; }
847                                 set {
848                                         if (focused != value)
849                                                 Invalidate ();
850                                         focused = value;
851                                 }
852                         }
853
854                         internal bool Active {
855                                 get { return active; }
856                                 set {
857                                         if (active != value)
858                                                 Invalidate ();
859                                         active = value;
860                                 }
861                         }
862
863                         internal LinkLabel Owner {
864                                 set { owner = value; }
865                         }
866                         #endregion
867
868                         private void Invalidate ()
869                         {
870                                 for (int i = 0; i < pieces.Count; i ++)
871                                         owner.Invalidate (((Piece)pieces[i]).region);
872                         }
873
874                         internal bool Contains (int x, int y)
875                         {
876                                 foreach (Piece p in pieces) {
877                                         if (p.region.IsVisible (new Point (x,y)))
878                                                 return true;
879                                 }
880                                 return false;
881                         }
882                 }
883
884                 class LinkComparer : IComparer
885                 {
886                         public int Compare (object x, object y)
887                         {
888                                 Link l1 = (Link)x;
889                                 Link l2 = (Link)y;
890
891                                 return l1.Start - l2.Start;
892                         }
893                 }
894
895                 //
896                 // System.Windows.Forms.LinkLabel.LinkCollection
897                 //
898                 public class LinkCollection :  IList, ICollection, IEnumerable
899                 {
900                         private LinkLabel owner;
901                         private bool links_added;
902
903                         public LinkCollection (LinkLabel owner)
904                         {
905                                 if (owner == null)
906                                         throw new ArgumentNullException ("owner");
907
908                                 this.owner = owner;
909                         }
910
911                         [Browsable (false)]
912                         public int Count {
913                                 get { return owner.links.Count; }
914                         }
915
916                         public bool IsReadOnly {
917                                 get { return false; }
918                         }
919
920                         public virtual LinkLabel.Link this[int index] {
921                                 get {
922                                         if (index < 0 || index >= Count)
923                                                 throw  new  ArgumentOutOfRangeException();
924
925                                         return (LinkLabel.Link) owner.links[index];
926                                 }
927                                 set {
928                                         if (index < 0 || index >= Count)
929                                                 throw new  ArgumentOutOfRangeException();
930
931                                         owner.links[index] = value;
932                                 }
933                         }
934
935                         public virtual Link this[string key] {
936                                 get {
937                                         if (string.IsNullOrEmpty (key))
938                                                 return null;
939                                                 
940                                         foreach (Link l in owner.links)
941                                                 if (string.Compare (l.Name, key, true) == 0)
942                                                         return l;
943                                                         
944                                         return null;
945                                 }
946                         }
947                         
948                         public int Add (Link value)
949                         {
950                                 value.Owner = owner;
951                                 /* remove the default 0,-1 link */
952                                 if (IsDefault) {
953                                         /* don't call Clear() here to save the additional CreateLinkPieces */
954                                         owner.links.Clear ();
955                                 }
956
957                                 int idx = owner.links.Add (value);
958                                 links_added = true;
959
960                                 owner.sorted_links = null;
961                                 owner.CheckLinks ();
962                                 owner.CreateLinkPieces ();
963
964                                 return idx;
965                         }
966
967                         public Link Add (int start, int length)
968                         {
969                                 return Add (start, length, null);
970                         }
971                         
972                         internal bool IsDefault {
973                                 get {
974                                         return (Count == 1
975                                                 && this[0].Start == 0
976                                                 && this[0].length == -1);
977                                 }
978                         }
979
980                         public Link Add (int start, int length, object linkData)
981                         {
982                                 Link link = new Link (owner);
983                                 link.Length = length;
984                                 link.Start = start;
985                                 link.LinkData = linkData;
986
987                                 int idx = Add (link);
988
989                                 return (Link) owner.links[idx];
990                         }
991
992                         public virtual void Clear ()
993                         {
994                                 owner.links.Clear();
995                                 owner.sorted_links = null;
996                                 owner.CreateLinkPieces ();
997                         }
998
999                         public bool Contains (Link link)
1000                         {
1001                                 return owner.links.Contains (link);
1002                         }
1003
1004                         public virtual bool ContainsKey (string key)
1005                         {
1006                                 return !(this[key] == null);
1007                         }
1008
1009                         public IEnumerator GetEnumerator ()
1010                         {
1011                                 return owner.links.GetEnumerator ();
1012                         }
1013
1014                         public int IndexOf (Link link)
1015                         {
1016                                 return owner.links.IndexOf (link);
1017                         }
1018
1019                         public virtual int IndexOfKey (string key)
1020                         {
1021                                 if (string.IsNullOrEmpty (key))
1022                                         return -1;
1023                                         
1024                                 return IndexOf (this[key]);
1025                         }
1026                         
1027                         public bool LinksAdded {
1028                                 get { return this.links_added; }
1029                         }
1030
1031                         public void Remove (LinkLabel.Link value)
1032                         {
1033                                 owner.links.Remove (value);
1034                                 owner.sorted_links = null;
1035                                 owner.CreateLinkPieces ();
1036                         }
1037
1038                         public virtual void RemoveByKey (string key)
1039                         {       
1040                                 Remove (this[key]);
1041                         }
1042
1043                         public void RemoveAt (int index)
1044                         {
1045                                 if (index >= Count)
1046                                         throw new ArgumentOutOfRangeException ("Invalid value for array index");
1047
1048                                 owner.links.Remove (owner.links[index]);
1049                                 owner.sorted_links = null;
1050                                 owner.CreateLinkPieces ();
1051                         }
1052
1053                         bool IList.IsFixedSize {
1054                                 get {return false;}
1055                         }
1056
1057                         object IList.this[int index] {
1058                                 get { return owner.links[index]; }
1059                                 set { owner.links[index] = value; }
1060                         }
1061
1062                         object ICollection.SyncRoot {
1063                                 get {return this;}
1064                         }
1065
1066                         bool ICollection.IsSynchronized {
1067                                 get {return false;}
1068                         }
1069
1070                         void ICollection.CopyTo (Array dest, int index)
1071                         {
1072                                 owner.links.CopyTo (dest, index);
1073                         }
1074
1075                         int IList.Add (object value)
1076                         {
1077                                 int idx = owner.links.Add (value);
1078                                 owner.sorted_links = null;
1079                                 owner.CheckLinks ();
1080                                 owner.CreateLinkPieces ();
1081                                 return idx;
1082                         }
1083
1084                         bool IList.Contains (object link)
1085                         {
1086                                 return Contains ((Link) link);
1087                         }
1088
1089                         int IList.IndexOf (object link)
1090                         {
1091                                 return owner.links.IndexOf (link);
1092                         }
1093
1094                         void IList.Insert (int index, object value)
1095                         {
1096                                 owner.links.Insert (index, value);
1097                                 owner.sorted_links = null;
1098                                 owner.CheckLinks ();
1099                                 owner.CreateLinkPieces ();
1100                         }
1101
1102                         void IList.Remove (object value)
1103                         {
1104                                 Remove ((Link) value);
1105                         }
1106                 }
1107
1108                 [RefreshProperties (RefreshProperties.Repaint)]
1109                 public new bool UseCompatibleTextRendering {
1110                         get {
1111                                 return use_compatible_text_rendering;
1112                         }
1113                         set {
1114                                 use_compatible_text_rendering = value;
1115                         }
1116                 }
1117         }
1118 }