// * // * Copyright (C) 2008 Roger Alsing : http://www.RogerAlsing.com // * // * This library is free software; you can redistribute it and/or modify it // * under the terms of the GNU Lesser General Public License 2.1 or later, as // * published by the Free Software Foundation. See the included license.txt // * or http://www.gnu.org/copyleft/lesser.html for details. // * // * using System; using System.Collections; using System.ComponentModel; using System.Drawing; using System.Globalization; using System.Web; using System.Windows.Forms; using Alsing.Drawing.GDI; using Alsing.Windows.Forms.FormatLabel; using System.Collections.Generic; namespace Alsing.Windows.Forms.CoreLib { /// /// /// public class FormatLabelControl : BaseControl { private readonly Dictionary _Fonts = new Dictionary(); private Element _ActiveElement; private Element[] _Elements; private bool _HasImageError; private ImageList _ImageList; private Color _Link_Color = Color.Blue; private Color _Link_Color_Hover = Color.Blue; private bool _Link_UnderLine; private bool _Link_UnderLine_Hover = true; private List _Rows; private ScrollBars _ScrollBars = 0; private string _Text = "format label"; private bool _WordWrap = true; private PictureBox Filler; private HScrollBar hScroll; private VScrollBar vScroll; public ImageList ImageList { get { return _ImageList; } set { _ImageList = value; Invalidate(); //this.Text = this.Text; } } public Color Link_Color { get { return _Link_Color; } set { _Link_Color = value; Invalidate(); } } public Color Link_Color_Hover { get { return _Link_Color_Hover; } set { _Link_Color_Hover = value; Invalidate(); } } public bool Link_UnderLine { get { return _Link_UnderLine; } set { _Link_UnderLine = value; Invalidate(); } } public bool Link_UnderLine_Hover { get { return _Link_UnderLine_Hover; } set { _Link_UnderLine_Hover = value; Invalidate(); } } public bool AutoSizeHorizontal { get; set; } public bool AutoSizeVertical { get; set; } public bool WordWrap { get { return _WordWrap; } set { _WordWrap = value; CreateRows(); Invalidate(); } } [Browsable(true), EditorBrowsable(EditorBrowsableState.Always), Obsolete("", false)] public override Image BackgroundImage { get { return base.BackgroundImage; } set { base.BackgroundImage = value; } } public ScrollBars ScrollBars { get { return _ScrollBars; } set { _ScrollBars = value; InitScrollbars(); } } #region Defaults private Container components; public FormatLabelControl() { SetStyle(ControlStyles.ResizeRedraw, true); SetStyle(ControlStyles.Opaque, true); InitializeComponent(); Text = Text; } protected override void Dispose(bool disposing) { if (disposing) { foreach (GDIObject o in _Fonts.Values) o.Dispose(); if (components != null) components.Dispose(); } base.Dispose(disposing); } #region Component Designer generated code [Browsable(true)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)] public override string Text { get { return _Text; } set { try { //Text=value; _Text = value; CreateAll(); this.Invalidate(); } catch (Exception x) { Console.WriteLine(x.Message); System.Diagnostics.Debugger.Break(); } } } /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { this.Filler = new System.Windows.Forms.PictureBox(); this.vScroll = new System.Windows.Forms.VScrollBar(); this.hScroll = new System.Windows.Forms.HScrollBar(); this.SuspendLayout(); // // Filler // this.Filler.BackColor = System.Drawing.SystemColors.Control; this.Filler.Cursor = System.Windows.Forms.Cursors.Default; this.Filler.Location = new System.Drawing.Point(136, 112); this.Filler.Name = "Filler"; this.Filler.Size = new System.Drawing.Size(16, 16); this.Filler.TabIndex = 5; this.Filler.TabStop = false; // // vScroll // this.vScroll.Cursor = System.Windows.Forms.Cursors.Default; this.vScroll.LargeChange = 2; this.vScroll.Location = new System.Drawing.Point(136, -8); this.vScroll.Name = "vScroll"; this.vScroll.Size = new System.Drawing.Size(16, 112); this.vScroll.TabIndex = 4; this.vScroll.Scroll += new System.Windows.Forms.ScrollEventHandler(this.vScroll_Scroll); // // hScroll // this.hScroll.Cursor = System.Windows.Forms.Cursors.Default; this.hScroll.LargeChange = 1; this.hScroll.Location = new System.Drawing.Point(0, 112); this.hScroll.Maximum = 600; this.hScroll.Name = "hScroll"; this.hScroll.Size = new System.Drawing.Size(128, 16); this.hScroll.TabIndex = 3; // // FormatLabelControl // this.BackColor = System.Drawing.SystemColors.Window; this.Controls.AddRange(new System.Windows.Forms.Control[] { this.Filler, this.vScroll, this.hScroll }); this.Name = "FormatLabelControl"; this.Size = new System.Drawing.Size(160, 136); this.ResumeLayout(false); } private void CreateAll() { _Elements = CreateElements(); ClearFonts(); ApplyFormat(_Elements); CreateWords(_Elements); CreateRows(); SetAutoSize(); } private void ClearFonts() { foreach (GDIFont gf in _Fonts.Values) { gf.Dispose(); } _Fonts.Clear(); } #endregion #endregion public event ClickLinkEventHandler ClickLink = null; protected void OnClickLink(string Link) { if (ClickLink != null) ClickLink(this, new ClickLinkEventArgs(Link)); } private void SetAutoSize() { if (AutoSizeHorizontal) Width = GetWidth(); if (AutoSizeVertical) Height = GetHeight(); } protected override void OnPaint(PaintEventArgs e) { SetAutoSize(); //base.OnPaint (e); if (_HasImageError) CreateAll(); var bbuff = new GDISurface(Width, Height, this, true); Graphics g = Graphics.FromHdc(bbuff.hDC); try { bbuff.FontTransparent = true; if (BackgroundImage != null) { g.DrawImage(BackgroundImage, 0, 0, Width, Height); } else { bbuff.Clear(BackColor); } int x = Margin; int y = Margin; for (int i = vScroll.Value; i < _Rows.Count; i++) { var r = (Row) _Rows[i]; x = Margin; r.Visible = true; r.Top = y; if (r.RenderSeparator) { Color c1 = Color.FromArgb(120, 0, 0, 0); Brush b1 = new SolidBrush(c1); g.FillRectangle(b1, 0, y, Width, 1); Color c2 = Color.FromArgb(120, 255, 255, 255); Brush b2 = new SolidBrush(c2); g.FillRectangle(b2, 0, y + 1, Width, 1); b1.Dispose(); b2.Dispose(); //bbuff.DrawLine (this.ForeColor,new Point (0,y),new Point (this.Width,y)); } foreach (Word w in r.Words) { int ypos = r.Height - w.Height + y; if (w.Image != null) { g.DrawImage(w.Image, x, y); //bbuff.FillRect (Color.Red ,x,ypos,w.Width ,w.Height); } else { GDIFont gf; if (w.Element.Link != null) { Font f = null; FontStyle fs = w.Element.Font.Style; if (w.Element.Link == _ActiveElement) { if (_Link_UnderLine_Hover) fs |= FontStyle.Underline; f = new Font(w.Element.Font, fs); } else { if (_Link_UnderLine) fs |= FontStyle.Underline; f = new Font(w.Element.Font, fs); } gf = GetFont(f); } else { gf = GetFont(w.Element.Font); } bbuff.Font = gf; if (w.Element.Effect != TextEffect.None) { bbuff.TextForeColor = w.Element.EffectColor; if (w.Element.Effect == TextEffect.Outline) { for (int xx = -1; xx <= 1; xx++) for (int yy = -1; yy <= 1; yy++) bbuff.DrawTabbedString(w.Text, x + xx, ypos + yy, 0, 0); } else if (w.Element.Effect != TextEffect.None) { bbuff.DrawTabbedString(w.Text, x + 1, ypos + 1, 0, 0); } } if (w.Element.Link != null) { if (w.Element.Link == _ActiveElement) { bbuff.TextForeColor = Link_Color_Hover; } else { bbuff.TextForeColor = Link_Color; } } else bbuff.TextForeColor = w.Element.ForeColor; bbuff.TextBackColor = w.Element.BackColor; bbuff.DrawTabbedString(w.Text, x, ypos, 0, 0); } w.ScreenArea.X = x; w.ScreenArea.Y = ypos; x += w.Width; } y += r.Height + r.BottomPadd; if (y > Height) break; } } catch (Exception x) { Console.WriteLine(x.Message); } bbuff.RenderToControl(0, 0); bbuff.Dispose(); g.Dispose(); } private Element[] CreateElements() { string text = Text.Replace("\n", ""); text = text.Replace("\r", ""); string[] parts = text.Split('<'); var elements = new List(); int i = 0; foreach (string part in parts) { var cmd = new Element(); if (i == 0) { cmd.Text = part; } else { string[] TagTextPair = part.Split('>'); cmd.Tag = TagTextPair[0].ToLowerInvariant(); if (cmd.Tag.IndexOfAny(" \t".ToCharArray()) >= 0) { int ws = cmd.Tag.IndexOfAny(" \t".ToCharArray()); string s1 = TagTextPair[0].Substring(0, ws).ToLowerInvariant(); string s2 = TagTextPair[0].Substring(ws + 1); cmd.Tag = s1 + " " + s2; } cmd.Text = TagTextPair[1]; if (cmd.TagName == "img") { var img = new Element { Tag = cmd.Tag }; elements.Add(img); cmd.Tag = ""; // Elements.Add (cmd); } // // if (cmd.TagName == "hr") // { // Element hr=new Element(); // hr.Tag = cmd.Tag; // Elements.Add (hr); // cmd.Tag =""; // cmd.Text ="a"; // // Elements.Add (cmd); // } cmd.Text = cmd.Text.Replace("\t", " "); cmd.Text = cmd.Text.Replace("‘", "'"); cmd.Text = cmd.Text.Replace("’", "'"); cmd.Text = cmd.Text.Replace(" ", ((char) 1).ToString()); cmd.Text = HttpUtility.HtmlDecode(cmd.Text); // cmd.Text =cmd.Text.Replace (" ","*"); cmd.Text = cmd.Text.Replace(((char) 1).ToString(), " "); } elements.Add(cmd); i++; } var res = new Element[elements.Count]; elements.CopyTo(res); return res; } private string GetAttrib(string attrib, string tag) { try { if (tag.IndexOf(attrib) < 0) return ""; //tag=tag.Replace("\"",""); tag = tag.Replace("\t", " "); int start = tag.IndexOf(attrib); int end = start + attrib.Length; int valuestart = tag.IndexOf("=", end); if (valuestart < 0) return ""; valuestart++; string value = tag.Substring(valuestart); while (value.StartsWith(" ")) value = value.Substring(1); //int pos=0; if (value.StartsWith("\"")) { // = "value" value = value.Substring(1); int valueend = value.IndexOf("\""); value = value.Substring(0, valueend); return value; } else { // = value int valueend = value.IndexOf(" "); if (valueend < 0) valueend = value.Length; value = value.Substring(0, valueend); return value; } //return ""; } catch { return ""; } } private void ApplyFormat(Element[] Elements) { var bold = new Stack(); var italic = new Stack(); var underline = new Stack(); var forecolor = new Stack(); var backcolor = new Stack(); var fontsize = new Stack(); var fontname = new Stack(); var link = new Stack(); var effectcolor = new Stack(); var effect = new Stack(); bold.Push(Font.Bold); italic.Push(Font.Italic); underline.Push(Font.Underline); forecolor.Push(ForeColor); backcolor.Push(Color.Transparent); fontsize.Push((int) (Font.Size*1.3)); fontname.Push(Font.Name); effect.Push(TextEffect.None); effectcolor.Push(Color.Black); link.Push(null); foreach (Element Element in Elements) { switch (Element.TagName) { case "b": { bold.Push(true); break; } case "a": { //underline.Push (true); //forecolor.Push (_l); link.Push(Element); break; } case "i": case "em": { italic.Push(true); break; } case "u": { underline.Push(true); break; } case "font": { string _fontname = GetAttrib("face", Element.Tag); string _size = GetAttrib("size", Element.Tag); string _color = GetAttrib("color", Element.Tag); string _effectcolor = GetAttrib("effectcolor", Element.Tag); string _effect = GetAttrib("effect", Element.Tag); if (_size == "") fontsize.Push(fontsize.Peek()); else fontsize.Push(int.Parse(_size)); if (_fontname == "") fontname.Push(fontname.Peek()); else fontname.Push(_fontname); if (_color == "") forecolor.Push(forecolor.Peek()); else forecolor.Push(Color.FromName(_color)); if (_effectcolor == "") effectcolor.Push(effectcolor.Peek()); else effectcolor.Push(Color.FromName(_effectcolor)); if (_effect == "") effect.Push(effect.Peek()); else effect.Push(Enum.Parse(typeof (TextEffect), _effect, true)); break; } case "br": { Element.NewLine = true; break; } case "hr": { Element.NewLine = true; break; } case "h3": { fontsize.Push((int) (Font.Size*1.4)); bold.Push(true); Element.NewLine = true; break; } case "h4": { fontsize.Push((int) (Font.Size*1.2)); bold.Push(true); Element.NewLine = true; break; } case "/b": { bold.Pop(); break; } case "/a": { //underline.Pop (); //forecolor.Pop (); link.Pop(); break; } case "/i": case "/em": { italic.Pop(); break; } case "/u": { underline.Pop(); break; } case "/font": { fontname.Pop(); fontsize.Pop(); forecolor.Pop(); effect.Pop(); effectcolor.Pop(); break; } case "/h3": { fontsize.Pop(); bold.Pop(); Element.NewLine = true; break; } case "/h4": { fontsize.Pop(); bold.Pop(); Element.NewLine = true; break; } default: { break; } } //--------------------------------------------------------------------- var Bold = (bool) bold.Peek(); var Italic = (bool) italic.Peek(); var Underline = (bool) underline.Peek(); var Link = (Element) link.Peek(); var FontName = (string) fontname.Peek(); var FontSize = (int) fontsize.Peek(); var BackColor = (Color) backcolor.Peek(); var ForeColor1 = (Color) forecolor.Peek(); var Effect = (TextEffect) effect.Peek(); var EffectColor = (Color) effectcolor.Peek(); FontStyle fs = 0; if (Bold) fs |= FontStyle.Bold; if (Italic) fs |= FontStyle.Italic; if (Underline) fs |= FontStyle.Underline; var font = new Font(FontName, FontSize, fs); Element.Font = font; Element.BackColor = BackColor; Element.ForeColor = ForeColor1; Element.Link = Link; Element.Effect = Effect; Element.EffectColor = EffectColor; } } private bool IsIndex(string src) { int i; return int.TryParse(src,out i); } private void CreateWords(Element[] Elements) { var bbuff = new GDISurface(1, 1, this, false); _HasImageError = false; foreach (Element Element in Elements) { if (Element.TagName == "img") { Element.words = new Word[1]; Element.words[0] = new Word(); Image img = null; try { string SRC = GetAttrib("img", Element.Tag).ToLowerInvariant(); if (IsIndex(SRC)) { int index = int.Parse(SRC); img = ImageList.Images[index]; } else if (SRC.StartsWith("http://")) //from url {} else if (SRC.StartsWith("file://")) // from file { img = Image.FromFile(SRC.Substring(7)); } else //from file { img = Image.FromFile(SRC); } } catch { img = new Bitmap(20, 20); _HasImageError = true; } Element.words[0].Image = img; Element.words[0].Element = Element; if (img != null) { Element.words[0].Height = img.Height; Element.words[0].Width = img.Width; Element.words[0].ScreenArea.Width = img.Width; Element.words[0].ScreenArea.Height = img.Height; } } else { string[] words = Element.Text.Split(' '); Element.words = new Word[words.Length]; int i = 0; foreach (string word in words) { Element.words[i] = new Word(); string tmp ; Element.words[i].Element = Element; if (i == words.Length - 1) { Element.words[i].Text = word; tmp = word; } else { Element.words[i].Text = word + " "; tmp = word + " "; //last space cant be measured , lets measure an "," instead } //SizeF size=g.MeasureString (tmp,Element.Font); bbuff.Font = GetFont(Element.Font); Size s = bbuff.MeasureTabbedString(tmp, 0); Element.words[i].Height = s.Height; Element.words[i].Width = s.Width - 0; Element.words[i].ScreenArea.Width = Element.words[i].Width; Element.words[i].ScreenArea.Height = Element.words[i].Height; // Element.words[i].Link =Element.Link ; i++; } } } bbuff.Dispose(); } private GDIFont GetFont(Font font) { GDIFont gf = null; if (!_Fonts.TryGetValue(GetFontKey(font),out gf)) { gf = new GDIFont(font.Name, font.Size, font.Bold, font.Italic, font.Underline, false); _Fonts[GetFontKey(font)] = gf; } return gf; } private string GetFontKey(Font font) { return font.Name + font.Bold + font.Italic + font.Underline + font.Size; } private void CreateRows() { if (_Elements != null) { int x = 0; _Rows = new List(); //build rows--------------------------------------------- var row = new Row(); _Rows.Add(row); bool WhiteSpace = false; foreach (Element Element in _Elements) { if (Element.words == null) return; if (Element.NewLine) { //tag forces a new line x = 0; row = new Row(); _Rows.Add(row); WhiteSpace = true; } if (Element.TagName == "hr") { row.RenderSeparator = true; } //else //{ foreach (Word word in Element.words) { if (WordWrap) { int scrollwdh = 0; if (ScrollBars == ScrollBars.Both || ScrollBars == ScrollBars.Vertical) scrollwdh = vScroll.Width; if ((word.Width + x) > ClientWidth - Margin - scrollwdh) { //new line due to wordwrap x = 0; row = new Row(); _Rows.Add(row); WhiteSpace = true; } } if (word.Text.Replace(" ", "") != "" || word.Image != null) WhiteSpace = false; if (!WhiteSpace) { row.Words.Add(word); x += word.Width; } } //} } //apply width and height to all rows int index = 0; foreach (Row r in _Rows) { int width = 0; int height = 0; int padd = 0; if (index > 0) { int previndex = index - 1; var prev = (Row) _Rows[previndex]; while (previndex >= 0 && prev.Words.Count == 0) { prev = (Row) _Rows[previndex]; previndex--; } if (previndex >= 0) { prev = (Row) _Rows[previndex]; if (prev.Words.Count > 0) { var w = (Word) prev.Words[prev.Words.Count - 1]; height = w.Height; } } } foreach (Word w in r.Words) { if (w.Height > height && (w.Text != "")) height = w.Height; width += w.Width; } r.Height = height; int MaxImageH = 0; foreach (Word w in r.Words) { if (w.Image != null) { if (w.Height > height) MaxImageH = w.Height; } } foreach (Word w in r.Words) { int imgH = 0; int imgPadd = 0; if (w.Image != null) { string valign = GetAttrib("valign", w.Element.Tag); switch (valign) { case "top": { imgH = r.Height; imgPadd = w.Height - imgH; break; } case "middle": case "center": { imgH = r.Height; int tmp = (w.Height - imgH)/2; imgH += tmp; imgPadd = tmp; break; } case "bottom": { imgH = w.Height; imgPadd = 0; break; } default: { imgH = w.Height; imgPadd = 0; break; } } if (imgH > height) height = imgH; if (imgPadd > padd) padd = imgPadd; width += w.Width; } } r.Width = width; r.Height = height; r.BottomPadd = padd; index++; } vScroll.Maximum = _Rows.Count; } } private void InitScrollbars() { if (vScroll == null || hScroll == null) return; if (ScrollBars == ScrollBars.Both) { vScroll.Left = ClientWidth - vScroll.Width; vScroll.Top = 0; vScroll.Height = ClientHeight - hScroll.Height; hScroll.Left = 0; hScroll.Top = ClientHeight - hScroll.Height; hScroll.Width = ClientWidth - vScroll.Width; Filler.Left = vScroll.Left; Filler.Top = hScroll.Top; Filler.Visible = true; vScroll.Visible = true; hScroll.Visible = true; } else if (ScrollBars == ScrollBars.Vertical) { vScroll.Left = ClientWidth - vScroll.Width; vScroll.Top = 0; vScroll.Height = ClientHeight; hScroll.Left = 0; hScroll.Top = ClientHeight - hScroll.Height; hScroll.Width = ClientWidth - vScroll.Width; Filler.Left = vScroll.Left; Filler.Top = hScroll.Top; Filler.Visible = false; vScroll.Visible = true; hScroll.Visible = false; } else if (ScrollBars == ScrollBars.Horizontal) { vScroll.Left = ClientWidth - vScroll.Width; vScroll.Top = 0; vScroll.Height = ClientHeight; hScroll.Left = 0; hScroll.Top = ClientHeight - hScroll.Height; hScroll.Width = ClientWidth; Filler.Left = vScroll.Left; Filler.Top = hScroll.Top; Filler.Visible = false; vScroll.Visible = false; hScroll.Visible = true; } else if (ScrollBars == ScrollBars.None) { vScroll.Left = ClientWidth - vScroll.Width; vScroll.Top = 0; vScroll.Height = ClientHeight; hScroll.Left = 0; hScroll.Top = ClientHeight - hScroll.Height; hScroll.Width = ClientWidth; Filler.Left = vScroll.Left; Filler.Top = hScroll.Top; Filler.Visible = false; vScroll.Visible = false; hScroll.Visible = false; } } protected override void OnResize(EventArgs e) { try { InitScrollbars(); SetAutoSize(); } catch {} CreateRows(); base.OnResize(e); } protected override void OnMouseUp(MouseEventArgs e) { int y = e.Y; int x = e.X; int index = 0; bool Link = false; //this.Cursor =Cursors.Arrow; _ActiveElement = null; if (_Rows != null) { foreach (Row r in _Rows) { if (y >= r.Top && y <= r.Top + r.Height) { foreach (Word w in r.Words) { if (y >= w.ScreenArea.Top && y <= w.ScreenArea.Bottom) { if (x >= w.ScreenArea.Left && x <= w.ScreenArea.Right) { if (w.Element.Link != null) { Link = true; _ActiveElement = w.Element.Link; break; } } } } break; } index++; } } if (Link) { Cursor = Cursors.Hand; Invalidate(); OnClickLink(GetAttrib("href", _ActiveElement.Tag)); } else { Cursor = Cursors.Arrow; Invalidate(); } base.OnMouseUp(e); } protected override void OnMouseMove(MouseEventArgs e) { int y = e.Y; int x = e.X; int index = 0; bool Link = false; //this.Cursor =Cursors.Arrow; _ActiveElement = null; if (_Rows != null) { foreach (Row r in _Rows) { if (y >= r.Top && y <= r.Top + r.Height) { foreach (Word w in r.Words) { if (y >= w.ScreenArea.Top && y <= w.ScreenArea.Bottom) { if (x >= w.ScreenArea.Left && x <= w.ScreenArea.Right) { if (w.Element.Link != null) { Link = true; _ActiveElement = w.Element.Link; break; } } } } break; } index++; } } if (Link) { Cursor = Cursors.Hand; Invalidate(); } else { Cursor = Cursors.Arrow; Invalidate(); } base.OnMouseMove(e); } private void vScroll_Scroll(object sender, ScrollEventArgs e) { Invalidate(); } public int GetWidth() { int max = 0; foreach (Row r in _Rows) { if (r.Width > max) max = r.Width; } return max + Margin*2 + BorderWidth*2; } public int GetHeight() { int max = 0; foreach (Row r in _Rows) { max += r.Height; } return max + Margin*2 + BorderWidth*2; } #region PUBLIC PROPERTY MARGIN private int _Margin; public new int Margin { get { return _Margin; } set { _Margin = value; CreateRows(); Invalidate(); } } #endregion } }