Click here to Skip to main content
15,891,204 members
Articles / Programming Languages / C#

Personal Wave Recorder

Rate me:
Please Sign up or sign in to vote.
4.97/5 (94 votes)
19 Mar 2010CPOL15 min read 141.5K   11.7K   208  
DSP chains, Complex Fourier, ACM, Visualizers, EQ, Custom controls.. the works.
#region Directives
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Drawing;
using System.Diagnostics;
using System.Text;
using System.Reflection;
using System.Drawing.Drawing2D;
#endregion

namespace Personal_Wave_Recorder
{
    public class CustomToolTip : NativeWindow, IDisposable
    {
        #region Constants
        // setwindowpos
        static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2);
        static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
        static readonly IntPtr HWND_TOP = new IntPtr(0);
        static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
        // size/move
        private const uint SWP_NOSIZE = 0x0001;
        private const uint SWP_NOMOVE = 0x0002;
        private const uint SWP_NOZORDER = 0x0004;
        private const uint SWP_NOREDRAW = 0x0008;
        private const uint SWP_NOACTIVATE = 0x0010;
        private const uint SWP_FRAMECHANGED = 0x0020;
        private const uint SWP_SHOWWINDOW = 0x0040;
        private const uint SWP_HIDEWINDOW = 0x0080;
        private const uint SWP_NOCOPYBITS = 0x0100;
        private const uint SWP_NOOWNERZORDER = 0x0200;
        private const uint SWP_NOSENDCHANGING = 0x0400;
        // styles
        private const int TTS_ALWAYSTIP = 0x01;
        private const int TTS_NOPREFIX = 0x02;
        private const int TTS_NOANIMATE = 0x10;
        private const int TTS_NOFADE = 0x20;
        private const int TTS_BALLOON = 0x40;
        private const int TTS_CLOSE = 0x80;
        private const int TTS_USEVISUALSTYLE = 0x100;
        // window messages
        private const int WM_NOTIFY = 0x4E;
        private const int WM_REFLECT = 0x2000;
        private const int WM_PAINT = 0xF;
        private const int WM_SIZE = 0x5;
        private const int WM_MOVE = 0x3;
        private const int WM_SETFONT = 0x30;
        private const int WM_GETFONT = 0x31;
        private const int WM_SHOWWINDOW = 0x18;
        private const int WM_MOUSEMOVE = 0x200;
        private const int WM_MOUSELEAVE = 0x2A3;
        private const int WM_LBUTTONDOWN = 0x201;
        private const int WM_LBUTTONUP = 0x202;
        private const int WM_LBUTTONDBLCLK = 0x203;
        private const int WM_RBUTTONDOWN = 0x204;
        private const int WM_RBUTTONUP = 0x205;
        private const int WM_RBUTTONDBLCLK = 0x206;
        private const int WM_MBUTTONDOWN = 0x207;
        private const int WM_MBUTTONUP = 0x208;
        private const int WM_MBUTTONDBLCLK = 0x209;
        private const int WM_MOUSEWHEEL = 0x20A;
        private const int WM_TIMER = 0x113;
        private const int WM_NCPAINT = 0x85;
        private const int WM_DESTROY = 0x2;
        private const int WM_SETFOCUS = 0x7;
        private const int WM_KILLFOCUS = 0x8;
        private const int WM_IME_NOTIFY = 0x282;
        private const int WM_IME_SETCONTEXT = 0x281;
        private const int WM_ACTIVATE = 0x6;
        private const int WM_NCACTIVATE = 0x86;
        private const int WM_STYLECHANGED = 0x7d;
        private const int WM_STYLECHANGING = 0x7c;
        private const int WM_WINDOWPOSCHANGING = 0x46;
        private const int WM_WINDOWPOSCHANGED = 0x47;
        private const int WM_NCCALCSIZE = 0x83;
        private const int WM_CTLCOLOR = 0x3d8d610;
        // window styles
        private const int GWL_STYLE = (-16);
        private const int GWL_EXSTYLE = (-20);
        private const int SS_OWNERDRAW = 0xD;
        private const int WS_OVERLAPPED = 0x0;
        private const int WS_TABSTOP = 0x10000;
        private const int WS_THICKFRAME = 0x40000;
        private const int WS_HSCROLL = 0x100000;
        private const int WS_VSCROLL = 0x200000;
        private const int WS_BORDER = 0x800000;
        private const int WS_CLIPCHILDREN = 0x2000000;
        private const int WS_CLIPSIBLINGS = 0x4000000;
        private const int WS_VISIBLE = 0x10000000;
        private const int WS_CHILD = 0x40000000;
        private const int WS_POPUP = -2147483648;
        // window extended styles
        private const int WS_EX_LTRREADING = 0x0;
        private const int WS_EX_LEFT = 0x0;
        private const int WS_EX_RIGHTSCROLLBAR = 0x0;
        private const int WS_EX_DLGMODALFRAME = 0x1;
        private const int WS_EX_NOPARENTNOTIFY = 0x4;
        private const int WS_EX_TOPMOST = 0x8;
        private const int WS_EX_ACCEPTFILES = 0x10;
        private const int WS_EX_TRANSPARENT = 0x20;
        private const int WS_EX_MDICHILD = 0x40;
        private const int WS_EX_TOOLWINDOW = 0x80;
        private const int WS_EX_WINDOWEDGE = 0x100;
        private const int WS_EX_CLIENTEDGE = 0x200;
        private const int WS_EX_CONTEXTHELP = 0x400;
        private const int WS_EX_RIGHT = 0x1000;
        private const int WS_EX_RTLREADING = 0x2000;
        private const int WS_EX_LEFTSCROLLBAR = 0x4000;
        private const int WS_EX_CONTROLPARENT = 0x10000;
        private const int WS_EX_STATICEDGE = 0x20000;
        private const int WS_EX_APPWINDOW = 0x40000;
        private const int WS_EX_NOACTIVATE = 0x8000000;
        private const int WS_EX_LAYERED = 0x80000;
        #endregion

        #region Structs
        [StructLayout(LayoutKind.Sequential)]
        private struct RECT
        {
            private RECT(int X, int Y, int Width, int Height)
            {
                this.Left = X;
                this.Top = Y;
                this.Right = Width;
                this.Bottom = Height;
            }
            internal int Left;
            internal int Top;
            internal int Right;
            internal int Bottom;
        }
        #endregion

        #region API
        [DllImport("user32.dll", SetLastError = true)]
        private static extern IntPtr CreateWindowEx(int exstyle, string lpClassName, string lpWindowName, int dwStyle,
            int x, int y, int nWidth, int nHeight, IntPtr hwndParent, IntPtr Menu, IntPtr hInstance, IntPtr lpParam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool DestroyWindow(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = false)]
        private static extern IntPtr GetDesktopWindow();

        [DllImport("user32.dll", ExactSpelling = true)]
        private static extern IntPtr SetTimer(IntPtr hWnd, int nIDEvent, uint uElapse, IntPtr lpTimerFunc);

        [DllImport("user32.dll", ExactSpelling = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool KillTimer(IntPtr hWnd, uint uIDEvent);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndAfter, int x, int y, int cx, int cy, uint flags);

        [DllImport("user32.dll")]
        private static extern bool GetClientRect(IntPtr hWnd, ref RECT r);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect);

        [DllImport("user32.dll")]
        private static extern int GetWindowLong(IntPtr hwnd, int nIndex);

        [DllImport("user32.dll")]
        private static extern int SetWindowLong(IntPtr hwnd, int nIndex, int dwNewLong);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetCursorPos(ref Point lpPoint);

        [DllImport("user32.dll")]
        private static extern bool ScreenToClient(IntPtr hWnd, ref Point lpPoint);

        [DllImport("user32.dll")]
        private static extern IntPtr GetDC(IntPtr handle);

        [DllImport("user32.dll")]
        private static extern int ReleaseDC(IntPtr handle, IntPtr hdc);

        [DllImport("gdi32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
        #endregion

        #region Fields
        private bool _bTimerActive = false;
        private bool _bTipShowing = false;
        private bool _bTextRightToLeft = false;
        private bool _bUseIcon = false;
        private int _iTimerTick = 0;
        private int _iDelayTime = 1000;
        private int _iVisibleTime = 3000;
        private string _sClientCaption = String.Empty;
        private string _sClientTitle = String.Empty;
        private Color _clrForeColor = Color.Black;
        private Color _clrGradientBegin = Color.White;
        private Color _clrGradientEnd = Color.Silver;
        private IntPtr _hTipWnd = IntPtr.Zero;
        private IntPtr _hInstance = IntPtr.Zero;
        private IntPtr _hParentWnd = IntPtr.Zero;
        private Rectangle _rcClientBounds = new Rectangle();
        private Font _fntTitleFont;
        private Font _fntCaptionFont;
        private Image _imgClientImage = null;
        private cStoreDc _cDrawDc;
        #endregion

        #region Constructor
        public CustomToolTip(IntPtr hParentWnd)
        {
            Type t = typeof(ToolTip);
            Module m = t.Module;
            _hInstance = Marshal.GetHINSTANCE(m);
            _hParentWnd = hParentWnd;
            // create window
            _hTipWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_TOOLWINDOW,
                "STATIC", "",
                SS_OWNERDRAW | WS_CHILD | WS_CLIPSIBLINGS | WS_OVERLAPPED,
                0, 0,
                0, 0,
                GetDesktopWindow(),
                IntPtr.Zero, _hInstance, IntPtr.Zero);
            // set starting position
            SetWindowPos(_hTipWnd, HWND_TOP,
                0, 0,
                0, 0,
                SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOOWNERZORDER);
            this.AssignHandle(_hTipWnd);
        }

        private void Create()
        {
            Destroy();
            _fntTitleFont = new Font("Tahoma", 8, FontStyle.Bold);
            _fntCaptionFont = new Font("Tahoma", 8, FontStyle.Regular);
        }

        private void Destroy()
        {
            if (_fntTitleFont != null)
                _fntTitleFont.Dispose();
            if (_fntCaptionFont != null)
                _fntCaptionFont.Dispose();
        }
        #endregion

        #region Properties
        private Rectangle TipBounds
        {
            get { return _rcClientBounds; }
            set { _rcClientBounds = value; }
        }

        private string Caption
        {
            get { return _sClientCaption; }
            set { _sClientCaption = value; }
        }

        public int DelayTime
        {
            get { return _iDelayTime; }
            set { _iDelayTime = value; }
        }

        public Color ForeColor
        {
            get { return _clrForeColor; }
            set { _clrForeColor = value; }
        }

        public Color GradientBegin
        {
            get { return _clrGradientBegin; }
            set { _clrGradientBegin = value; }
        }

        public Color GradientEnd
        {
            get { return _clrGradientEnd; }
            set { _clrGradientEnd = value; }
        }

        public Image ItemImage
        {
            get { return _imgClientImage; }
            set { _imgClientImage = value; }
        }

        public int MaximumLength
        {
            set { _rcClientBounds.Width = value; }
        }

        public bool TextRightToLeft
        {
            get { return _bTextRightToLeft; }
            set { _bTextRightToLeft = value; }
        }

        private string Title
        {
            get { return _sClientTitle; }
            set { _sClientTitle = value; }
        }

        public bool UseIcon
        {
            get { return _bUseIcon; }
            set { _bUseIcon = value; }
        }

        public int VisibleTime
        {
            get { return _iVisibleTime; }
            set { _iVisibleTime = value; }
        }
        #endregion

        #region Public Methods
        /// <summary>Start the ToolTip timer</summary>
        /// <param name="title">optional title text</param>
        /// <param name="caption">caption text</param>
        /// <param name="image">optional image</param>
        /// <param name="bounds">size and location of tooltip [manual sizing]</param>
        public void Start(string title, string caption, Image image, Rectangle bounds)
        {
            // reset timer
            if (_bTimerActive)
                Stop();
            // create graphics objects
            Create();
            this.Title = title;
            this.Caption = caption;
            this.ItemImage = image;
            this.TipBounds = bounds;
            CreateBuffer(bounds);
            // start the timer
            SetTimer(_hTipWnd, 1, 100, IntPtr.Zero);
        }

        /// <summary>Start the ToolTip timer</summary>
        /// <param name="title">optional title text</param>
        /// <param name="caption">caption text</param>
        /// <param name="image">optional image</param>
        /// <param name="pt">location of tooltip [auto-calculate size]</param>
        public void Start(string title, string caption, Image image, Point pt)
        {
            // reset timer
            if (_bTimerActive)
                Stop();
            // create graphics objects
            Create();
            this.Title = title;
            this.Caption = caption;
            this.ItemImage = image;
            Rectangle bounds = CalculateSize();
            bounds.X = pt.X + 16;
            bounds.Y = pt.Y + bounds.Height + 16;
            this.TipBounds = bounds;
            CreateBuffer(bounds);
            // start the timer
            SetTimer(_hTipWnd, 1, 100, IntPtr.Zero);
        }

        private void CreateBuffer(Rectangle bounds)
        {
            // draw it into a buffer
            if (_cDrawDc != null)
                _cDrawDc.Dispose();
            _cDrawDc = new cStoreDc();
            _cDrawDc.Width = bounds.Width;
            _cDrawDc.Height = bounds.Height;
            bounds.X = 0;
            bounds.Y = 0;
            PositionWindow();
            using (Graphics g = Graphics.FromHdc(_cDrawDc.Hdc))
            {
                CopyBackground(g);
                g.SmoothingMode = SmoothingMode.HighQuality;
                bounds.Inflate(-4, -4);
                DrawDropShadow(g, bounds, 4, 255);
                DrawBackGround(g, bounds);
                DrawBorder(g, bounds);
                DrawCaption(g, bounds);
                DrawTitle(g, bounds);
                DrawIcon(g, bounds);
            }
        }

        /// <summary>Hide the tooltip</summary>
        public void Stop()
        {
            // hide the window
            ShowWindow(false);
            // dispose of graphics
            Destroy();
            // kill the timer
            KillTimer(_hTipWnd, 1);
            // reset properties
            this.Title = String.Empty;
            this.Caption = String.Empty;
            this.ItemImage = null;
            this.TipBounds = Rectangle.Empty;
            // reset timer values
            _iTimerTick = 0;
            _bTipShowing = false;
            _bTimerActive = false;
        }

        /// <summary>Dispose of the tooltip</summary>
        public void Dispose()
        {
            Destroy();
            if (this.ItemImage != null)
            {
                this.ItemImage.Dispose();
                this.ItemImage = null;
            }
            if (_hTipWnd != IntPtr.Zero)
            {
                this.ReleaseHandle();
                DestroyWindow(_hTipWnd);
                _hTipWnd = IntPtr.Zero;
            }
        }
        #endregion

        #region Private Methods
        #region Drawing
        private void Draw()
        {
            if ((this.Caption != String.Empty) && (this.TipBounds != Rectangle.Empty))
            {
                // show the window
                ShowWindow(true);
                // draw the tempdc to the window
                IntPtr hdc = GetDC(_hTipWnd);
                BitBlt(hdc, 0, 0, this.TipBounds.Width, this.TipBounds.Height, _cDrawDc.Hdc, 0, 0, 0xCC0020);
                ReleaseDC(_hTipWnd, hdc);
            }
        }

        private void DrawBorder(Graphics g, Rectangle bounds)
        {
            // draw the frame
            using (GraphicsPath borderPath = CreateRoundRectanglePath(g, bounds, 4f))
            {
                using (Pen borderPen = new Pen(Color.FromArgb(250, 0x44, 0x44, 0x44)))
                    g.DrawPath(borderPen, borderPath);
            }
        }

        private void DrawBackGround(Graphics g, Rectangle bounds)
        {
            // fill the body
            using (GraphicsPath fillPath = CreateRoundRectanglePath(g, bounds, 4f))
            {
                using (LinearGradientBrush shadowBrush = new LinearGradientBrush(bounds, 
                    this.GradientBegin, this.GradientEnd, 
                    LinearGradientMode.Vertical))
                {
                    Blend blend = new Blend();
                    blend.Positions = new float[] { 0f, .4f, .6f, 1f };
                    blend.Factors = new float[] { 0f, .3f, .6f, .8f };
                    shadowBrush.Blend = blend;
                    g.FillPath(shadowBrush, fillPath);
                }
            }
        }

        private void DrawCaption(Graphics g, Rectangle bounds)
        {
            if (!string.IsNullOrEmpty(this.Caption))
            {
                using (StringFormat sF = new StringFormat())
                {
                    int vOffset;
                    int hOffset;

                    if (this.ItemImage != null)
                    {
                        if (!string.IsNullOrEmpty(this.Title))
                        {
                            vOffset = 4;
                            hOffset = (this.ItemImage.Size.Height / 2) + (_fntTitleFont.Height);
                        }
                        else
                        {
                            vOffset = this.ItemImage.Size.Width + 4;
                            hOffset = 5;
                        }
                    }
                    else if (!string.IsNullOrEmpty(this.Title))
                    {
                        vOffset = 4;
                        hOffset = (_fntTitleFont.Height + 8);
                    }
                    else
                    {
                        vOffset = 4;
                        hOffset = 4;
                    }
                    sF.Alignment = StringAlignment.Near;
                    sF.LineAlignment = StringAlignment.Near;
                    if (this.TextRightToLeft)
                        sF.FormatFlags = StringFormatFlags.DirectionRightToLeft;
                    using (Brush captionBrush = new SolidBrush(this.ForeColor))
                        g.DrawString(this.Caption, _fntCaptionFont, captionBrush, new RectangleF(vOffset, hOffset, this.TipBounds.Width - vOffset, this.TipBounds.Height - hOffset), sF);
                }
            }
        }

        private void DrawDropShadow(Graphics g, Rectangle bounds, int depth, int opacity)
        {
            // offset shadow dimensions
            Rectangle shadowBounds = bounds;
            shadowBounds.Offset(depth, depth);

            // create a clipping region
            using (GraphicsPath clipPath = CreateRoundRectanglePath(g, bounds, 4))
            {
                // clip the interior
                using (Region region = new Region(clipPath))
                    g.SetClip(region, CombineMode.Exclude);
            }

            // create a graphics path
            using (GraphicsPath gp = CreateRoundRectanglePath(g, shadowBounds, 4))
            {
                // draw with a path brush
                using (PathGradientBrush borderBrush = new PathGradientBrush(gp))
                {
                    borderBrush.CenterColor = Color.FromArgb(opacity, Color.Black);
                    borderBrush.SurroundColors = new Color[] { Color.Transparent };
                    borderBrush.FocusScales = new PointF(.5f, .5f);
                    g.FillPath(borderBrush, gp);
                    g.ResetClip();
                }
            }
        }

        private void DrawIcon(Graphics g, Rectangle bounds)
        {
            if (this.ItemImage != null)
                g.DrawImage(this.ItemImage, new Rectangle(6, 4, this.ItemImage.Width, this.ItemImage.Height));
        }

        private void DrawTitle(Graphics g, Rectangle bounds)
        {
            if (!string.IsNullOrEmpty(this.Title))
            {
                using (StringFormat sF = new StringFormat())
                {
                    int vOffset;
                    int hOffset;

                    if (this.ItemImage != null)
                    {
                        vOffset = this.ItemImage.Size.Width + 6;
                        hOffset = 4;
                    }
                    else
                    {
                        vOffset = 4;
                        hOffset = 4;
                    }
                    sF.Alignment = StringAlignment.Near;
                    sF.LineAlignment = StringAlignment.Near;
                    sF.HotkeyPrefix = System.Drawing.Text.HotkeyPrefix.None;
                    sF.FormatFlags = StringFormatFlags.NoWrap;

                    if (this.TextRightToLeft)
                        sF.FormatFlags = StringFormatFlags.DirectionRightToLeft;
                    using (Brush titleBrush = new SolidBrush(this.ForeColor))
                        g.DrawString(this.Title, _fntTitleFont, titleBrush, new PointF(vOffset, hOffset), sF);
                }
            }
        }
        #endregion

        #region Helpers
        private Rectangle CalculateSize()
        {
            SizeF textSize = new SizeF();
            Rectangle bounds = new Rectangle();

            if (this.ItemImage != null && !string.IsNullOrEmpty(this.Title))
            {
                // image size
                bounds.Height = this.ItemImage.Size.Height + 4;
                bounds.Width = this.ItemImage.Size.Width + 4;
                // title size
                textSize = CalcTextSize(this.Title, _fntTitleFont, 0);
                bounds.Height = Math.Max((int)textSize.Height + 4, bounds.Height);
                bounds.Width += (int)textSize.Width + 4;
                // caption size
                textSize = CalcTextSize(this.Caption, _fntCaptionFont, 150);
                bounds.Height += (int)textSize.Height + 4;
                bounds.Width = Math.Max((int)textSize.Width + 4, bounds.Width);
            }
            else if (this.ItemImage != null && string.IsNullOrEmpty(this.Title))
            {
                // image size
                bounds.Height = this.ItemImage.Size.Height + 4;
                bounds.Width = this.ItemImage.Size.Width + 4;
                // caption size
                textSize = CalcTextSize(this.Caption, _fntCaptionFont, 150);
                bounds.Height = Math.Max((int)textSize.Height + 4, bounds.Height);
                bounds.Width += (int)textSize.Width + 8;
            }
            else if (!string.IsNullOrEmpty(this.Title))
            {
                // title size
                textSize = CalcTextSize(this.Title, _fntTitleFont, 0);
                bounds.Height = (int)textSize.Height + 4;
                bounds.Width = (int)textSize.Width + 4;
                // caption size
                textSize = CalcTextSize(this.Caption, _fntCaptionFont, 150);
                bounds.Height += (int)textSize.Height + 4;
                bounds.Width = Math.Max((int)textSize.Width + 4, bounds.Width);
            }
            else
            {
                // caption size
                textSize = CalcTextSize(this.Caption, _fntCaptionFont, 150);
                bounds.Height = (int)textSize.Height + 4;
                bounds.Width = (int)textSize.Width + 8;
            }
            bounds.Inflate(2, 2);
            return bounds;
        }

        private SizeF CalcTextSize(string text, Font font, int width)
        {
            SizeF sF = new SizeF();
            using (Graphics g = Graphics.FromHwnd(_hTipWnd))
            {
                if (width > 0)
                    sF = g.MeasureString(text, font, width);
                else
                    sF = g.MeasureString(text, font);
            }
            return sF;
        }

        private void CopyBackground(Graphics g)
        {
            RECT windowRect = new RECT();
            GetWindowRect(_hTipWnd, ref windowRect);
            g.CopyFromScreen(windowRect.Left, windowRect.Top, 0, 0, new Size(windowRect.Right - windowRect.Left, windowRect.Bottom - windowRect.Top), CopyPixelOperation.SourceCopy);
        }

        private GraphicsPath CreateRoundRectanglePath(Graphics g, Rectangle bounds, float radius)
        {
            // create a path
            GraphicsPath pathBounds = new GraphicsPath();
            // arc top left
            pathBounds.AddArc(bounds.Left, bounds.Top, radius, radius, 180, 90);
            // line top
            pathBounds.AddLine(bounds.Left + radius, bounds.Top, bounds.Right - radius, bounds.Top);
            // arc top right
            pathBounds.AddArc(bounds.Right - radius, bounds.Top, radius, radius, 270, 90);
            // line right
            pathBounds.AddLine(bounds.Right, bounds.Top + radius, bounds.Right, bounds.Bottom - radius);
            // arc bottom right
            pathBounds.AddArc(bounds.Right - radius, bounds.Bottom - radius, radius, radius, 0, 90);
            // line bottom
            pathBounds.AddLine(bounds.Right - radius, bounds.Bottom, bounds.Left + radius, bounds.Bottom);
            // arc bottom left
            pathBounds.AddArc(bounds.Left, bounds.Bottom - radius, radius, radius, 90, 90);
            // line left
            pathBounds.AddLine(bounds.Left, bounds.Bottom - radius, bounds.Left, bounds.Top + radius);
            pathBounds.CloseFigure();
            return pathBounds;
        }

        private void PositionWindow()
        {
            if (_hTipWnd != IntPtr.Zero)
            {
                // offset with screen position
                RECT windowRect = new RECT();
                GetWindowRect(_hParentWnd, ref windowRect);
                windowRect.Left += this.TipBounds.X;
                windowRect.Top += this.TipBounds.Y;
                // position the window
                SetWindowPos(_hTipWnd, HWND_TOPMOST, windowRect.Left, windowRect.Top, this.TipBounds.Width, this.TipBounds.Height, SWP_SHOWWINDOW | SWP_NOACTIVATE);
            }
        }

        private void ShowWindow(bool show)
        {
            if (show)
                SetWindowPos(_hTipWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
            else
                SetWindowPos(_hTipWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_HIDEWINDOW | SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
        }
        #endregion
        #endregion

        #region WndProc
        protected override void WndProc(ref Message m)
        {
            switch (m.Msg)
            {
                case WM_TIMER:
                    _iTimerTick++;
                    if (_iTimerTick > (this.DelayTime / 100))
                    {
                        if (!_bTipShowing)
                        {
                            _bTipShowing = true;
                            Draw();
                        }
                    }
                    if (_iTimerTick > ((this.DelayTime + this.VisibleTime) / 100))
                        Stop();
                    base.WndProc(ref m);
                    break;

                default:
                    base.WndProc(ref m);
                    break;
            }
        }
        #endregion

        #region GraphicsMode
        internal class GraphicsMode : IDisposable
        {
            #region Instance Fields
            private Graphics _graphicCopy;
            private SmoothingMode _oldMode;
            #endregion

            #region Identity
            /// <summary>
            /// Initialize a new instance of the class.
            /// </summary>
            /// <param name="g">Graphics instance.</param>
            /// <param name="mode">Desired Smoothing mode.</param>
            public GraphicsMode(Graphics g, SmoothingMode mode)
            {
                _graphicCopy = g;
                _oldMode = _graphicCopy.SmoothingMode;
                _graphicCopy.SmoothingMode = mode;
            }

            /// <summary>
            /// Revert the SmoothingMode to original setting.
            /// </summary>
            public void Dispose()
            {
                _graphicCopy.SmoothingMode = _oldMode;
            }
            #endregion
        }
        #endregion

        #region StoreDc
        /// <summary>DC buffer class</summary>
        private class cStoreDc : IDisposable
        {
            #region API
            [DllImport("gdi32.dll")]
            private static extern IntPtr CreateDCA([MarshalAs(UnmanagedType.LPStr)]string lpszDriver, [MarshalAs(UnmanagedType.LPStr)]string lpszDevice, [MarshalAs(UnmanagedType.LPStr)]string lpszOutput, int lpInitData);

            [DllImport("gdi32.dll")]
            private static extern IntPtr CreateDCW([MarshalAs(UnmanagedType.LPWStr)]string lpszDriver, [MarshalAs(UnmanagedType.LPWStr)]string lpszDevice, [MarshalAs(UnmanagedType.LPWStr)]string lpszOutput, int lpInitData);

            [DllImport("gdi32.dll")]
            private static extern IntPtr CreateDC(string lpszDriver, string lpszDevice, string lpszOutput, int lpInitData);

            [DllImport("gdi32.dll")]
            private static extern IntPtr CreateCompatibleDC(IntPtr hdc);

            [DllImport("gdi32.dll")]
            private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

            [DllImport("gdi32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool DeleteDC(IntPtr hdc);

            [DllImport("gdi32.dll", ExactSpelling = true, PreserveSig = true)]
            private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

            [DllImport("gdi32.dll")]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool DeleteObject(IntPtr hObject);
            #endregion

            #region Fields
            private int _Height = 0;
            private int _Width = 0;
            private IntPtr _Hdc = IntPtr.Zero;
            private IntPtr _Bmp = IntPtr.Zero;
            private IntPtr _BmpOld = IntPtr.Zero;
            #endregion

            #region Methods
            public IntPtr Hdc
            {
                get { return _Hdc; }
            }

            public IntPtr HBmp
            {
                get { return _Bmp; }
            }

            public int Height
            {
                get { return _Height; }
                set
                {
                    if (_Height != value)
                    {
                        _Height = value;
                        ImageCreate(_Width, _Height);
                    }
                }
            }

            public int Width
            {
                get { return _Width; }
                set
                {
                    if (_Width != value)
                    {
                        _Width = value;
                        ImageCreate(_Width, _Height);
                    }
                }
            }

            public void SelectImage(Bitmap image)
            {
                if (Hdc != IntPtr.Zero && image != null)
                    SelectObject(Hdc, image.GetHbitmap());
            }

            private void ImageCreate(int Width, int Height)
            {
                IntPtr pHdc = IntPtr.Zero;

                ImageDestroy();
                pHdc = CreateDCA("DISPLAY", "", "", 0);
                _Hdc = CreateCompatibleDC(pHdc);
                _Bmp = CreateCompatibleBitmap(pHdc, _Width, _Height);
                _BmpOld = SelectObject(_Hdc, _Bmp);
                if (_BmpOld == IntPtr.Zero)
                {
                    ImageDestroy();
                }
                else
                {
                    _Width = Width;
                    _Height = Height;
                }
                DeleteDC(pHdc);
                pHdc = IntPtr.Zero;
            }

            private void ImageDestroy()
            {
                if (_BmpOld != IntPtr.Zero)
                {
                    SelectObject(_Hdc, _BmpOld);
                    _BmpOld = IntPtr.Zero;
                }
                if (_Bmp != IntPtr.Zero)
                {
                    DeleteObject(_Bmp);
                    _Bmp = IntPtr.Zero;
                }
                if (_Hdc != IntPtr.Zero)
                {
                    DeleteDC(_Hdc);
                    _Hdc = IntPtr.Zero;
                }
            }

            public void Dispose()
            {
                ImageDestroy();
            }
            #endregion
        }
        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Network Administrator vtdev.com
Canada Canada
Network and programming specialist. Started in C, and have learned about 14 languages since then. Cisco programmer, and lately writing a lot of C# and WPF code, (learning Java too). If I can dream it up, I can probably put it to code. My software company, (VTDev), is on the verge of releasing a couple of very cool things.. keep you posted.

Comments and Discussions