LinkLabel.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  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 Novell, Inc.
  21. //
  22. // Authors:
  23. // Jordi Mas i Hernandez, [email protected]
  24. //
  25. // Based on work by:
  26. // Daniel Carrera, [email protected] (stubbed out)
  27. // Jaak Simm ([email protected]) (stubbed out)
  28. //
  29. // TODO:
  30. // - Change the cursor to a hand cursor when you are over a link (when cursors are available)
  31. // - Focus handeling
  32. //
  33. // INCOMPLETE
  34. using System.ComponentModel;
  35. using System.Collections;
  36. using System.Drawing;
  37. using System.Drawing.Drawing2D;
  38. namespace System.Windows.Forms
  39. {
  40. public class LinkLabel : Label, IButtonControl
  41. {
  42. /* Encapsulates a piece of text (regular or link)*/
  43. internal class Piece
  44. {
  45. public string text;
  46. public int start;
  47. public int end;
  48. public LinkLabel.Link link; // Empty link indicates regular text
  49. public RectangleF rect;
  50. public bool clicked;
  51. public Piece ()
  52. {
  53. start = end = 0;
  54. link = null;
  55. clicked = false;
  56. }
  57. }
  58. private Color active_link;
  59. private Color disabled_link;
  60. private Color link_color;
  61. private Color visited_color;
  62. private LinkArea link_area;
  63. private LinkBehavior link_behavior;
  64. private LinkCollection link_collection;
  65. private bool link_visited;
  66. private Font link_font;
  67. private bool link_click;
  68. private Piece[] pieces;
  69. private Cursor override_cursor;
  70. private DialogResult dialog_result;
  71. #region Events
  72. public event LinkLabelLinkClickedEventHandler LinkClicked;
  73. #endregion // Events
  74. public LinkLabel ()
  75. {
  76. link_collection = new LinkCollection (this);
  77. LinkArea = new LinkArea (0, -1);
  78. link_behavior = LinkBehavior.SystemDefault;
  79. link_visited = false;
  80. link_click = false;
  81. pieces = null;
  82. ActiveLinkColor = Color.Red;
  83. DisabledLinkColor = ThemeEngine.Current.ColorGrayText;
  84. LinkColor = Color.FromArgb (255, 0, 0, 255);
  85. VisitedLinkColor = Color.FromArgb (255, 128, 0, 128);
  86. }
  87. #region Public Properties
  88. public Color ActiveLinkColor {
  89. get { return active_link;}
  90. set {
  91. if (active_link == value)
  92. return;
  93. active_link = value;
  94. Refresh ();
  95. }
  96. }
  97. public Color DisabledLinkColor {
  98. get { return disabled_link;}
  99. set {
  100. if (disabled_link == value)
  101. return;
  102. disabled_link = value;
  103. Refresh ();
  104. }
  105. }
  106. public Color LinkColor {
  107. get { return link_color;}
  108. set {
  109. if (link_color == value)
  110. return;
  111. link_color = value;
  112. Refresh ();
  113. }
  114. }
  115. public Color VisitedLinkColor {
  116. get { return visited_color;}
  117. set {
  118. if (visited_color == value)
  119. return;
  120. visited_color = value;
  121. Refresh ();
  122. }
  123. }
  124. public LinkArea LinkArea {
  125. get { return link_area;}
  126. set {
  127. if (value.Start <0 || value.Length > 0)
  128. throw new ArgumentException ();
  129. if (!value.IsEmpty)
  130. Links.Add (value.Start, value.Length);
  131. link_area = value;
  132. Refresh ();
  133. }
  134. }
  135. public LinkBehavior LinkBehavior {
  136. get { return link_behavior;}
  137. set {
  138. if (link_behavior == value)
  139. return;
  140. link_behavior = value;
  141. Refresh ();
  142. }
  143. }
  144. public LinkLabel.LinkCollection Links {
  145. get { return link_collection;}
  146. }
  147. public bool LinkVisited {
  148. get { return link_visited;}
  149. set {
  150. if (link_visited == value)
  151. return;
  152. link_visited = value;
  153. Refresh ();
  154. }
  155. }
  156. protected Cursor OverrideCursor {
  157. get { return override_cursor;}
  158. set { override_cursor = value;}
  159. }
  160. public override string Text {
  161. get { return base.Text; }
  162. set {
  163. if (base.Text == value)
  164. return;
  165. base.Text = value;
  166. Refresh ();
  167. }
  168. }
  169. #endregion // Public Properties
  170. DialogResult IButtonControl.DialogResult {
  171. get { return dialog_result; }
  172. set { dialog_result = value; }
  173. }
  174. void IButtonControl.NotifyDefault (bool value)
  175. {
  176. }
  177. void IButtonControl.PerformClick ()
  178. {
  179. throw new NotImplementedException ();
  180. }
  181. #region Public Methods
  182. protected override AccessibleObject CreateAccessibilityInstance()
  183. {
  184. return base.CreateAccessibilityInstance();
  185. }
  186. protected override void CreateHandle ()
  187. {
  188. CreateLinkFont ();
  189. CreateLinkPieces ();
  190. base.CreateHandle();
  191. }
  192. protected override void OnEnabledChanged (EventArgs e)
  193. {
  194. base.OnEnabledChanged (e);
  195. Refresh ();
  196. }
  197. protected override void OnFontChanged (EventArgs e)
  198. {
  199. base.OnFontChanged (e);
  200. CreateLinkFont ();
  201. Refresh ();
  202. }
  203. protected override void OnGotFocus (EventArgs e)
  204. {
  205. base.OnGotFocus(e);
  206. }
  207. protected override void OnKeyDown (KeyEventArgs e)
  208. {
  209. base.OnKeyDown(e);
  210. }
  211. protected virtual void OnLinkClicked (LinkLabelLinkClickedEventArgs e)
  212. {
  213. if (LinkClicked != null)
  214. LinkClicked (this, e);
  215. }
  216. protected override void OnLostFocus (EventArgs e)
  217. {
  218. base.OnLostFocus (e);
  219. }
  220. protected override void OnMouseDown (MouseEventArgs e)
  221. {
  222. if (!Enabled) return;
  223. base.OnMouseDown(e);
  224. this.Capture = true;
  225. for (int i = 0; i < pieces.Length; i++) {
  226. if (pieces[i].rect.Contains (e.X, e.Y)) {
  227. if (pieces[i].link!= null) {
  228. pieces[i].clicked = true;
  229. Refresh ();
  230. }
  231. break;
  232. }
  233. }
  234. }
  235. protected override void OnMouseLeave(EventArgs e)
  236. {
  237. if (!Enabled) return;
  238. base.OnMouseLeave(e);
  239. }
  240. protected override void OnMouseMove (MouseEventArgs e)
  241. {
  242. base.OnMouseMove (e);
  243. }
  244. protected override void OnMouseUp (MouseEventArgs e)
  245. {
  246. if (!Enabled) return;
  247. base.OnMouseUp (e);
  248. this.Capture = false;
  249. for (int i = 0; i < pieces.Length; i++) {
  250. if (pieces[i].link!= null && pieces[i].clicked == true) {
  251. if (LinkClicked != null)
  252. LinkClicked (this, new LinkLabelLinkClickedEventArgs (pieces[i].link));
  253. pieces[i].clicked = false;
  254. Refresh ();
  255. }
  256. }
  257. }
  258. protected override void OnPaint (PaintEventArgs pevent)
  259. {
  260. if (Width <= 0 || Height <= 0 || Visible == false)
  261. return;
  262. Draw ();
  263. pevent.Graphics.DrawImage (ImageBuffer, 0, 0);
  264. }
  265. protected override void OnPaintBackground(PaintEventArgs e)
  266. {
  267. }
  268. protected override void OnTextAlignChanged (EventArgs e)
  269. {
  270. base.OnTextAlignChanged (e);
  271. Refresh ();
  272. }
  273. protected override void OnTextChanged (EventArgs e)
  274. {
  275. base.OnTextChanged (e);
  276. Refresh ();
  277. }
  278. protected Link PointInLink (int x, int y)
  279. {
  280. for (int i = 0; i < pieces.Length; i++) {
  281. if (pieces[i].rect.Contains (x,y) && pieces[i].link != null)
  282. return pieces[i].link;
  283. }
  284. return null;
  285. }
  286. protected override bool ProcessDialogKey (Keys keyData)
  287. {
  288. return base.ProcessDialogKey (keyData);
  289. }
  290. protected override void Select (bool directed, bool forward)
  291. {
  292. base.Select (directed, forward);
  293. }
  294. public new void Select ()
  295. {
  296. base.Select ();
  297. }
  298. protected override void SetBoundsCore (int x, int y, int width, int height, BoundsSpecified specified)
  299. {
  300. base.SetBoundsCore (x, y, width, height, specified);
  301. Refresh ();
  302. }
  303. protected override void WndProc (ref Message m)
  304. {
  305. base.WndProc (ref m);
  306. }
  307. #endregion //Public Methods
  308. #region Private Methods
  309. internal void CreateLinkPieces ()
  310. {
  311. if (Links.Count == 0)
  312. return;
  313. int cur_piece = 0;
  314. if (Links.Count == 1 && Links[0].Start == 0 && Links[0].Length == -1) {
  315. pieces = new Piece [1];
  316. pieces[cur_piece] = new Piece();
  317. pieces[cur_piece].start = 0;
  318. pieces[cur_piece].end = Text.Length;
  319. pieces[cur_piece].link = Links[0];
  320. pieces[cur_piece].text = Text;
  321. pieces[cur_piece].rect = ClientRectangle;
  322. return;
  323. }
  324. pieces = new Piece [(Links.Count * 2) + 1];
  325. pieces[cur_piece] = new Piece();
  326. pieces[cur_piece].start = 0;
  327. for (int i = 0; i < Text.Length; i++) { /* Every char on the text*/
  328. for (int l = 0; l < Links.Count; l++) { /* Every link that we know of*/
  329. if (Links[l].Start == i) {
  330. if (i > 0) {
  331. /*Push prev. regular text*/
  332. pieces[cur_piece].end = i;
  333. pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start,
  334. pieces[cur_piece].end - pieces[cur_piece].start);
  335. cur_piece++;
  336. /* New link*/
  337. pieces[cur_piece] = new Piece ();
  338. }
  339. pieces[cur_piece].start = Links[l].Start;
  340. pieces[cur_piece].end = Links[l].Start + Links[l].Length;
  341. pieces[cur_piece].link = Links[l];
  342. pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start,
  343. pieces[cur_piece].end - pieces[cur_piece].start);
  344. cur_piece++; /* Push link*/
  345. pieces[cur_piece] = new Piece();
  346. i+= Links[l].Length;
  347. pieces[cur_piece].start = i;
  348. }
  349. }
  350. }
  351. if (pieces[cur_piece].end == 0) {
  352. pieces[cur_piece].end = Text.Length;
  353. pieces[cur_piece].text = Text.Substring (pieces[cur_piece].start, pieces[cur_piece].end - pieces[cur_piece].start);
  354. }
  355. CharacterRange[] charRanges = new CharacterRange [pieces.Length];
  356. for (int i = 0; i < pieces.Length; i++)
  357. charRanges[i] = new CharacterRange (pieces[i].start, pieces[i].end - pieces[i].start);
  358. Region[] charRegions = new Region [pieces.Length];
  359. string_format.SetMeasurableCharacterRanges (charRanges);
  360. charRegions = DeviceContext.MeasureCharacterRanges (Text, Font, ClientRectangle, string_format);
  361. for (int i = 0; i < pieces.Length; i++) {
  362. //RectangleF[] f = charRegions[i].GetRegionScans (new Matrix());
  363. pieces[i].rect = charRegions[i].GetBounds (DeviceContext);
  364. Console.WriteLine (pieces[i].rect);
  365. }
  366. if (Visible && IsHandleCreated)
  367. Refresh ();
  368. }
  369. /* Check if the links overlap */
  370. internal void CheckLinks ()
  371. {
  372. for (int i = 0; i < Links.Count; i++) {
  373. for (int l = 0; l < Links.Count; l++) {
  374. if (i==l) continue;
  375. if (((Links[i].Start + Links[i].Length) >= Links[l].Start &&
  376. Links[i].Start + Links[i].Length <= Links[l].Start + Links[l].Length) ||
  377. (Links[i].Start >= Links[l].Start &&
  378. Links[i].Start <= Links[l].Start + Links[l].Length))
  379. throw new InvalidOperationException ("Overlapping link regions.");
  380. }
  381. }
  382. }
  383. private Color GetLinkColor (Piece piece, int i)
  384. {
  385. Color color;
  386. if (Enabled == false ||
  387. (piece.link != null && piece.link.Enabled == false))
  388. color = DisabledLinkColor;
  389. else
  390. if (piece.clicked == true)
  391. color = ActiveLinkColor;
  392. else
  393. if ((LinkVisited == true && i == 0) ||
  394. (piece.link != null && piece.link.Visited == true))
  395. color = VisitedLinkColor;
  396. else
  397. color = LinkColor;
  398. return color;
  399. }
  400. internal new void Draw ()
  401. {
  402. Color color;
  403. //dc.FillRectangle (label_br_back_color, area);
  404. ThemeEngine.Current.CPDrawBorderStyle (DeviceContext, ClientRectangle, BorderStyle);
  405. if (Links.Count == 1 && Links[0].Start == 0 && Links[0].Length == -1) {
  406. color = GetLinkColor (pieces[0], 0);
  407. DeviceContext.DrawString (Text, Font, new SolidBrush (color),
  408. ClientRectangle, string_format);
  409. return;
  410. }
  411. for (int i = 0; i < pieces.Length; i++) {
  412. color = GetLinkColor (pieces[i], i);
  413. if (pieces[i].link == null)
  414. DeviceContext.DrawString (pieces[i].text, Font, new SolidBrush (Color.Black),
  415. pieces[i].rect.X, pieces[i].rect.Y, string_format);
  416. else
  417. DeviceContext.DrawString (pieces[i].text, link_font, new SolidBrush (color),
  418. pieces[i].rect.X, pieces[i].rect.Y, string_format);
  419. }
  420. DrawImage (DeviceContext, Image, ClientRectangle, image_align);
  421. }
  422. private void CreateLinkFont ()
  423. {
  424. link_font = new Font (Font.FontFamily, Font.Size, Font.Style | FontStyle.Underline,
  425. Font.Unit);
  426. }
  427. #endregion // Private Methods
  428. //
  429. // System.Windows.Forms.LinkLabel.Link
  430. //
  431. public class Link
  432. {
  433. private bool enabled;
  434. private int length;
  435. private object linkData;
  436. private int start;
  437. private bool visited;
  438. private LinkLabel owner;
  439. internal Link ()
  440. {
  441. enabled = true;
  442. visited = false;
  443. length = start = 0;
  444. linkData = null;
  445. owner = null;
  446. }
  447. internal Link (LinkLabel owner)
  448. {
  449. enabled = true;
  450. visited = false;
  451. length = start = 0;
  452. linkData = null;
  453. this.owner = owner;
  454. }
  455. public bool Enabled {
  456. get { return enabled; }
  457. set {
  458. if (enabled == value)
  459. return;
  460. enabled = value;
  461. if (owner != null)
  462. owner.CreateLinkPieces ();
  463. }
  464. }
  465. public int Length {
  466. get { return length; }
  467. set {
  468. if (length == value)
  469. return;
  470. length = value;
  471. if (owner != null)
  472. owner.CreateLinkPieces ();
  473. }
  474. }
  475. public object LinkData {
  476. get { return linkData; }
  477. set { linkData = value; }
  478. }
  479. public int Start {
  480. get { return start; }
  481. set {
  482. if (start == value)
  483. return;
  484. start = value;
  485. if (owner != null)
  486. owner.CreateLinkPieces ();
  487. }
  488. }
  489. public bool Visited {
  490. get { return visited; }
  491. set {
  492. if (visited == value)
  493. return;
  494. visited = value;
  495. if (owner != null)
  496. owner.CreateLinkPieces ();
  497. }
  498. }
  499. }
  500. //
  501. // System.Windows.Forms.LinkLabel.Link
  502. //
  503. public class LinkCollection : IList, ICollection, IEnumerable
  504. {
  505. private LinkLabel owner;
  506. private ArrayList collection = new ArrayList();
  507. public LinkCollection (LinkLabel owner)
  508. {
  509. if (owner==null)
  510. throw new ArgumentNullException ();
  511. this.owner = owner;
  512. }
  513. public int Count {
  514. get { return collection.Count; }
  515. }
  516. public bool IsReadOnly {
  517. get { return false; }
  518. }
  519. public virtual LinkLabel.Link this[int index] {
  520. get {
  521. if (index < 0 || index >= Count)
  522. throw new ArgumentOutOfRangeException();
  523. return (LinkLabel.Link) collection[index];
  524. }
  525. set {
  526. if (index < 0 || index >= Count)
  527. throw new ArgumentOutOfRangeException();
  528. collection[index] = value;
  529. }
  530. }
  531. public Link Add (int start, int length)
  532. {
  533. return Add (start, length, null);
  534. }
  535. public Link Add (int start, int length, object o)
  536. {
  537. Link link = new Link ();
  538. int idx;
  539. if (Count == 1 && this[0].Start == 0
  540. && this[0].Length == -1) {
  541. Console.WriteLine ("Clear list");
  542. Clear ();
  543. }
  544. link.Length = length;
  545. link.Start = start;
  546. link.LinkData = o;
  547. idx = collection.Add (link);
  548. owner.CheckLinks ();
  549. owner.CreateLinkPieces ();
  550. return (Link) collection[idx];
  551. }
  552. public virtual void Clear ()
  553. {
  554. collection.Clear();
  555. owner.CreateLinkPieces ();
  556. }
  557. public bool Contains (LinkLabel.Link link)
  558. {
  559. return collection.Contains (link);
  560. }
  561. public IEnumerator GetEnumerator ()
  562. {
  563. return collection.GetEnumerator ();
  564. }
  565. public int IndexOf (LinkLabel.Link link)
  566. {
  567. return collection.IndexOf (link);
  568. }
  569. public void Remove (LinkLabel.Link value)
  570. {
  571. collection.Remove (value);
  572. owner.CreateLinkPieces ();
  573. }
  574. public void RemoveAt (int index)
  575. {
  576. if (index >= Count)
  577. throw new ArgumentOutOfRangeException ("Invalid value for array index");
  578. collection.Remove (collection[index]);
  579. owner.CreateLinkPieces ();
  580. }
  581. bool IList.IsFixedSize {
  582. get {return false;}
  583. }
  584. object IList.this[int index] {
  585. get { return collection[index]; }
  586. set { collection[index] = value; }
  587. }
  588. object ICollection.SyncRoot {
  589. get {return this;}
  590. }
  591. bool ICollection.IsSynchronized {
  592. get {return false;}
  593. }
  594. void ICollection.CopyTo (Array dest, int index)
  595. {
  596. collection.CopyTo (dest, index);
  597. }
  598. int IList.Add (object control)
  599. {
  600. return collection.Add (control);
  601. }
  602. bool IList.Contains (object control)
  603. {
  604. return collection.Contains (control);
  605. }
  606. int IList.IndexOf (object control)
  607. {
  608. return collection.IndexOf (control);
  609. }
  610. void IList.Insert (int index, object value)
  611. {
  612. collection.Insert (index, value);
  613. }
  614. void IList.Remove (object control)
  615. {
  616. collection.Remove (control);
  617. }
  618. }
  619. }
  620. }