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)
35 // $Log: LinkLabel.cs,v $
36 // Revision 1.9 2004/09/28 18:44:25 pbartok
37 // - Streamlined Theme interfaces:
38 // * Each DrawXXX method for a control now is passed the object for the
39 // control to be drawn in order to allow accessing any state the theme
42 // * ControlPaint methods for the theme now have a CP prefix to avoid
43 // name clashes with the Draw methods for controls
45 // * Every control now retrieves it's DefaultSize from the current theme
47 // Revision 1.8 2004/09/07 09:40:15 jordi
48 // LinkLabel fixes, methods, multiple links
50 // Revision 1.7 2004/08/21 22:32:14 pbartok
53 // Revision 1.6 2004/08/10 15:24:35 jackson
54 // Let Control handle buffering.
56 // Revision 1.5 2004/08/08 17:52:12 jordi
57 // *** empty log message ***
59 // Revision 1.4 2004/08/07 23:31:15 jordi
60 // fixes label bug and draw method name
62 // Revision 1.3 2004/08/07 19:16:31 jordi
63 // throw exceptions, fixes events, missing methods
65 // Revision 1.2 2004/07/22 15:22:19 jordi
66 // link label: check link overlapping, implement events, and fixes
68 // Revision 1.1 2004/07/21 16:19:17 jordi
69 // LinkLabel control implementation
75 using System.ComponentModel;
76 using System.Collections;
78 using System.Drawing.Drawing2D;
80 namespace System.Windows.Forms
82 public class LinkLabel : Label, IButtonControl
84 /* Encapsulates a piece of text (regular or link)*/
90 public LinkLabel.Link link; // Empty link indicates regular text
91 public RectangleF rect;
102 private Color active_link;
103 private Color disabled_link;
104 private Color link_color;
105 private Color visited_color;
106 private LinkArea link_area;
107 private LinkBehavior link_behavior;
108 private LinkCollection link_collection;
109 private bool link_visited;
110 private Font link_font;
111 private bool link_click;
112 private Piece[] pieces;
113 private Cursor override_cursor;
114 private DialogResult dialog_result;
117 public event LinkLabelLinkClickedEventHandler LinkClicked;
122 link_collection = new LinkCollection (this);
123 LinkArea = new LinkArea (0, -1);
124 link_behavior = LinkBehavior.SystemDefault;
125 link_visited = false;
129 ActiveLinkColor = Color.Red;
130 DisabledLinkColor = ThemeEngine.Current.ColorGrayText;
131 LinkColor = Color.FromArgb (255, 0, 0, 255);
132 VisitedLinkColor = Color.FromArgb (255, 128, 0, 128);
135 #region Public Properties
137 public Color ActiveLinkColor {
138 get { return active_link;}
140 if (active_link == value)
148 public Color DisabledLinkColor {
150 get { return disabled_link;}
152 if (disabled_link == value)
155 disabled_link = value;
160 public Color LinkColor {
161 get { return link_color;}
163 if (link_color == value)
171 public Color VisitedLinkColor {
172 get { return visited_color;}
174 if (visited_color == value)
177 visited_color = value;
182 public LinkArea LinkArea {
183 get { return link_area;}
186 if (value.Start <0 || value.Length > 0)
187 throw new ArgumentException ();
190 Links.Add (value.Start, value.Length);
197 public LinkBehavior LinkBehavior {
199 get { return link_behavior;}
201 if (link_behavior == value)
204 link_behavior = value;
209 public LinkLabel.LinkCollection Links {
210 get { return link_collection;}
213 public bool LinkVisited {
214 get { return link_visited;}
216 if (link_visited == value)
219 link_visited = value;
224 protected Cursor OverrideCursor {
225 get { return override_cursor;}
226 set { override_cursor = value;}
229 public override string Text {
230 get { return base.Text; }
232 if (base.Text == value)
240 #endregion // Public Properties
242 DialogResult IButtonControl.DialogResult {
243 get { return dialog_result; }
244 set { dialog_result = value; }
248 void IButtonControl.NotifyDefault (bool value)
253 void IButtonControl.PerformClick ()
255 throw new NotImplementedException ();
258 #region Public Methods
259 protected override AccessibleObject CreateAccessibilityInstance()
261 return base.CreateAccessibilityInstance();
264 protected override void CreateHandle ()
272 protected override void OnEnabledChanged (EventArgs e)
274 base.OnEnabledChanged (e);
278 protected override void OnFontChanged (EventArgs e)
280 base.OnFontChanged (e);
285 protected override void OnGotFocus (EventArgs e)
290 protected override void OnKeyDown (KeyEventArgs e)
295 protected virtual void OnLinkClicked (LinkLabelLinkClickedEventArgs e)
297 if (LinkClicked != null)
298 LinkClicked (this, e);
301 protected override void OnLostFocus (EventArgs e)
303 base.OnLostFocus (e);
306 protected override void OnMouseDown (MouseEventArgs e)
308 if (!Enabled) return;
313 for (int i = 0; i < pieces.Length; i++) {
314 if (pieces[i].rect.Contains (e.X, e.Y)) {
315 if (pieces[i].link!= null) {
316 pieces[i].clicked = true;
324 protected override void OnMouseLeave(EventArgs e)
326 if (!Enabled) return;
328 base.OnMouseLeave(e);
331 protected override void OnMouseMove (MouseEventArgs e)
333 base.OnMouseMove (e);
336 protected override void OnMouseUp (MouseEventArgs e)
338 if (!Enabled) return;
341 this.Capture = false;
343 for (int i = 0; i < pieces.Length; i++) {
344 if (pieces[i].link!= null && pieces[i].clicked == true) {
346 if (LinkClicked != null)
347 LinkClicked (this, new LinkLabelLinkClickedEventArgs (pieces[i].link));
349 pieces[i].clicked = false;
355 protected override void OnPaint (PaintEventArgs pevent)
357 if (Width <= 0 || Height <= 0 || Visible == false)
361 pevent.Graphics.DrawImage (ImageBuffer, 0, 0);
364 protected override void OnPaintBackground(PaintEventArgs e)
369 protected override void OnTextAlignChanged (EventArgs e)
371 base.OnTextAlignChanged (e);
375 protected override void OnTextChanged (EventArgs e)
377 base.OnTextChanged (e);
381 protected Link PointInLink (int x, int y)
383 for (int i = 0; i < pieces.Length; i++) {
384 if (pieces[i].rect.Contains (x,y) && pieces[i].link != null)
385 return pieces[i].link;
391 protected override bool ProcessDialogKey (Keys keyData)
393 return base.ProcessDialogKey (keyData);
396 protected override void Select (bool directed, bool forward)
398 base.Select (directed, forward);
401 public void Select ()
406 protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
408 base.SetBoundsCore (x, y, width, height, specified);
412 protected override void WndProc (ref Message m)
414 base.WndProc (ref m);
417 #endregion //Public Methods
419 #region Private Methods
421 internal void CreateLinkPieces ()
423 if (Links.Count == 0)
428 if (Links.Count == 1 && Links[0].Start == 0 && Links[0].Length == -1) {
429 pieces = new Piece [1];
430 pieces[cur_piece] = new Piece();
431 pieces[cur_piece].start = 0;
432 pieces[cur_piece].end = Text.Length;
433 pieces[cur_piece].link = Links[0];
434 pieces[cur_piece].text = Text;
435 pieces[cur_piece].rect = ClientRectangle;
439 pieces = new Piece [(Links.Count * 2) + 1];
440 pieces[cur_piece] = new Piece();
441 pieces[cur_piece].start = 0;
443 for (int i = 0; i < Text.Length; i++) { /* Every char on the text*/
444 for (int l = 0; l < Links.Count; l++) { /* Every link that we know of*/
445 if (Links[l].Start == i) {
447 /*Push prev. regular text*/
448 pieces[cur_piece].end = i;
449 pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start,
450 pieces[cur_piece].end - pieces[cur_piece].start);
455 pieces[cur_piece] = new Piece ();
458 pieces[cur_piece].start = Links[l].Start;
459 pieces[cur_piece].end = Links[l].Start + Links[l].Length;
460 pieces[cur_piece].link = Links[l];
461 pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start,
462 pieces[cur_piece].end - pieces[cur_piece].start);
464 cur_piece++; /* Push link*/
465 pieces[cur_piece] = new Piece();
467 pieces[cur_piece].start = i;
472 if (pieces[cur_piece].end == 0) {
473 pieces[cur_piece].end = Text.Length;
474 pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start, pieces[cur_piece].end - pieces[cur_piece].start);
477 CharacterRange[] charRanges = new CharacterRange [pieces.Length];
479 for (int i = 0; i < pieces.Length; i++)
480 charRanges[i] = new CharacterRange (pieces[i].start, pieces[i].end - pieces[i].start);
482 Region[] charRegions = new Region [pieces.Length];
483 string_format.SetMeasurableCharacterRanges (charRanges);
485 charRegions = DeviceContext.MeasureCharacterRanges (Text, Font, ClientRectangle, string_format);
487 for (int i = 0; i < pieces.Length; i++) {
488 //RectangleF[] f = charRegions[i].GetRegionScans (new Matrix());
489 pieces[i].rect = charRegions[i].GetBounds (DeviceContext);
490 Console.WriteLine (pieces[i].rect);
493 if (Visible && IsHandleCreated)
498 /* Check if the links overlap */
499 internal void CheckLinks ()
501 for (int i = 0; i < Links.Count; i++) {
502 for (int l = 0; l < Links.Count; l++) {
505 if (((Links[i].Start + Links[i].Length) >= Links[l].Start &&
506 Links[i].Start + Links[i].Length <= Links[l].Start + Links[l].Length) ||
507 (Links[i].Start >= Links[l].Start &&
508 Links[i].Start <= Links[l].Start + Links[l].Length))
509 throw new InvalidOperationException ("Overlapping link regions.");
514 private Color GetLinkColor (Piece piece, int i)
518 if (Enabled == false ||
519 (piece.link != null && piece.link.Enabled == false))
520 color = DisabledLinkColor;
522 if (piece.clicked == true)
523 color = ActiveLinkColor;
525 if ((LinkVisited == true && i == 0) ||
526 (piece.link != null && piece.link.Visited == true))
527 color = VisitedLinkColor;
534 internal void Draw ()
538 //dc.FillRectangle (label_br_back_color, area);
539 ThemeEngine.Current.CPDrawBorderStyle (DeviceContext, ClientRectangle, BorderStyle);
541 if (Links.Count == 1 && Links[0].Start == 0 && Links[0].Length == -1) {
543 color = GetLinkColor (pieces[0], 0);
544 DeviceContext.DrawString (Text, Font, new SolidBrush (color),
545 ClientRectangle, string_format);
549 for (int i = 0; i < pieces.Length; i++) {
551 color = GetLinkColor (pieces[i], i);
553 if (pieces[i].link == null)
554 DeviceContext.DrawString (pieces[i].text, Font, new SolidBrush (Color.Black),
555 pieces[i].rect.X, pieces[i].rect.Y, string_format);
557 DeviceContext.DrawString (pieces[i].text, link_font, new SolidBrush (color),
558 pieces[i].rect.X, pieces[i].rect.Y, string_format);
561 DrawImage (DeviceContext, Image, ClientRectangle, image_align);
564 private void CreateLinkFont ()
566 link_font = new Font (Font.FontFamily, Font.Size, Font.Style | FontStyle.Underline,
570 #endregion // Private Methods
573 // System.Windows.Forms.LinkLabel.Link
577 private bool enabled;
579 private object linkData;
581 private bool visited;
582 private LinkLabel owner;
593 internal Link (LinkLabel owner)
602 public bool Enabled {
603 get { return enabled; }
605 if (enabled == value)
611 owner.CreateLinkPieces ();
616 get { return length; }
624 owner.CreateLinkPieces ();
628 public object LinkData {
629 get { return linkData; }
630 set { linkData = value; }
634 get { return start; }
642 owner.CreateLinkPieces ();
646 public bool Visited {
647 get { return visited; }
649 if (visited == value)
655 owner.CreateLinkPieces ();
661 // System.Windows.Forms.LinkLabel.Link
663 public class LinkCollection : IList, ICollection, IEnumerable
665 private LinkLabel owner;
666 private ArrayList collection = new ArrayList();
668 public LinkCollection (LinkLabel owner)
671 throw new ArgumentNullException ();
677 get { return collection.Count; }
680 public bool IsReadOnly {
681 get { return false; }
684 public virtual LinkLabel.Link this[int index] {
686 if (index < 0 || index >= Count)
687 throw new ArgumentOutOfRangeException();
689 return (LinkLabel.Link) collection[index];
692 if (index < 0 || index >= Count)
693 throw new ArgumentOutOfRangeException();
695 collection[index] = value;
699 public Link Add (int start, int length)
701 return Add (start, length, null);
705 public Link Add (int start, int length, object o)
707 Link link = new Link ();
710 if (Count == 1 && this[0].Start == 0
711 && this[0].Length == -1) {
712 Console.WriteLine ("Clear list");
716 link.Length = length;
719 idx = collection.Add (link);
722 owner.CreateLinkPieces ();
723 return (Link) collection[idx];
726 public virtual void Clear ()
729 owner.CreateLinkPieces ();
732 public bool Contains (LinkLabel.Link link)
734 return collection.Contains (link);
737 public IEnumerator GetEnumerator ()
739 return collection.GetEnumerator ();
742 public int IndexOf (LinkLabel.Link link)
744 return collection.IndexOf (link);
747 public void Remove (LinkLabel.Link value)
749 collection.Remove (value);
750 owner.CreateLinkPieces ();
753 public void RemoveAt (int index)
756 throw new ArgumentOutOfRangeException ("Invalid value for array index");
758 collection.Remove (collection[index]);
759 owner.CreateLinkPieces ();
762 bool IList.IsFixedSize {
766 object IList.this[int index] {
767 get { return collection[index]; }
768 set { collection[index] = value; }
771 object ICollection.SyncRoot {
775 bool ICollection.IsSynchronized {
779 void ICollection.CopyTo (Array dest, int index)
781 collection.CopyTo (dest, index);
784 int IList.Add (object control)
786 return collection.Add (control);
789 bool IList.Contains (object control)
791 return collection.Contains (control);
794 int IList.IndexOf (object control)
796 return collection.IndexOf (control);
799 void IList.Insert (int index, object value)
801 collection.Insert (index, value);
804 void IList.Remove (object control)
806 collection.Remove (control);