private void DoLayout(IWin32Window window, Control content, StemPosition stemPosition, ref Rectangle toolTipBounds)
            {
                int bubbleSize   = Win32.SendMessage(Handle, Win32.TTM_GETBUBBLESIZE, 0, ref _toolInfo);
                int bubbleWidth  = bubbleSize & 0xFFFF;
                int bubbleHeight = bubbleSize >> 16;

                // centre our content on the bubble-area of the tooltip
                content.Left = (bubbleWidth - content.Width) / 2;

                if (StemPosition.BottomLeft == stemPosition || StemPosition.BottomCentre == stemPosition || StemPosition.BottomRight == stemPosition)
                {
                    // stem is below the bubble
                    content.Top = (bubbleHeight - content.Height) / 2;
                }
                else
                {
                    // stem is on top of the bubble
                    int bubbleOffset = toolTipBounds.Height - bubbleHeight;
                    content.Top = (bubbleHeight - content.Height) / 2 + bubbleOffset;
                }

                _contentPanel        = new ContentPanel(Handle);
                _contentPanel.Width  = toolTipBounds.Width;
                _contentPanel.Height = toolTipBounds.Height;
                _contentPanel.Controls.Add(content);

                Win32.SetWindowPos(Handle, Win32.HWND_TOPMOST, toolTipBounds.X, toolTipBounds.Y, 0, 0, Win32.SWP_NOACTIVATE | Win32.SWP_NOSIZE | Win32.SWP_NOOWNERZORDER);
            }
        /// <summary>
        /// Sets the ToolTip content associated with the specified control, and displays the <see cref="InteractiveToolTip"/> at the specified relative position.
        /// </summary>
        /// <param name="content">The content to be displayed in the <see cref="InteractiveToolTip"/>.</param>
        /// <param name="window">The <see cref="T:System.Windows.Forms.Control"/> to display the <see cref="InteractiveToolTip"/> for.</param>
        /// <param name="x">The horizontal offset, in pixels, relative to the upper-left corner of the associated control window, to display the <see cref="InteractiveToolTip"/>.</param>
        /// <param name="y">The vertical offset, in pixels, relative to the upper-left corner of the associated control window, to display the <see cref="InteractiveToolTip"/>.</param>
        /// <param name="stemPosition">The desired position for the stem of the balloon.</param>
        /// <param name="duration">The time in milliseconds for which the <see cref="InteractiveToolTip"/> should be displayed, or zero for indefinite display.</param>
        /// <remarks>
        /// <para>
        /// The <see cref="InteractiveToolTip"/> is displayed until either one of the <b>Show</b> methods is called to display another <see cref="InteractiveToolTip"/>,
        /// or <see cref="Hide"/> is called, or the specified <paramref name="duration"/> is exceeded. It will be drawn with the stem positioned at the specified part
        /// of the balloon, tip located at the specified position relative to the top-left corner of <paramref name="window"/>. If there is insufficient space on the
        /// display for this, the stem and balloon may be repositioned to accomodate this.
        /// </para>
        /// <para>
        /// If the mouse is moved over the <see cref="InteractiveToolTip"/>, the duration timer will be halted. It will resume - with its original value - when the mouse
        /// leaves the <see cref="InteractiveToolTip"/> again.
        /// </para>
        /// </remarks>
        public void Show(Control content, IWin32Window window, int x, int y, StemPosition stemPosition, int duration)
        {
            if (null == content || null == window)
            {
                throw new ArgumentNullException();
            }

            Hide();
            _currentToolTip = new ToolTipWindow(content, window, x, y, stemPosition, UseAnimation, UseFading);

            if (duration > 0)
            {
                _currentToolTip.MouseEnter += delegate(object sender, EventArgs e)
                {
                    _durationTimer.Stop();
                };

                _currentToolTip.MouseLeave += delegate(object sender, EventArgs e)
                {
                    if (duration > 0)
                    {
                        _durationTimer.Start();
                    }
                };
                _currentToolTip.MouseDown += delegate(object sender, EventArgs e) {
                    if (duration > 0)
                    {
                        duration = 1;
                    }
                    _durationTimer.Start();
                };

                _durationTimer.Interval = duration;
                _durationTimer.Start();
            }

            if (null != ToolTipShown)
            {
                ToolTipShown(this, new InteractiveToolTipEventArgs(window));
            }
        }
 /// <summary>
 /// Sets the ToolTip content associated with the specified control, and displays the <see cref="InteractiveToolTip"/> at the specified relative position.
 /// </summary>
 /// <param name="content">The content to be displayed in the <see cref="InteractiveToolTip"/>.</param>
 /// <param name="window">The <see cref="T:System.Windows.Forms.Control"/> to display the <see cref="InteractiveToolTip"/> for.</param>
 /// <param name="location">A <see cref="T:System.Drawing.Point"/> containing the offset, in pixels, relative to the upper-left corner of the associated control window, to display the <see cref="InteractiveToolTip"/>.</param>
 /// <param name="stemPosition">The desired position for the stem of the balloon.</param>
 /// <param name="duration">The time in milliseconds for which the <see cref="InteractiveToolTip"/> should be displayed, or zero for indefinite display.</param>
 /// <remarks>
 /// <para>
 /// The <see cref="InteractiveToolTip"/> is displayed until either one of the <b>Show</b> methods is called to display another <see cref="InteractiveToolTip"/>,
 /// or <see cref="Hide"/> is called, or the specified <paramref name="duration"/> is exceeded. It will be drawn with the stem positioned at the specified part
 /// of the balloon, tip located at the specified position relative to the top-left corner of <paramref name="window"/>. If there is insufficient space on the
 /// display for this, the stem and balloon may be repositioned to accomodate this.
 /// </para>
 /// <para>
 /// If the mouse is moved over the <see cref="InteractiveToolTip"/>, the duration timer will be halted. It will resume - with its original value - when the mouse
 /// leaves the <see cref="InteractiveToolTip"/> again.
 /// </para>
 /// </remarks>
 public void Show(Control content, IWin32Window window, Point location, StemPosition stemPosition, int duration)
 {
     Show(content, window, location.X, location.Y, stemPosition, duration);
 }
 /// <summary>
 /// Sets the ToolTip content associated with the specified control, and displays the <see cref="InteractiveToolTip"/> with its stem in the specified position.
 /// </summary>
 /// <param name="content">The content to be displayed in the <see cref="InteractiveToolTip"/>.</param>
 /// <param name="window">The <see cref="T:System.Windows.Forms.Control"/> to display the <see cref="InteractiveToolTip"/> for.</param>
 /// <param name="stemPosition">The desired position for the stem of the balloon.</param>
 /// <remarks>
 /// <para>
 /// The <see cref="InteractiveToolTip"/> is displayed until either one of the <b>Show</b> methods is called to display another <see cref="InteractiveToolTip"/>,
 /// or <see cref="Hide"/> is called. It will be drawn with the stem positioned at the specified part of the balloon, tip located at either the top-left corner
 /// (if <paramref name="stemPosition"/> specifies the bottom edge) or the bottom-left corner (if <paramref name="stemPosition"/> specifies the top edge) of
 /// <paramref name="window"/>. If there is insufficient space on the display for this, the stem and balloon may be repositioned to accomodate this.
 /// </para>
 /// </remarks>
 public void Show(Control content, IWin32Window window, StemPosition stemPosition)
 {
     Show(content, window, 0, 0, stemPosition, 0);
 }
            public ToolTipWindow(Control content, IWin32Window window, int x, int y, StemPosition stemPosition, bool useAnimation, bool useFading)
            {
                Window = window;

                CreateParams createParams = new CreateParams();

                createParams.ClassName = Win32.TOOLTIPS_CLASS;
                createParams.Style     = Win32.TTS_ALWAYSTIP | Win32.TTS_BALLOON;

                if (!useAnimation)
                {
                    createParams.Style |= Win32.TTS_NOANIMATE;
                }

                if (!useFading)
                {
                    createParams.Style |= Win32.TTS_NOFADE;
                }

                CreateHandle(createParams);

                // first, work out the actual stem-position: the supplied value is a hint, but may have to be changed if there isn't enough space to accomodate it
                string contentSpacing = GetSizingText(content);

                // this is where the caller would like us to be
                Rectangle toolTipBounds = CalculateToolTipLocation(contentSpacing, Window, x, y, stemPosition);
                Screen    currentScreen = Screen.FromHandle(Window.Handle);
                Rectangle screenBounds  = currentScreen.WorkingArea;

                stemPosition = AdjustStemPosition(stemPosition, ref toolTipBounds, ref screenBounds);

                // and this is where we'll actually end up
                toolTipBounds   = CalculateToolTipLocation(contentSpacing, Window, x, y, stemPosition);
                toolTipBounds.X = Math.Max(0, toolTipBounds.X);
                toolTipBounds.Y = Math.Max(0, toolTipBounds.Y);

                // build the tooltip window
                _toolInfo = CreateTool(contentSpacing, Window, stemPosition);

                // initial position to force the stem into the correct orientation
                int initialX = screenBounds.X;
                int initialY = screenBounds.Y;

                if (StemPosition.TopLeft == stemPosition || StemPosition.BottomLeft == stemPosition)
                {
                    initialX += StemInset;
                }
                else if (StemPosition.TopCentre == stemPosition || StemPosition.BottomCentre == stemPosition)
                {
                    initialX += screenBounds.Width / 2;
                }
                else
                {
                    initialX += screenBounds.Width - StemInset;
                }

                if (StemPosition.BottomLeft == stemPosition || StemPosition.BottomCentre == stemPosition || StemPosition.BottomRight == stemPosition)
                {
                    initialY += screenBounds.Height;
                }

                Win32.SendMessage(Handle, Win32.TTM_TRACKPOSITION, 0, (initialY << 16) | initialX);

                // and finally display it
                Win32.SendMessage(Handle, Win32.TTM_TRACKACTIVATE, 1, ref _toolInfo);
                DoLayout(Window, content, stemPosition, ref toolTipBounds);
                _contentPanel.MouseDown += delegate(object sender, MouseEventArgs e) {
                    if (null != MouseDown && _mouseOverToolTip)
                    {
                        _mouseOverToolTip = true;
                        MouseDown(this, e);
                    }
                };
                _contentPanel.MouseEnter += delegate(object sender, EventArgs e)
                {
                    if (null != MouseEnter && !_mouseOverToolTip)
                    {
                        _mouseOverToolTip = true;
                        MouseEnter(this, e);
                    }
                };


                _contentPanel.MouseLeave += delegate(object sender, EventArgs e)
                {
                    // only send the event if the mouse has actually left the balloon and not simply moved from _contentPanel to the caller-supplied content
                    if (null != MouseLeave && _mouseOverToolTip && null == _contentPanel.GetChildAtPoint(_contentPanel.PointToClient(Control.MousePosition)))
                    {
                        _mouseOverToolTip = false;
                        MouseLeave(this, e);
                    }
                };
            }
            private Rectangle CalculateToolTipLocation(string contentSpacing, IWin32Window window, int x, int y, StemPosition stemPosition)
            {
                Rectangle toolTipBounds = new Rectangle();
                Size      toolTipSize   = GetToolTipWindowSize(contentSpacing);

                Win32.RECT windowBounds = new Win32.RECT();

                Win32.GetWindowRect(window.Handle, ref windowBounds);
                x += windowBounds.left;

                if (StemPosition.TopLeft == stemPosition || StemPosition.BottomLeft == stemPosition)
                {
                    toolTipBounds.X = x - StemInset;
                }
                else if (StemPosition.TopCentre == stemPosition || StemPosition.BottomCentre == stemPosition)
                {
                    toolTipBounds.X = x - (toolTipSize.Width / 2);
                }
                else
                {
                    toolTipBounds.X = x - toolTipSize.Width + StemInset;
                }

                if (StemPosition.TopLeft == stemPosition || StemPosition.TopCentre == stemPosition || StemPosition.TopRight == stemPosition)
                {
                    toolTipBounds.Y = windowBounds.bottom - y;
                }
                else
                {
                    toolTipBounds.Y = y + windowBounds.top - toolTipSize.Height;
                }

                toolTipBounds.Width  = toolTipSize.Width;
                toolTipBounds.Height = toolTipSize.Height;

                return(toolTipBounds);
            }
            private StemPosition AdjustStemPosition(StemPosition stemPosition, ref Rectangle toolTipBounds, ref Rectangle screenBounds)
            {
                if (toolTipBounds.Left < screenBounds.Left)
                {
                    // the window is too close to the left edge of the display
                    if (StemPosition.TopCentre == stemPosition || StemPosition.TopRight == stemPosition)
                    {
                        stemPosition = StemPosition.TopLeft;
                    }
                    else if (StemPosition.BottomCentre == stemPosition || StemPosition.BottomRight == stemPosition)
                    {
                        stemPosition = StemPosition.BottomLeft;
                    }
                }
                else if (toolTipBounds.Right > screenBounds.Right)
                {
                    // the window is too close to the right edge of the display
                    if (StemPosition.TopCentre == stemPosition || StemPosition.TopLeft == stemPosition)
                    {
                        stemPosition = StemPosition.TopRight;
                    }
                    else if (StemPosition.BottomCentre == stemPosition || StemPosition.BottomLeft == stemPosition)
                    {
                        stemPosition = StemPosition.BottomRight;
                    }
                }

                if (toolTipBounds.Top < screenBounds.Top)
                {
                    // the window is too close to the top edge of the display
                    switch (stemPosition)
                    {
                    case StemPosition.BottomLeft:
                        stemPosition = StemPosition.TopLeft;
                        break;

                    case StemPosition.BottomCentre:
                        stemPosition = StemPosition.TopCentre;
                        break;

                    case StemPosition.BottomRight:
                        stemPosition = StemPosition.TopRight;
                        break;
                    }
                }
                else if (toolTipBounds.Bottom > screenBounds.Bottom)
                {
                    // the window is too close to the bottom edge of the display
                    switch (stemPosition)
                    {
                    case StemPosition.TopLeft:
                        stemPosition = StemPosition.BottomLeft;
                        break;

                    case StemPosition.TopCentre:
                        stemPosition = StemPosition.BottomCentre;
                        break;

                    case StemPosition.TopRight:
                        stemPosition = StemPosition.BottomRight;
                        break;
                    }
                }

                return(stemPosition);
            }
            private Win32.TOOLINFO CreateTool(string contentSpacing, IWin32Window window, StemPosition stemPosition)
            {
                Win32.TOOLINFO ti = new Win32.TOOLINFO();
                ti.cbSize   = Marshal.SizeOf(ti);
                ti.uFlags   = Win32.TTF_IDISHWND | Win32.TTF_TRACK | Win32.TTF_TRANSPARENT;
                ti.uId      = window.Handle;
                ti.hwnd     = window.Handle;
                ti.lpszText = contentSpacing;

                if (StemPosition.BottomCentre == stemPosition || StemPosition.TopCentre == stemPosition)
                {
                    ti.uFlags |= Win32.TTF_CENTERTIP;
                }

                if (0 == Win32.SendMessage(Handle, Win32.TTM_ADDTOOL, 0, ref ti))
                {
                    throw new Exception();
                }

                // enable multi-line text-layout
                Win32.SendMessage(Handle, Win32.TTM_SETMAXTIPWIDTH, 0, SystemInformation.MaxWindowTrackSize.Width);

                return(ti);
            }