Imports System.ComponentModel.Design.Serialization
Imports System.ComponentModel
Imports System.Drawing.Design
Imports System.Reflection
Imports System.Resources
Imports System.IO
Imports System.Xml
Imports System.Text
Imports System.Text.RegularExpressions

Imports Stax.AppFramework

Namespace UI
    <ProvideProperty("TipText", GetType(Control)), _
    ProvideProperty("TipName", GetType(Control))> _
    Public Class TipProvider
        Inherits Component
        Implements IExtenderProvider, ISupportInitialize, IDisposable

        Public Sub New(ByVal cont As IContainer)
            cont.Add(Me)
        End Sub

        Private TipNamesValue As New Dictionary(Of Control, String)

        Public ReadOnly Property TipNames() As Dictionary(Of Control, String)
            Get
                Return TipNamesValue
            End Get
        End Property

        Private TipTextsValue As New Dictionary(Of Control, String)

        Public ReadOnly Property TipTexts() As Dictionary(Of Control, String)
            Get
                Return TipTextsValue
            End Get
        End Property

        Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            MyBase.Dispose(disposing)
        End Sub

        Public Function CanExtend(ByVal obj As Object) As Boolean Implements IExtenderProvider.CanExtend
            Return TypeOf obj Is Control
        End Function

        <Category("TipProvider"), DefaultValue("")> _
        Public Function GetTipName(ByVal c As Control) As String
            If TipNames.ContainsKey(c) Then
                Return TipNames(c)
            End If

            Return ""
        End Function

        Public Sub SetTipName(ByVal c As Control, ByVal value As String)
            TipNames(c) = value
        End Sub

        <Category("TipProvider"), DefaultValue(""), _
        Editor(GetType(StringEditor), GetType(UITypeEditor))> _
        Public Function GetTipText(ByVal c As Control) As String
            If TipTexts.ContainsKey(c) Then
                Return TipTexts(c)
            End If

            Return ""
        End Function

        Public Sub SetTip(ByVal c As Control, ByVal name As String, ByVal value As String)
            SetTipName(c, name)
            SetTipText(c, value)
            AddHandlers(c)
        End Sub

        Public Sub SetTipText(ByVal c As Control, ByVal value As String)
            TipTexts(c) = value
        End Sub

        Public Sub BeginInit() Implements ISupportInitialize.BeginInit
        End Sub

        Public Sub EndInit() Implements ISupportInitialize.EndInit
            For Each i As Control In TipTexts.Keys
                AddHandlers(i)
            Next
        End Sub

        Private Sub AddHandlers(ByVal c As Control)
            AddHandler c.HelpRequested, AddressOf TipHelpRequested
            AddHandler c.MouseDown, AddressOf TipMouseDown
        End Sub

        Private Sub TipHelpRequested(ByVal sender As Object, ByVal e As HelpEventArgs)
            Dim c As Control = CType(sender, Control)

            If Not HasContextMenu(c) Then
                If Not StaxApplication.StringList Is Nothing Then
                    Dim pos As Point = Cursor.Position
                    Msg.Show("Tooltips can be invoked directly with a right-click.", "Useability Tip", StaxApplication.StringList)
                    Cursor.Position = pos
                End If
            End If

            ShowPopup(c)
        End Sub

        Private Sub TipMouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
            If e.Button = MouseButtons.Right AndAlso _
                Not HasContextMenu(CType(sender, Control)) Then

                ShowPopup(CType(sender, Control))
            End If
        End Sub

        Private Sub ShowPopup(ByVal c As Control)
            Dim t As StringPair = GetTip(c)

            Dim p As New PopUp

            If HelpDocument.MustConvert(t.Value) Then
                p.Text = HelpDocument.ConvertMarkup(t.Value, True)
            Else
                p.Text = t.Value
            End If

            If t.Name = "" Then
                p.Title = c.Text

                If p.Title.StartsWith(" "c) Then
                    p.Title = p.Title.TrimStart(" "c)
                End If

                If p.Title.EndsWith("...") Then
                    p.Title = p.Title.TrimEnd("."c)
                End If

                If p.Title.EndsWith(":") Then
                    p.Title = p.Title.TrimEnd(":"c)
                End If
            Else
                p.Title = t.Name
            End If

            p.Show()
        End Sub

        Private Function GetTip(ByVal c As Control) As StringPair
            Dim t As New StringPair

            If TipNames.ContainsKey(c) Then
                t.Name = TipNames(c)
            End If

            If TipTexts.ContainsKey(c) Then
                t.Value = TipTexts(c)
            End If

            Return t
        End Function

        Private Function HasContextMenu(ByVal c As Control) As Boolean
            If TypeOf c Is TextBox Then Return True
            If TypeOf c Is RichTextBox Then Return True
            If TypeOf c Is NumericUpDown Then Return True

            If TypeOf c Is ComboBox AndAlso CType(c, ComboBox).DropDownStyle <> ComboBoxStyle.DropDownList Then
                Return True
            End If

            Dim pi As PropertyInfo = c.GetType.GetProperty("ContextMenuStrip")

            If Not pi Is Nothing Then
                If Not pi.GetValue(c, Nothing) Is Nothing Then
                    Return True
                End If
            End If

            Return False
        End Function

        Public Function GetTips() As StringPairList
            Dim list As New StringPairList
            Dim names As New List(Of String) 'to ignore double tips

            For Each i As Control In TipTexts.Keys
                If Not i.IsDisposed Then 'controls in background tabs are not visible!
                    Dim t As New StringPair
                    t.Value = TipTexts(i)

                    If TipNames.ContainsKey(i) Then
                        t.Name = TipNames(i)
                    Else
                        If i.Text.StartsWith(" ") Then
                            t.Name = i.Text.TrimStart(" "c)
                        Else
                            t.Name = i.Text
                        End If
                    End If

                    If Not names.Contains(t.Name) Then
                        names.Add(t.Name)

                        If t.Name.EndsWith("...") Then
                            t.Name = t.Name.TrimEnd("."c)
                        End If

                        If t.Name.EndsWith(":") Then
                            t.Name = t.Name.TrimEnd(":"c)
                        End If

                        list.Add(t)
                    End If
                End If
            Next

            list.Sort()

            Return list
        End Function
    End Class

    Public Class TipTarget
        Private TitleControl As Control
        Private DescriptionControl As Control

        Public Sub New(ByVal nameControl As Control, ByVal valueControl As Control)
            Me.TitleControl = nameControl
            Me.DescriptionControl = valueControl
        End Sub

        Public Property Title() As String
            Get
                If TitleControl Is Nothing Then
                    Return ""
                End If

                Return TitleControl.Text
            End Get
            Set(ByVal Value As String)
                If TitleControl IsNot Nothing Then
                    TitleControl.Text = Value
                End If
            End Set
        End Property

        Public Property Description() As String
            Get
                If DescriptionControl Is Nothing Then
                    Return ""
                End If

                Return DescriptionControl.Text
            End Get
            Set(ByVal Value As String)
                If DescriptionControl IsNot Nothing Then
                    DescriptionControl.Text = Value
                End If
            End Set
        End Property

        Public Function GetTip() As StringPair
            Return New StringPair(Title, Description)
        End Function

        Public Sub SetTarget(ByVal t As StringPair)
            Title = t.Name
            Description = t.Value
        End Sub
    End Class

    Public Class PopUp
        Private TitleValue As String

        Public Property Title() As String
            Get
                Return TitleValue
            End Get
            Set(ByVal value As String)
                TitleValue = value
            End Set
        End Property

        Private TextValue As String

        Public Property Text() As String
            Get
                Return TextValue
            End Get
            Set(ByVal value As String)
                TextValue = value
            End Set
        End Property

        Private ImageValue As Image

        Public Property Image() As Image
            Get
                Return ImageValue
            End Get
            Set(ByVal value As Image)
                ImageValue = value
            End Set
        End Property

        Private CenterOwnerValue As Form

        Public Property CenterOwner() As Form
            Get
                Return CenterOwnerValue
            End Get
            Set(ByVal value As Form)
                CenterOwnerValue = value
            End Set
        End Property

        Public WriteOnly Property Tip() As StringPair
            Set(ByVal Value As StringPair)
                Title = Value.Name
                Text = Value.Value
            End Set
        End Property

        Public Sub Show()
            Dim f As New PopUpForm(Title, Text, Image, CenterOwner)
            f.Show()
        End Sub
    End Class
End Namespace