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