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:
9 // The above copyright notice and this permission notice shall be
10 // included in all copies or substantial portions of the Software.
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.
20 // Copyright (c) 2004 Novell, Inc.
23 // Jordi Mas i Hernandez, jordi@ximian.com
26 // Daniel Carrera, dcarrera@math.toronto.edu (stubbed out)
27 // Jaak Simm (jaaksimm@firm.ee) (stubbed out)
30 // - Change the cursor to a hand cursor when you are over a link (when cursors are available)
38 using System.ComponentModel;
39 using System.Collections;
41 using System.Drawing.Drawing2D;
43 namespace System.Windows.Forms
45 [DefaultEvent("LinkClicked")]
46 public class LinkLabel : Label, IButtonControl
48 /* Encapsulates a piece of text (regular or link)*/
54 public LinkLabel.Link link; // Empty link indicates regular text
55 public RectangleF rect;
66 private Color active_link;
67 private Color disabled_link;
68 private Color link_color;
69 private Color visited_color;
70 private LinkArea link_area;
71 private LinkBehavior link_behavior;
72 private LinkCollection link_collection;
73 private bool link_visited;
74 private Font link_font;
75 private bool link_click;
76 private Piece[] pieces;
77 private int num_pieces;
78 private Cursor override_cursor;
79 private DialogResult dialog_result;
82 public event LinkLabelLinkClickedEventHandler LinkClicked;
87 link_collection = new LinkCollection (this);
88 LinkArea = new LinkArea (0, -1);
89 link_behavior = LinkBehavior.SystemDefault;
95 ActiveLinkColor = Color.Red;
96 DisabledLinkColor = ThemeEngine.Current.ColorGrayText;
97 LinkColor = Color.FromArgb (255, 0, 0, 255);
98 VisitedLinkColor = Color.FromArgb (255, 128, 0, 128);
101 #region Public Properties
103 public Color ActiveLinkColor {
104 get { return active_link;}
106 if (active_link == value)
114 public Color DisabledLinkColor {
116 get { return disabled_link;}
118 if (disabled_link == value)
121 disabled_link = value;
126 public Color LinkColor {
127 get { return link_color;}
129 if (link_color == value)
137 public Color VisitedLinkColor {
138 get { return visited_color;}
140 if (visited_color == value)
143 visited_color = value;
149 [Editor ("System.Windows.Forms.Design.LinkAreaEditor, " + Consts.AssemblySystem_Design, typeof (System.Drawing.Design.UITypeEditor))]
150 public LinkArea LinkArea {
151 get { return link_area;}
154 if (value.Start <0 || value.Length > 0)
155 throw new ArgumentException ();
158 Links.Add (value.Start, value.Length);
165 [DefaultValue (LinkBehavior.SystemDefault)]
166 public LinkBehavior LinkBehavior {
168 get { return link_behavior;}
170 if (link_behavior == value)
173 link_behavior = value;
179 [EditorBrowsable (EditorBrowsableState.Advanced)]
180 [DesignerSerializationVisibility (DesignerSerializationVisibility.Hidden)]
181 public LinkLabel.LinkCollection Links {
182 get { return link_collection;}
185 [DefaultValue (false)]
186 public bool LinkVisited {
187 get { return link_visited;}
189 if (link_visited == value)
192 link_visited = value;
197 protected Cursor OverrideCursor {
198 get { return override_cursor;}
199 set { override_cursor = value;}
202 [RefreshProperties(RefreshProperties.Repaint)]
203 public override string Text {
204 get { return base.Text; }
206 if (base.Text == value)
214 #endregion // Public Properties
216 DialogResult IButtonControl.DialogResult {
217 get { return dialog_result; }
218 set { dialog_result = value; }
222 void IButtonControl.NotifyDefault (bool value)
227 void IButtonControl.PerformClick ()
229 throw new NotImplementedException ();
232 #region Public Methods
233 protected override AccessibleObject CreateAccessibilityInstance ()
235 return base.CreateAccessibilityInstance();
238 protected override void CreateHandle ()
240 base.CreateHandle ();
245 protected override void OnEnabledChanged (EventArgs e)
247 base.OnEnabledChanged (e);
251 protected override void OnFontChanged (EventArgs e)
253 base.OnFontChanged (e);
258 protected override void OnGotFocus (EventArgs e)
263 protected override void OnKeyDown (KeyEventArgs e)
268 protected virtual void OnLinkClicked (LinkLabelLinkClickedEventArgs e)
270 if (LinkClicked != null)
271 LinkClicked (this, e);
274 protected override void OnLostFocus (EventArgs e)
276 base.OnLostFocus (e);
279 protected override void OnMouseDown (MouseEventArgs e)
281 if (!Enabled) return;
283 base.OnMouseDown (e);
286 for (int i = 0; i < num_pieces; i++) {
287 if (pieces[i].rect.Contains (e.X, e.Y)) {
288 if (pieces[i].link!= null) {
289 pieces[i].clicked = true;
297 protected override void OnMouseLeave(EventArgs e)
299 if (!Enabled) return;
301 base.OnMouseLeave (e);
304 protected override void OnMouseMove (MouseEventArgs e)
306 base.OnMouseMove (e);
309 protected override void OnMouseUp (MouseEventArgs e)
311 if (!Enabled) return;
314 this.Capture = false;
316 for (int i = 0; i < num_pieces; i++) {
317 if (pieces[i].link!= null && pieces[i].clicked == true) {
319 if (LinkClicked != null)
320 LinkClicked (this, new LinkLabelLinkClickedEventArgs (pieces[i].link));
322 pieces[i].clicked = false;
328 protected override void OnPaint (PaintEventArgs pevent)
330 if (Width <= 0 || Height <= 0 || Visible == false)
334 pevent.Graphics.DrawImage (ImageBuffer, 0, 0);
337 protected override void OnPaintBackground (PaintEventArgs e)
339 base.OnPaintBackground (e);
342 protected override void OnTextAlignChanged (EventArgs e)
344 base.OnTextAlignChanged (e);
348 protected override void OnTextChanged (EventArgs e)
350 base.OnTextChanged (e);
354 protected Link PointInLink (int x, int y)
356 for (int i = 0; i < num_pieces; i++) {
357 if (pieces[i].rect.Contains (x,y) && pieces[i].link != null)
358 return pieces[i].link;
364 protected override bool ProcessDialogKey (Keys keyData)
366 return base.ProcessDialogKey (keyData);
369 protected override void Select (bool directed, bool forward)
371 base.Select (directed, forward);
374 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
376 base.SetBoundsCore (x, y, width, height, specified);
379 protected override void WndProc (ref Message m)
381 base.WndProc (ref m);
384 #endregion //Public Methods
386 #region Private Methods
388 internal void CreateLinkPieces ()
390 if (Links.Count == 0 || IsHandleCreated == false || Text.Length == 0)
396 if (Links.Count == 1 && Links[0].Start == 0 && Links[0].Length == -1) {
398 pieces = new Piece [num_pieces];
399 pieces[cur_piece] = new Piece();
400 pieces[cur_piece].start = 0;
401 pieces[cur_piece].end = Text.Length;
402 pieces[cur_piece].link = Links[0];
403 pieces[cur_piece].text = Text;
404 pieces[cur_piece].rect = ClientRectangle;
408 pieces = new Piece [(Links.Count * 2) + 1];
409 pieces[cur_piece] = new Piece();
410 pieces[cur_piece].start = 0;
412 for (int i = 0; i < Text.Length; i++) { /* Every char on the text*/
413 for (int l = 0; l < Links.Count; l++) { /* Every link that we know of*/
414 if (Links[l].Start == i) {
416 /*Push prev. regular text*/
417 pieces[cur_piece].end = i;
418 pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start,
419 pieces[cur_piece].end - pieces[cur_piece].start);
424 pieces[cur_piece] = new Piece ();
429 if (Links[l].Start + Links[l].Length > Text.Length) {
430 end = Text.Length - Links[l].Start;
433 end = Links[l].Length;
436 Console.WriteLine ("Punt 2: {0} ", end);
438 pieces[cur_piece].start = Links[l].Start;
439 pieces[cur_piece].end = Links[l].Start + end;
440 pieces[cur_piece].link = Links[l];
442 Console.WriteLine ("Punt 2: start:{0} end:{1} text:{2} len {3} ", pieces[cur_piece].start, end, Text,
445 pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start, end);
447 cur_piece++; /* Push link*/
448 pieces[cur_piece] = new Piece();
450 pieces[cur_piece].start = i;
455 if (pieces[cur_piece].end == 0 && pieces[cur_piece].start <= Text.Length) {
456 pieces[cur_piece].end = Text.Length;
457 pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start, pieces[cur_piece].end - pieces[cur_piece].start);
461 num_pieces = cur_piece;
463 CharacterRange[] charRanges = new CharacterRange [num_pieces];
465 for (int i = 0; i < num_pieces; i++)
466 charRanges[i] = new CharacterRange (pieces[i].start, pieces[i].end - pieces[i].start);
468 Region[] charRegions = new Region [num_pieces];
469 string_format.SetMeasurableCharacterRanges (charRanges);
471 charRegions = DeviceContext.MeasureCharacterRanges (Text, Font, ClientRectangle, string_format);
473 for (int i = 0; i < num_pieces; i++) {
474 //RectangleF[] f = charRegions[i].GetRegionScans (new Matrix());
475 pieces[i].rect = charRegions[i].GetBounds (DeviceContext);
476 Console.WriteLine (pieces[i].rect);
479 if (Visible && IsHandleCreated)
484 /* Check if the links overlap */
485 internal void CheckLinks ()
487 for (int i = 0; i < Links.Count; i++) {
488 for (int l = 0; l < Links.Count; l++) {
491 if (((Links[i].Start + Links[i].Length) >= Links[l].Start &&
492 Links[i].Start + Links[i].Length <= Links[l].Start + Links[l].Length) ||
493 (Links[i].Start >= Links[l].Start &&
494 Links[i].Start <= Links[l].Start + Links[l].Length))
495 throw new InvalidOperationException ("Overlapping link regions.");
500 private Color GetLinkColor (Piece piece, int i)
504 if (Enabled == false ||
505 (piece.link != null && piece.link.Enabled == false))
506 color = DisabledLinkColor;
508 if (piece.clicked == true)
509 color = ActiveLinkColor;
511 if ((LinkVisited == true && i == 0) ||
512 (piece.link != null && piece.link.Visited == true))
513 color = VisitedLinkColor;
520 internal new void Draw ()
524 //dc.FillRectangle (label_br_back_color, area);
525 ThemeEngine.Current.CPDrawBorderStyle (DeviceContext, ClientRectangle, BorderStyle);
527 if (Links.Count == 1 && Links[0].Start == 0 && Links[0].Length == -1) {
529 color = GetLinkColor (pieces[0], 0);
530 DeviceContext.DrawString (Text, Font, ThemeEngine.Current.ResPool.GetSolidBrush (color),
531 ClientRectangle, string_format);
535 for (int i = 0; i < num_pieces; i++) {
537 color = GetLinkColor (pieces[i], i);
539 if (pieces[i].link == null)
540 DeviceContext.DrawString (pieces[i].text, Font, ThemeEngine.Current.ResPool.GetSolidBrush (Color.Black),
541 pieces[i].rect.X, pieces[i].rect.Y, string_format);
543 DeviceContext.DrawString (pieces[i].text, link_font, ThemeEngine.Current.ResPool.GetSolidBrush (color),
544 pieces[i].rect.X, pieces[i].rect.Y, string_format);
547 DrawImage (DeviceContext, Image, ClientRectangle, image_align);
550 private void CreateLinkFont ()
552 link_font = new Font (Font.FontFamily, Font.Size, Font.Style | FontStyle.Underline,
556 #endregion // Private Methods
559 // System.Windows.Forms.LinkLabel.Link
563 private bool enabled;
565 private object linkData;
567 private bool visited;
568 private LinkLabel owner;
579 internal Link (LinkLabel owner)
588 public bool Enabled {
589 get { return enabled; }
591 if (enabled == value)
597 owner.CreateLinkPieces ();
602 get { return length; }
610 owner.CreateLinkPieces ();
614 public object LinkData {
615 get { return linkData; }
616 set { linkData = value; }
620 get { return start; }
628 owner.CreateLinkPieces ();
632 public bool Visited {
633 get { return visited; }
635 if (visited == value)
641 owner.CreateLinkPieces ();
647 // System.Windows.Forms.LinkLabel.Link
649 public class LinkCollection : IList, ICollection, IEnumerable
651 private LinkLabel owner;
652 private ArrayList collection = new ArrayList();
654 public LinkCollection (LinkLabel owner)
657 throw new ArgumentNullException ();
664 get { return collection.Count; }
667 public bool IsReadOnly {
668 get { return false; }
671 public virtual LinkLabel.Link this[int index] {
673 if (index < 0 || index >= Count)
674 throw new ArgumentOutOfRangeException();
676 return (LinkLabel.Link) collection[index];
679 if (index < 0 || index >= Count)
680 throw new ArgumentOutOfRangeException();
682 collection[index] = value;
686 public Link Add (int start, int length)
688 return Add (start, length, null);
692 public Link Add (int start, int length, object o)
694 Link link = new Link ();
697 if (Count == 1 && this[0].Start == 0
698 && this[0].Length == -1) {
702 link.Length = length;
705 idx = collection.Add (link);
708 owner.CreateLinkPieces ();
709 return (Link) collection[idx];
712 public virtual void Clear ()
715 owner.CreateLinkPieces ();
718 public bool Contains (LinkLabel.Link link)
720 return collection.Contains (link);
723 public IEnumerator GetEnumerator ()
725 return collection.GetEnumerator ();
728 public int IndexOf (LinkLabel.Link link)
730 return collection.IndexOf (link);
733 public void Remove (LinkLabel.Link value)
735 collection.Remove (value);
736 owner.CreateLinkPieces ();
739 public void RemoveAt (int index)
742 throw new ArgumentOutOfRangeException ("Invalid value for array index");
744 collection.Remove (collection[index]);
745 owner.CreateLinkPieces ();
748 bool IList.IsFixedSize {
752 object IList.this[int index] {
753 get { return collection[index]; }
754 set { collection[index] = value; }
757 object ICollection.SyncRoot {
761 bool ICollection.IsSynchronized {
765 void ICollection.CopyTo (Array dest, int index)
767 collection.CopyTo (dest, index);
770 int IList.Add (object control)
772 return collection.Add (control);
775 bool IList.Contains (object control)
777 return collection.Contains (control);
780 int IList.IndexOf (object control)
782 return collection.IndexOf (control);
785 void IList.Insert (int index, object value)
787 collection.Insert (index, value);
790 void IList.Remove (object control)
792 collection.Remove (control);