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