Imports System.Reflection
Imports System.ComponentModel
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms.VisualStyles

Imports Stax.Win32
Imports Stax.UI.Design

Namespace UI
    Public Class ExtendedTreeView
        Inherits TreeView

        Public Event CustomPaint(ByVal e As PaintEventArgs)

        Private AutoCollapsValue As Boolean

        <DefaultValue(False)> _
        Public Property AutoCollaps() As Boolean
            Get
                Return AutoCollapsValue
            End Get
            Set(ByVal Value As Boolean)
                AutoCollapsValue = Value
            End Set
        End Property

        Private AutoExpandValue As Boolean

        <DefaultValue(False)> _
        Public Property AutoExpand() As Boolean
            Get
                Return AutoExpandValue
            End Get
            Set(ByVal Value As Boolean)
                AutoExpandValue = Value
            End Set
        End Property

        Private SelectOnMouseDownValue As Boolean

        <DefaultValue(False)> _
        Public Property SelectOnMouseDown() As Boolean
            Get
                Return SelectOnMouseDownValue
            End Get
            Set(ByVal Value As Boolean)
                SelectOnMouseDownValue = Value
            End Set
        End Property

        Protected Overrides Sub OnAfterSelect(ByVal e As TreeViewEventArgs)
            If AutoCollaps Then
                Dim rootNode As TreeNode = e.Node

                While True
                    If rootNode.Parent Is Nothing Then
                        Exit While
                    Else
                        rootNode = rootNode.Parent
                    End If
                End While

                For Each i As TreeNode In Nodes
                    If Not i Is rootNode Then
                        i.Collapse()
                    End If
                Next
            End If

            If AutoExpand Then
                e.Node.ExpandAll()
            End If

            MyBase.OnAfterSelect(e)
        End Sub

        Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
            MyBase.OnMouseDown(e)

            If SelectOnMouseDown Then
                Dim n As TreeNode = GetNodeAt(PointToClient(Control.MousePosition))

                If Not n Is Nothing AndAlso n.Nodes.Count = 0 Then
                    SelectedNode = n
                End If
            End If
        End Sub

        Public Sub MoveSelectionLeft()
            Dim n As TreeNode = SelectedNode

            If Not n Is Nothing AndAlso Not n.Parent Is Nothing Then
                Dim parentParentNodes As TreeNodeCollection = GetParentParentNodes(n)
                Dim parentIndex As Integer = n.Parent.Index

                If Not parentParentNodes Is Nothing Then
                    n.Remove()
                    parentParentNodes.Insert(parentIndex + 1, n)
                    SelectedNode = n
                End If
            End If
        End Sub

        Public Sub MoveSelectionUp()
            Dim n As TreeNode = SelectedNode

            If Not n Is Nothing Then
                If n.Index = 0 Then
                    Dim parentParentNodes As TreeNodeCollection = GetParentParentNodes(n)

                    If Not parentParentNodes Is Nothing Then
                        Dim index As Integer = n.Parent.Index
                        n.Remove()
                        parentParentNodes.Insert(index, n)
                    End If
                Else
                    Dim index As Integer = n.Index
                    Dim parentNodes As TreeNodeCollection = GetParentNodes(n)
                    n.Remove()
                    parentNodes.Insert(index - 1, n)
                End If

                SelectedNode = n
            End If
        End Sub

        Public Sub MoveSelectionRight()
            Dim n As TreeNode = SelectedNode

            If Not n Is Nothing Then
                If n.Index > 0 Then
                    Dim previousNode As TreeNode = n.PrevNode
                    n.Remove()
                    previousNode.Nodes.Add(n)
                    SelectedNode = n
                End If
            End If
        End Sub

        Public Sub MoveSelectionDown()
            Dim n As TreeNode = SelectedNode

            If Not n Is Nothing Then
                If n.NextNode Is Nothing Then
                    Dim parentParentNodes As TreeNodeCollection = GetParentParentNodes(n)

                    If Not parentParentNodes Is Nothing Then
                        Dim index As Integer = n.Parent.Index
                        n.Remove()
                        parentParentNodes.Insert(index + 1, n)
                    End If
                Else
                    Dim index As Integer = n.Index
                    Dim parentNodes As TreeNodeCollection = GetParentNodes(n)
                    n.Remove()
                    parentNodes.Insert(index + 1, n)
                End If

                SelectedNode = n
            End If
        End Sub

        Private Function GetParentParentNodes(ByVal n As TreeNode) As TreeNodeCollection
            Dim parent As TreeNode = n.Parent

            If parent Is Nothing Then
                Return Nothing
            End If

            If parent.Parent Is Nothing Then
                Return Nodes
            Else
                Return parent.Parent.Nodes
            End If
        End Function

        Private Function GetParentNodes(ByVal n As TreeNode) As TreeNodeCollection
            Dim parent As TreeNode = n.Parent

            If parent Is Nothing Then
                Return Nodes
            Else
                Return parent.Nodes
            End If
        End Function
    End Class

    Public Class BorderlessButton
        Inherits ButtonBase

        Private IsMouseOver As Boolean
        Private IsMousePressed As Boolean

        Protected Overrides Sub OnPaint(ByVal pevent As PaintEventArgs)
            Dim g As Graphics = pevent.Graphics

            If IsMousePressed Then
                g.Clear(Color.LightGray)
            Else
                g.Clear(BackColor)
            End If

            If IsMouseOver Then
                ControlPaint.DrawBorder(g, ClientRectangle, Color.Black, ButtonBorderStyle.Solid)
            End If

            RaisePaintEvent(Nothing, pevent)
        End Sub

        Protected Overrides Sub OnMouseEnter(ByVal eventargs As EventArgs)
            IsMouseOver = True
            MyBase.OnMouseEnter(eventargs)
        End Sub

        Protected Overrides Sub OnMouseLeave(ByVal eventargs As EventArgs)
            IsMouseOver = False
            MyBase.OnMouseLeave(eventargs)
        End Sub

        Protected Overrides Sub OnMouseDown(ByVal mevent As MouseEventArgs)
            IsMousePressed = True
            MyBase.OnMouseDown(mevent)
        End Sub

        Protected Overrides Sub OnMouseUp(ByVal mevent As MouseEventArgs)
            IsMousePressed = False
            MyBase.OnMouseUp(mevent)
        End Sub

        Protected Overrides Sub OnLostFocus(ByVal e As EventArgs)
            IsMousePressed = False
            MyBase.OnLostFocus(e)
        End Sub

        Protected Overrides Sub OnClick(ByVal e As EventArgs)
            IsMousePressed = False
            IsMouseOver = False
            Refresh()
            MyBase.OnClick(e)
        End Sub
    End Class

    Public Class ExtendedToolStrip
        Inherits ToolStrip

        Private IsAppThemed As Boolean = VisualStyleInformation.IsEnabledByUser AndAlso Application.RenderWithVisualStyles

        Public Sub New()
            MyBase.New()
        End Sub

        Public Sub New(ByVal ParamArray items As ToolStripItem())
            MyBase.New(items)
        End Sub

        Private OverdrawLineValue As Boolean

        <DefaultValue(False), Description("Overdraws a weird grey line visible at the bottom in some configurations.")> _
        Public Property OverdrawLine() As Boolean
            Get
                Return OverdrawLineValue
            End Get
            Set(ByVal Value As Boolean)
                OverdrawLineValue = Value
                Invalidate()
            End Set
        End Property

        Private SingleAutoCheckedValue As Boolean

        <DefaultValue(False), Description("Only one button can be checked at the same time.")> _
        Public Property SingleAutoChecked() As Boolean
            Get
                Return SingleAutoCheckedValue
            End Get
            Set(ByVal Value As Boolean)
                SingleAutoCheckedValue = Value
            End Set
        End Property

        Private ShowControlBorderValue As Boolean

        <DefaultValue(False), Description("Shows a themed control border.")> _
        Public Property ShowControlBorder() As Boolean
            Get
                Return ShowControlBorderValue
            End Get
            Set(ByVal Value As Boolean)
                ShowControlBorderValue = Value
            End Set
        End Property

        Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
            MyBase.OnPaint(e)

            If OverdrawLine Then
                Dim b As New SolidBrush(BackColor)
                e.Graphics.FillRectangle(b, 0, Height - 2, Width, 2)
                b.Dispose()
            End If

            If ShowControlBorder AndAlso IsAppThemed Then
                ControlPaint.DrawBorder(e.Graphics, ClientRectangle, VisualStyleInformation.TextControlBorder, ButtonBorderStyle.Solid)
            End If
        End Sub

        Protected Overrides Sub OnItemClicked(ByVal e As ToolStripItemClickedEventArgs)
            If SingleAutoChecked Then
                For Each i As ToolStripItem In Items
                    If TypeOf i Is ToolStripButton Then
                        If i Is e.ClickedItem Then
                            CType(i, ToolStripButton).Checked = True
                        Else
                            CType(i, ToolStripButton).Checked = False
                        End If
                    End If
                Next
            End If

            MyBase.OnItemClicked(e)
        End Sub

        Protected Overrides ReadOnly Property CreateParams() As CreateParams
            Get
                Dim ret As CreateParams = MyBase.CreateParams

                If ShowControlBorder AndAlso Not IsAppThemed Then
                    ret.ExStyle = ret.ExStyle Or CInt(WS.EX_CLIENTEDGE)
                End If

                Return ret
            End Get
        End Property
    End Class

    Public Class ProxyControlContainer
        Inherits Panel

        Private ActiveControl As Type
        Private ChildControls As New Dictionary(Of Type, Control)

        Public Function ShowControl(ByVal t As Type, ByVal show As Boolean) As Control
            If Not ActiveControl Is t Then
                If Not ChildControls.ContainsKey(t) Then
                    Dim c As Control = CType(Activator.CreateInstance(t), Control)
                    c.Visible = show
                    ChildControls(t) = c
                    c.Dock = DockStyle.Fill
                    Controls.Add(c)
                End If

                If show Then
                    If Not ChildControls(t).Visible Then
                        ChildControls(t).Visible = True
                    End If

                    ChildControls(t).BringToFront()
                    ActiveControl = t
                End If
            End If

            Return ChildControls(t)
        End Function
    End Class

    <ToolboxItem(False)> _
Public Class DialogControl
        Inherits UserControl

        Private IsAppThemed As Boolean = VisualStyleInformation.IsEnabledByUser AndAlso Application.RenderWithVisualStyles

        Public Event FormClosingOK()

        Public Sub New()
            MyBase.New()
            Padding = New Padding(8)
            SetStyle(ControlStyles.CacheText, True)
        End Sub

        <Category("Appearance"), DefaultValue(""), Bindable(True), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), _
        Browsable(True), EditorBrowsable(EditorBrowsableState.Always)> _
        Public Overrides Property Text() As String
            Get
                Return MyBase.Text
            End Get
            Set(ByVal Value As String)
                If Value <> MyBase.Text Then
                    MyBase.Text = Value
                    Dim p As Padding = Padding

                    If Value = "" Then
                        p.Top = 3
                    Else
                        p.Top = Font.Height + 15
                    End If

                    If Not p = Padding Then
                        Padding = p
                    End If

                    Invalidate()
                End If
            End Set
        End Property

        Private TabPageStyleValue As Boolean

        <DefaultValue(False)> _
        Public Property UseVisualStyle() As Boolean
            Get
                Return TabPageStyleValue
            End Get
            Set(ByVal Value As Boolean)
                If Value <> TabPageStyleValue Then
                    TabPageStyleValue = Value

                    If Value Then
                        BackColor = Color.Transparent
                    End If

                    Invalidate()
                End If
            End Set
        End Property

        Private BackgroundRendererValue As VisualStyleRenderer

        Private ReadOnly Property BackgroundRenderer() As VisualStyleRenderer
            Get
                If BackgroundRendererValue Is Nothing AndAlso IsAppThemed AndAlso UseVisualStyle Then
                    Dim e As VisualStyleElement = VisualStyleElement.Tab.Pane.Normal
                    BackgroundRendererValue = New VisualStyleRenderer(e.ClassName, e.Part, 0)
                End If

                Return BackgroundRendererValue
            End Get
        End Property

        Protected Overrides Sub OnPaintBackground(ByVal e As PaintEventArgs)
            If Not BackgroundRenderer Is Nothing Then
                BackgroundRenderer.DrawBackground(e.Graphics, ClientRectangle)
            Else
                MyBase.OnPaintBackground(e)
            End If

            If Text <> "" Then
                Dim b As New SolidBrush(ForeColor)
                Dim f As Font = New Font(Font, FontStyle.Bold)
                e.Graphics.DrawString(Text, f, b, Padding.Left + 3, 6)
                b.Dispose()

                Dim size As SizeF = e.Graphics.MeasureString(Text, Font)

                If Not BackgroundRenderer Is Nothing Then
                    Dim rect As New Rectangle(Padding.Left + 3, Font.Height + 10, Width - Padding.Left - Padding.Right - 6, 1)
                    Dim p As New LinearGradientBrush(rect, SystemColors.ControlDarkDark, SystemColors.ControlLight, LinearGradientMode.Horizontal)
                    e.Graphics.FillRectangle(p, rect)
                    p.Dispose()
                Else
                    If Enabled Then
                        Dim p As New Pen(SystemColors.ControlDark)
                        e.Graphics.DrawLine(p, Padding.Left + 3, Font.Height + 10, Width - Padding.Right - 3, Font.Height + 10)
                        p.Dispose()

                        e.Graphics.DrawLine(Pens.White, Padding.Left + 3, Font.Height + 11, Width - Padding.Right - 3, Font.Height + 11)

                        Dim offset As Integer = 1
                        Dim visibleWidth As Integer = Width - 5
                    Else
                        Dim p As New Pen(SystemColors.InactiveBorder)
                        e.Graphics.DrawLine(p, Padding.Left + 3, Font.Height + 11, Width - Padding.Right - 3, Font.Height + 11)
                        p.Dispose()
                    End If
                End If
            End If
        End Sub

        Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs)
            MyBase.OnHandleCreated(e)

            If Not DesignMode Then
                AddHandler FindForm.FormClosing, AddressOf FormClosing
            End If
        End Sub

        Private Sub FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs)
            If DirectCast(sender, Form).DialogResult = DialogResult.OK Then
                OnFormClosingOK()
            End If
        End Sub

        Protected Overridable Sub OnFormClosingOK()
            RaiseEvent FormClosingOK()
        End Sub
    End Class

    <ToolboxItem(False)> _
    Public Class DialogPanel
        Inherits Panel

        Private IsAppThemed As Boolean = VisualStyleInformation.IsEnabledByUser AndAlso Application.RenderWithVisualStyles

        Public Event FormClosingOK()

        Public Sub New()
            MyBase.New()
            Padding = New Padding(8)
            SetStyle(ControlStyles.CacheText, True)
        End Sub

        <Category("Appearance"), DefaultValue(""), Bindable(True), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), _
        Browsable(True), EditorBrowsable(EditorBrowsableState.Always)> _
        Public Overrides Property Text() As String
            Get
                Return MyBase.Text
            End Get
            Set(ByVal Value As String)
                If Value <> MyBase.Text Then
                    MyBase.Text = Value
                    Dim p As Padding = Padding

                    If Value = "" Then
                        p.Top = 3
                    Else
                        p.Top = Font.Height + 15
                    End If

                    If Not p = Padding Then
                        Padding = p
                    End If

                    Invalidate()
                End If
            End Set
        End Property

        Private TabPageStyleValue As Boolean

        <DefaultValue(False)> _
        Public Property UseVisualStyle() As Boolean
            Get
                Return TabPageStyleValue
            End Get
            Set(ByVal Value As Boolean)
                If Value <> TabPageStyleValue Then
                    TabPageStyleValue = Value

                    If Value Then
                        BackColor = Color.Transparent
                    End If

                    Invalidate()
                End If
            End Set
        End Property

        Private BackgroundRendererValue As VisualStyleRenderer

        Private ReadOnly Property BackgroundRenderer() As VisualStyleRenderer
            Get
                If BackgroundRendererValue Is Nothing AndAlso IsAppThemed AndAlso UseVisualStyle Then
                    Dim e As VisualStyleElement = VisualStyleElement.Tab.Pane.Normal
                    BackgroundRendererValue = New VisualStyleRenderer(e.ClassName, e.Part, 0)
                End If

                Return BackgroundRendererValue
            End Get
        End Property

        Protected Overrides Sub OnPaintBackground(ByVal e As PaintEventArgs)
            If Not BackgroundRenderer Is Nothing Then
                BackgroundRenderer.DrawBackground(e.Graphics, ClientRectangle)
            Else
                MyBase.OnPaintBackground(e)
            End If

            If Text <> "" Then
                Dim b As New SolidBrush(ForeColor)
                Dim f As Font = New Font(Font, FontStyle.Bold)
                e.Graphics.DrawString(Text, f, b, Padding.Left + 3, 6)
                b.Dispose()

                Dim size As SizeF = e.Graphics.MeasureString(Text, Font)

                If Not BackgroundRenderer Is Nothing Then
                    Dim rect As New Rectangle(Padding.Left + 3, Font.Height + 10, Width - Padding.Left - Padding.Right - 6, 1)
                    Dim p As New LinearGradientBrush(rect, SystemColors.ControlDarkDark, SystemColors.ControlLight, LinearGradientMode.Horizontal)
                    e.Graphics.FillRectangle(p, rect)
                    p.Dispose()
                Else
                    If Enabled Then
                        Dim p As New Pen(SystemColors.ControlDark)
                        e.Graphics.DrawLine(p, Padding.Left + 3, Font.Height + 10, Width - Padding.Right - 3, Font.Height + 10)
                        p.Dispose()

                        e.Graphics.DrawLine(Pens.White, Padding.Left + 3, Font.Height + 11, Width - Padding.Right - 3, Font.Height + 11)

                        Dim offset As Integer = 1
                        Dim visibleWidth As Integer = Width - 5
                    Else
                        Dim p As New Pen(SystemColors.InactiveBorder)
                        e.Graphics.DrawLine(p, Padding.Left + 3, Font.Height + 11, Width - Padding.Right - 3, Font.Height + 11)
                        p.Dispose()
                    End If
                End If
            End If
        End Sub

        Protected Overrides Sub OnHandleCreated(ByVal e As EventArgs)
            MyBase.OnHandleCreated(e)

            If Not DesignMode Then
                AddHandler FindForm.FormClosing, AddressOf FormClosing
            End If
        End Sub

        Private Sub FormClosing(ByVal sender As Object, ByVal e As FormClosingEventArgs)
            If DirectCast(sender, Form).DialogResult = DialogResult.OK Then
                OnFormClosingOK()
            End If
        End Sub

        Protected Overridable Sub OnFormClosingOK()
            RaiseEvent FormClosingOK()
        End Sub
    End Class

    <DesignerAttribute(GetType(LineControlDesigner))> _
    Public Class LineControl
        Inherits UserControl

        Private IsGradientValue As Boolean

        <DefaultValue(False)> _
        Public Property IsGradient() As Boolean
            Get
                Return IsGradientValue
            End Get
            Set(ByVal Value As Boolean)
                IsGradientValue = Value
                Invalidate()
            End Set
        End Property

        <Category("Appearance"), DefaultValue(""), Bindable(True), _
        DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), _
        Browsable(True), EditorBrowsable(EditorBrowsableState.Always)> _
        Public Overrides Property Text() As String
            Get
                Return MyBase.Text
            End Get
            Set(ByVal value As String)
                MyBase.Text = value
            End Set
        End Property

        Protected Overrides Sub OnTextChanged(ByVal e As EventArgs)
            MyBase.OnTextChanged(e)

            If Text <> "" AndAlso Height < 16 Then
                Height = 16
            End If

            Invalidate()
        End Sub

        Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
            MyBase.OnPaint(e)

            If Width > Height Then
                Dim textSize As SizeF
                Dim g As Graphics = e.Graphics

                If Text <> "" Then
                    textSize = e.Graphics.MeasureString(Text, Font)

                    If Enabled Then
                        Dim brush As Brush = New SolidBrush(ForeColor)
                        g.DrawString(Text, Font, brush, 0, (Height - Font.Height) \ 2)
                        brush.Dispose()
                    Else
                        Dim brush As Brush = New SolidBrush(SystemColors.GrayText)
                        g.DrawString(Text, Font, brush, 0, (Height - Font.Height) \ 2)
                        brush.Dispose()
                    End If
                End If

                If Enabled Then
                    If IsGradient Then
                        Dim rect As New Rectangle(CInt(textSize.Width), Height \ 2, Width - CInt(textSize.Width), 1)
                        Dim p As New LinearGradientBrush(rect, SystemColors.ControlDarkDark, SystemColors.ControlLight, LinearGradientMode.Horizontal)
                        e.Graphics.FillRectangle(p, rect)
                        p.Dispose()
                    Else
                        Dim p As New Pen(SystemColors.ControlDark)
                        g.DrawLine(p, textSize.Width, Height \ 2, Width, Height \ 2)
                        p.Dispose()

                        g.DrawLine(Pens.White, textSize.Width, _
                             Height \ 2 + 1, Width, Height \ 2 + 1)
                    End If
                Else
                    Dim p As New Pen(SystemColors.InactiveBorder)
                    g.DrawLine(p, textSize.Width, Height \ 2, Width, Height \ 2)
                    p.Dispose()
                End If
            Else
                e.Graphics.DrawLine(New Pen(SystemColors.ControlDark), _
                      Width \ 2 - 1, 0, Width \ 2 - 1, Height)

                e.Graphics.DrawLine(Pens.White, _
                    Width \ 2, 0, Width \ 2, Height)
            End If
        End Sub
    End Class
End Namespace