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