#include once "window9.bi"

Type ListNode
    as integer iValue = 0
    as USTRING sValue 
    as ListNode Ptr prev_node = 0
    as ListNode Ptr next_node = 0
End type

type List
    Declare function addLast (byval iValue as Integer) as Integer
    Declare Function addLast (byval sValue as USTRING) as Integer
    declare sub removeGadget (byval iGadget as Integer)
    Declare sub removeColor (byval iColor as Integer)
    declare function findNode  (byval nodeID as Integer) as ListNode Ptr
    as ListNode Ptr first_node = 0
    as ListNode Ptr last_node = 0
    As HWND hCurrent
end Type

Dim Shared As boolean g_ListsCreated
Dim Shared As List Ptr g_LastList,g_ClassList,g_ColorList,g_TabStop

function List.addLast(byval iValue as Integer ) as integer
    dim as ListNode Ptr new_node = new ListNode
    if new_node = 0 then return 0
    new_node->iValue = iValue
    if this.first_node = 0 then
        this.first_node = new_node
    else
        this.last_node->next_node = new_node
        new_node->prev_node = this.last_node
    end if
    this.last_node = new_node
    return new_node->iValue
end Function

function List.addLast(byval sValue as USTRING ) as integer
    dim as ListNode Ptr new_node = new ListNode
    if new_node = 0 then return 0
    new_node->sValue = sValue
    if this.first_node = 0 then
        new_node->iValue = 1 : this.first_node = new_node
    else
        new_node->iValue = this.last_node->iValue + 1
        this.last_node->next_node = new_node
        new_node->prev_node = this.last_node
    end if
    this.last_node = new_node
    return new_node->iValue
end Function

sub List.removeGadget(byval id as Integer)
    dim as ListNode Ptr del_node = this.findNode( id )
    if del_node = 0 Then exit sub
    if this.first_node = del_node Then
        this.first_node = del_node->next_node->next_node
    else
        del_node->prev_node->next_node = del_node->next_node->next_node
    end if
    
    if this.last_node->prev_node = del_node Then
        this.last_node = del_node->prev_node
    Else
        del_node->next_node->next_node->prev_node = del_node->prev_node
    end If
    
    Delete del_node->next_node
    Delete del_node
End Sub

sub List.removeColor(byval id as Integer )
    dim del_node as ListNode Ptr
    del_node = this.findNode( id )
    if del_node = 0 Then
        exit sub
    end If
    if this.first_node = del_node Then
        this.first_node = del_node->next_node->next_node->next_node
    Else
        del_node->prev_node->next_node = del_node->next_node->next_node->next_node
    end if
    
    if this.last_node->prev_node->prev_node = del_node Then
        this.last_node = del_node->prev_node
    Else
        del_node->next_node->next_node->next_node->prev_node = del_node->prev_node
    end If
    
    Delete del_node->next_node->next_node
    Delete del_node->next_node
    Delete del_node
End Sub

function List.findNode(byval id as Integer) as ListNode Ptr
    dim as ListNode Ptr seek_node = this.first_node
    do while seek_node
        if seek_node->iValue = id Then
            return seek_node
        end if
        seek_node = seek_node->next_node
    loop
    return 0
end Function

Function OpenWindow(ByRef sName As USTRING, _
ByVal x As Integer, ByVal y As Integer, _
ByVal w As Integer, ByVal h As Integer, _
ByVal Style   As Integer, _
ByVal ExStyle As Integer,_
ByVal User_data_Sub as any ptr = 0, _
Byval hParentHwnd as HWND = 0) As HWND Export
    Static as integer iAddIndexStr = 0
    dim as Integer iExWinStyle
    if hParentHwnd then
        iExWinStyle = ExStyle or WS_EX_CONTROLPARENT
    else
        iExWinStyle = ExStyle
    EndIf
    iAddIndexStr += 1
    If g_ListsCreated=false Then
        g_LastList= New List
        g_ClassList=New List
        g_ColorList=New List
        if style and WS_TABSTOPENABLE then
            g_TabStop=New List
            style = style xor WS_TABSTOPENABLE
        EndIf
        g_ListsCreated=true
    End If
    
    dim as USTRING sClassTemp = "CLASSCLASS_"+Str(Time())+ Str(iAddIndexStr)
    g_ClassList->addLast(sClassTemp)
    sClassTemp = g_ClassList->findNode(iAddIndexStr)->sValue
    Dim As WNDCLASSEX wc
    with wc
        .cbSize        = SIZEOF( WNDCLASSEX )
        .style         = CS_HREDRAW or CS_VREDRAW or CS_OWNDC
        .lpfnWndProc   = Cast(WNDPROC,win9GetGuiWinProc())
        .hInstance     = GetModuleHandle(0)
        .hbrBackground = CPtr(HGDIOBJ, 16)
        #ifdef UNICODE
            .lpszClassName = *sClassTemp
        #else	
            .lpszClassName = StrPtr(sClassTemp)
        #EndIf		
        .hIcon         = LoadIcon(NULL, IDI_APPLICATION )
        .hIconSm       = .hIcon
        .hCursor       = LoadCursor(NULL, IDC_ARROW)
    end With
    if(RegisterClassEx(@wc) = FALSE) Then
        Var e = GetLastError()
        ? "RegisterClassEx() failed, error: " & e
        Return 0
    end If
    #ifdef UNICODE
        Var hWin = CreateWindowEx(iExWinStyle, *sClassTemp, *sName, Style, x,y,w,h, hParentHwnd,0,0,0)
    #else	
        Var hWin = CreateWindowEx(iExWinStyle, StrPtr(sClassTemp), sName, Style, x,y,w,h, hParentHwnd,0,0,0)
    #EndIf
    if g_TabStop then
        setprop(hWin , "tabstopenable" , cast(handle,1))
    EndIf
    g_LastList->hCurrent = hWin
    Return hWin
End Function

Function win9GetCurrent() As HWND Export
    Return g_LastList->hCurrent
End Function

sub UseGadgetList(byval hWin As HWND) Export
    g_LastList->hCurrent = hWin
End Sub

Function win9AddNewGadget(byval iGadget as integer, byval hWin as HWND) As Integer Export
    if g_LastList then
        FreeGadget(iGadget)
        g_LastList->addLast(iGadget)
        g_LastList->addLast(Cast(Integer,hWin))
        Return 1
    endif
End Function

Function win9AddColor(byval hWin as HWND, ByVal iColorBg as Integer, ByVal iColorText as Integer) As Integer Export
    if g_ColorList then
        g_ColorList->addLast(Cast(Integer,hWin))
        g_ColorList->addLast(iColorBg)
        g_ColorList->addLast(iColorText)
        Return 1
    endif
End Function

sub win9AddTabStop(byval hWin as HWND, ByVal iGadget as Integer) Export
    if g_TabStop then
        g_TabStop->addLast(iGadget)
        g_TabStop->addLast(Cast(Integer,hWin))
        if getprop(hWin , "tabstopshortcut") then
        else
            AddKeyboardShortcut(hWin,FVIRTKEY,VK_TAB,65525)
            AddKeyboardShortcut(hWin,FSHIFT,VK_TAB,65526)
            AddKeyboardShortcut(hWin,FCONTROL,VK_TAB,65527)
            setprop(hWin , "tabstopshortcut" , cast(handle , 1))
        EndIf
    endif
End sub

sub win9DeleteGadgetTabStop(ByVal iGadget as Integer) Export
    if g_TabStop then
        g_TabStop->removeGadget(iGadget)
    endif
End Sub

#ifdef w9TabStopSortByGadget
    Sub win9InsertSort(m() As integer)
        Dim i As Integer
        Dim j As Integer
        Dim As Integer key
        For i = 1 To UBound(m)
            key = m(i)
            j = i - 1
            while j >= 0 andalso m(i) < m(j)
                
                m(j+1) = m(j)
                j = j - 1
                
            Wend
            m(j+1) = key
        Next
    End Sub
#else
    Sub win9InsertSort(m() As integer)
        Dim i As Integer
        Dim j As Integer
        Dim As Integer iX , iY , key
        For i = 1 To UBound(m)
            iY = gadgety(m(i))
            iX = gadgetx(m(i))
            key = m(i)
            j = i - 1
            while j >= 0 andalso ((gadgety(m(j)) > iY) orelse _
            (gadgety(m(j)) = iY andalso gadgetX(m(j)) > iX))
                
                m(j+1) = m(j)
                j = j - 1
                
            Wend
            m(j+1) = key
        Next
    End Sub
#EndIf

function win9FillAndSortTabStop(byval parentHwnd as Hwnd , m() as integer) as LONG
    if g_TabStop then
        dim as LISTNODE ptr pTemp = g_TabStop->first_node
        dim as Integer iNextHwnd , iIndexArray
        dim as Hwnd hwToolbarIs = getwindow(parentHwnd , GW_CHILD)
        if getclassname_(hwToolbarIs) = "ToolbarWindow32" andalso IsWindowVisible(hwToolbarIs) andalso IsWindowEnabled(hwToolbarIs) then
            m(0) = cast(integer ,hwToolbarIs)
            iIndexArray+=1
        EndIf
        while pTemp
            if cast(hwnd ,pTemp->next_node->iValue) = parentHwnd then
                dim as integer iGadgetTemp = pTemp->iValue
                if iGadgetTemp andalso IsWindowVisible(gadgetid(iGadgetTemp)) andalso IsWindowEnabled(gadgetid(iGadgetTemp)) then
                    m(iIndexArray) = iGadgetTemp
                    iIndexArray+=1
                    if iIndexArray > Ubound(m) then
                        Redim preserve as Integer m(iIndexArray+50)
                    EndIf
                EndIf
            endif
            pTemp = pTemp->next_node->next_node
        Wend
        if iIndexArray = 0 then return 0
        Redim preserve as Integer m(iIndexArray-1)
        win9InsertSort(m()) 
        return 1
    endif
End Function

function win9GetAndCheckWinInTabStop(iGadget as Integer) as hwnd
    if getclassname_(cast(hwnd ,iGadget)) = "ToolbarWindow32" then
        return cast(hwnd ,iGadget)
    EndIf
    dim h as Hwnd = gadgetid(iGadget)
    if getclassname_(h) = "ExplorerGadget" then
        return GetExplorerListGadgetHwnd(iGadget)
    else
        return h
    EndIf
End function

sub win9SetFosusTabStop(byval iGadget as Integer , byval parentHwnd as Hwnd,_
byval iId as Integer , byval iFlagFindAny as Long = 0)
    
    if iGadget andalso iFlagFindAny = 0 then
        select case iId
            case 65525 , 65526
                Redim as Integer m(100)
                if win9FillAndSortTabStop(parentHwnd , m()) then
                    dim as integer iCurentInArray = -1
                    for i as Long = 0 to Ubound(m)
                        if iGadget = m(i) then
                            iCurentInArray = i
                            exit for
                        EndIf
                    Next
                    if iCurentInArray <> -1 then
                        if iId = 65525 then
                            if iCurentInArray = Ubound(m) then
                                DIM AS HWND hWin = win9GetAndCheckWinInTabStop(m(0))
                                setfocus(hWin)
                                setprop(parentHwnd , "tabstopcurentgadget" , hWin)                                
                            else
                                DIM AS HWND hWin = win9GetAndCheckWinInTabStop(m(iCurentInArray+1))
                                setfocus(hWin)
                                setprop(parentHwnd , "tabstopcurentgadget" , hWin)
                            EndIf
                        elseif iId = 65526 then
                            if iCurentInArray = 0 then
                                DIM AS HWND hWin = win9GetAndCheckWinInTabStop(m(Ubound(m)))
                                setfocus(hWin)
                                setprop(parentHwnd , "tabstopcurentgadget" , hWin)
                            else
                                DIM AS HWND hWin = win9GetAndCheckWinInTabStop(m(iCurentInArray-1))
                                setfocus(hWin)
                                setprop(parentHwnd , "tabstopcurentgadget" , hWin)
                            EndIf
                        EndIf
                    endif
                endif
        End Select
    elseif iFlagFindAny = 1 then
        select case iId
            case 65525 , 65526 , 65527
                Redim as Integer m(100)
                if win9FillAndSortTabStop(parentHwnd , m()) then
                    DIM AS HWND hWin = win9GetAndCheckWinInTabStop(m(0))
                    setfocus(hWin)
                    setprop(parentHwnd , "tabstopcurentgadget" , hWin)
                endif
        End Select
    elseif iFlagFindAny = 2 then
        select case iId
            case 65527
                Redim as Integer m(100)
                if win9FillAndSortTabStop(parentHwnd , m()) then
                    dim as integer iCurentInArray = -1
                    for i as Long = 0 to Ubound(m)
                        if iGadget = m(i) then
                            iCurentInArray = i
                            exit for
                        EndIf
                    Next
                    if iCurentInArray <> -1 then
                        if iCurentInArray = Ubound(m) then
                            DIM AS HWND hWin = win9GetAndCheckWinInTabStop(m(0))
                            setfocus(hWin)
                            setprop(parentHwnd , "tabstopcurentgadget" , hWin)
                        else
                            DIM AS HWND hWin = win9GetAndCheckWinInTabStop(m(iCurentInArray+1))
                            setfocus(hWin)
                            setprop(parentHwnd , "tabstopcurentgadget" , hWin)
                        EndIf
                    endif
                endif
        End Select
    endif
    
End sub

sub win9SwitchTabStop(byval parentHwnd as Hwnd, byval iId as Integer) Export
    if g_TabStop then
        dim as Hwnd hWinFocused = getFocus()
        dim as Hwnd hWinIsExplorer = getparent(hWinFocused)
        if hWinIsExplorer andalso getclassname_(hWinIsExplorer) = "ExplorerGadget" then
            hWinFocused = hWinIsExplorer
        EndIf
        if hWinFocused then
            if hWinFocused = parentHwnd andalso (iId = 65525 orelse iId = 65526 orelse iId = 65527) then
                win9SetFosusTabStop(0 , parentHwnd , iId , 1) ' find 1
                exit sub
            elseif iId = 65527 then
                if getclassname_(hWinFocused) = "ToolbarWindow32" then
                    win9SetFosusTabStop(cast(integer ,hWinFocused), parentHwnd , iId , 2)
                    exit sub
                EndIf
                dim as Integer iGadget = ID_In_Number(hWinFocused)
                if iGadget then
                    dim as listNode ptr p = g_TabStop->findNode(iGadget)
                    if p then
                        win9SetFosusTabStop(iGadget, parentHwnd , iId , 2) ' find next ctrl+tab
                    else
                        win9SetFosusTabStop(0 , parentHwnd , iId , 1) ' find 1 
                    EndIf
                else
                    win9SetFosusTabStop(0 , parentHwnd , iId , 1) ' find 1
                EndIf
                exit sub
            else
                dim as USTRING szClass = UCASE(getclassname_(hWinFocused))
                if (szClass = "RICHEDIT20W" orelse szClass = "RICHEDIT20A") andalso _
                (getwindowlongptr(hWinFocused , GWL_STYLE) and ES_READONLY) = 0 then
                    if iId = 65525 orelse iId = 65526 then
                        sendmessage(hWinFocused , WM_CHAR , cast(wparam ,VK_TAB) , cast(lparam,0))
                        exit sub
                    endif
                elseif szClass = "SCINTILLA" then
                    if iId = 65525 then
                        sendmessage(hWinFocused , WM_KEYDOWN , cast(wparam ,VK_TAB) , cast(lparam,0))
                        sendmessage(hWinFocused , WM_KEYUP , cast(wparam ,VK_TAB) , cast(lparam,0))
                        exit sub
                    elseif iId = 65526 then
                        sendmessage(hWinFocused , WM_KEYDOWN , cast(wparam ,VK_SHIFT) , cast(lparam,0))
                        sendmessage(hWinFocused , WM_KEYDOWN , cast(wparam ,VK_TAB) , cast(lparam,0))
                        sendmessage(hWinFocused , WM_KEYUP , cast(wparam ,VK_TAB) , cast(lparam,0))
                        sendmessage(hWinFocused , WM_KEYUP , cast(wparam ,VK_SHIFT) , cast(lparam,0))
                        exit sub
                    endif            
                EndIf
            EndIf
            if getclassname_(hWinFocused) = "ToolbarWindow32" then
                win9SetFosusTabStop(cast(integer ,hWinFocused), parentHwnd , iId)
                exit sub
            EndIf
            dim as Integer iGadget = ID_In_Number(hWinFocused)
            if iGadget = 0 then
                win9SetFosusTabStop(0 , parentHwnd , iId , 1) ' find 1 
            else
                dim as listNode ptr p = g_TabStop->findNode(iGadget)
                if p then
                    win9SetFosusTabStop(iGadget , parentHwnd , iId)
                else
                    win9SetFosusTabStop(0 , parentHwnd , iId , 1) ' find 1
                EndIf
            EndIf
        else
            win9SetFosusTabStop(0 , parentHwnd , iId , 1) ' find 1 
        endif
    endif
End Sub

Function GadgetID(ByVal iGadget As integer) As HWND Export
    dim as ListNode Ptr p = g_LastList->findNode(Cast(Integer,iGadget))
    If p<>0 andalso p->next_node<>0 Then
        Return Cast(HWND,p->next_node->iValue)
    Else
        Return 0
    End If
End Function

sub SleepW9(byval iN as integer) export
    static as integer _SleepCounter=0
    _SleepCounter+=1
    If _SleepCounter>=iN Then
        sleep(1) :_SleepCounter=0
    End If
end sub

Function GetGadgetColor(byval iGadget as Long, ByVal iFlag as Long) As Integer Export
    if g_ColorList then
        Dim As USTRING szClass
        Dim as HWND hWinTemp
        szClass = GetClassName_(GadgetID(iGadget))
        If szClass = "ExplorerGadget" then
            hWinTemp = *cast(HWND ptr,GetProp(GadgetID(iGadget),"addparam60000"))
        ElseIf iFlag < 3 Then
            hWinTemp = GadgetID(iGadget)
        End If
        
        Select Case iFlag
            Case 1
                If g_ColorList->findNode(CInt(hWinTemp))<>0 Then
                    Return g_ColorList->findNode(CInt(hWinTemp))->next_node->iValue
                EndIf
            Case 2
                If g_ColorList->findNode(CInt(hWinTemp))<>0 Then
                    Return g_ColorList->findNode(CInt(hWinTemp))->next_node->next_node->iValue
                EndIf
            Case 3
                If g_ColorList->findNode(iGadget)<>0 Then
                    Return g_ColorList->findNode(iGadget)->next_node->iValue
                Else
                    win9SetErrorColor(1)
                EndIf
            Case 4
                If g_ColorList->findNode(iGadget)<>0 Then
                    Return g_ColorList->findNode(iGadget)->next_node->next_node->iValue
                Else
                    win9SetErrorColor(1)
                End If
        End Select
    endif
End Function

Sub SetGadgetColor(ByVal iGadget as Long, _
ByVal iColorBg as Long, _
ByVal iColorText as Long, _
ByVal iFlag as Long)  Export
    if g_ColorList then
        Var return_DSGC = win9SetGadgetColor(GadgetID(iGadget),iColorBg ,iColorText ,iFlag )
        Dim As HWND hWinTemp
        If return_DSGC = 4 Then
            dim as HWND ptr pHwnd = cast(HWND ptr, GetProp(GadgetID(iGadget), "addparam60000"))
            if pHwnd then
                hWinTemp = *pHwnd
            EndIf
        Else
            hWinTemp = GadgetID(iGadget)
        End If
        
        If g_ColorList->findNode(Cast(Integer,hWinTemp))<>0 Then
            If g_ColorList->findNode(Cast(Integer,hWinTemp))->next_node<>0 Then
                Select Case iFlag
                    Case 1
                        g_ColorList->findNode(Cast(Integer,hWinTemp))->next_node->iValue=iColorBg
                    Case 2
                        If g_ColorList->findNode(Cast(Integer,hWinTemp))->next_node->next_node<>0 Then
                            g_ColorList->findNode(Cast(Integer,hWinTemp))->next_node->next_node->iValue=iColorText
                        endif
                    Case 3
                        g_ColorList->findNode(Cast(Integer,hWinTemp))->next_node->iValue=iColorBg
                        If g_ColorList->findNode(Cast(Integer,hWinTemp))->next_node->next_node<>0 Then
                            g_ColorList->findNode(Cast(Integer,hWinTemp))->next_node->next_node->iValue=iColorText
                        EndIf
                End Select
            EndIf
        EndIf
        If return_DSGC=2 Then SendMessage(getparent(hWinTemp),WM_CTLCOLORSCROLLBAR,0,Cast(LPARAM,hWinTemp))
    endif
End Sub

Function ID_In_Number(ByVal hWin As HWND) As Integer Export
    dim as ListNode Ptr p = g_LastList->findNode(cast(integer,hWin))
    If p<>0 andalso p->prev_node<>0 Then
        Return p->prev_node->iValue
    EndIf
End Function

Sub RemoveStatusbar(ByVal iGadget As Integer)
    Dim As Integer Ptr iArray = GetProp(GadgetID(iGadget),"STATUSBARARRAY")
    If iArray <> 0 Then
        DeAllocate iArray
        RemoveProp(GadgetID(iGadget),"STATUSBARARRAY")
        RemoveProp(GadgetID(iGadget),"STATUSBARNUMBERFIELDS")
    End If
    RemoveProp(GadgetID(iGadget),"STATUSBARWINDOW")
End Sub

Sub FreeGadget(ByVal iGadget As long) Export
    RemoveStatusbar(iGadget)
    Dim As HWND hWin = GadgetID(iGadget)
    If hWin<>0 andalso g_ColorList Then g_ColorList->removeColor(Cast(Integer,hWin))
    Dim sBuf As USTRING = GetClassName_(hWin)
    If sBuf = "ExplorerGadget" Then
        Dim freeexplorerproc As Function (byval pEG As Any Ptr) As bool
        freeexplorerproc = GetProp(hWin,"EXPLORERLISTFREEPROC")
        If freeexplorerproc<>0 Then
            freeexplorerproc(GetProp(hWin,"addparam60000"))
            RemoveProp(hWin,"EXPLORERLISTFREEPROC")
            RemoveProp(hWin,"EXPLORERGADGETLISTVIEWWINDOW")
        EndIf
    elseif sBuf = "SysTreeView32" orelse sBuf = "SysListView32" then
        dim as integer imgList = Cast(integer,GetHimageCombo_(iGadget))
        if imgList then ImageList_RemoveAll(cast(any ptr ,imgList))
        dim as any ptr pStackIndexes = GetProp(hWin, "stackindexes")
        if pStackIndexes then FreeStackPointer(pStackIndexes)
        RemoveProp(hWin,"stackindexes")
    elseif sBuf = "msctls_updown32" then
        dim as hwnd hExtWin = cast(hwnd ,GetProp(hWin,"addparam60000"))
        if hExtWin then DestroyWindow(hExtWin)
    elseif sBuf = "ComboBoxEx32" then
        dim as any ptr imglist = GetProp(hWin,"addparam60000")
        if imglist then ImageList_RemoveAll(imgList)
    elseif sBuf = "SplitterGadget" then
        RemoveProp(hWin,"HANDLEPAN")
        RemoveProp(hWin,"POSITIONPAN")
        RemoveProp(hWin,"STYLEPAN")
        RemoveProp(hWin,"SPLITTERMOVE")
        RemoveProp(hWin,"GADGET1")
        RemoveProp(hWin,"GADGET2")
    End If
    win9DeleteGadgetTabStop(iGadget)
    RemoveProp(hWin,"addparam60000")
    DestroyWindow(hWin)
    g_LastList->removeGadget(iGadget)
End Sub

Sub win9removeMenu(ByVal menu As Integer)
    if g_LastList then g_LastList->removeGadget(menu)
End Sub

