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