'SkinTHEMES
'skinned with WinLIFT64 version 7.01
'06-18-2025 
'copyright (c) Patrice Terrier
'www.objreader.com
'objreader@gmail.com

#Include "WinLIFT64.bi"
#inclib "WinLIFT64"
#Include Once "win/shlwapi.bi"  ' For PathRemoveFileSpecW

enum FontStyle
    FontStyleRegular    = 0
    FontStyleBold       = 1
    FontStyleItalic     = 2
    FontStyleBoldItalic = 3
    FontStyleUnderline  = 4
    FontStyleStrikeout  = 8
end enum

Const CLIENT_WIDTH  = 480
Const CLIENT_HEIGHT = 360

const IDI_ICON1       = 101

const IDC_BTN_LEFT    = 101
const IDC_BTN_PLAY    = 102
const IDC_BTN_RIGHT   = 103
const IDC_BTN_CHECK   = 104
const IDC_BTN_DELETE  = 105
const IDC_BTN_RECYCLE = 106
const IDC_BTN_PRINT   = 107
const IDC_BTN_TEXT    = 108
const IDC_BTN_SAVE    = 109
const IDC_BTN_32BIT   = 110
const IDC_BTN_64BIT   = 111
const IDC_BTN_00      = 112
const IDC_BTN_01      = 113
const IDC_BTN_02      = 114
const IDC_BTN_03      = 115
const IDC_BTN_04      = 116
const IDC_BTN_05      = 117
const IDC_BTN_06      = 118
const IDC_BTN_07      = 119
const IDC_BTN_08      = 120
const IDC_BTN_09      = 121
const IDC_CLOCK       = 122
const IDC_FRAME       = 123
const IDC_LIPS        = 124
const IDC_BTN_HELP    = 125

const IDC_SKIN_LIST   = 126

Type PROP
    hMain as HWND
    hList as HWND
    minWidth as Long
    minHeight as Long
end Type

dim Shared gP as PROP

Function rWidth(ByRef r as RECT) as Long
    return r.right - r.left
end Function

Function rHeight(ByRef r as RECT) as Long
    return r.bottom - r.top
end Function

sub ClearMemory(mem as any ptr, nSize as long)
    memset(mem, 0, nSize)
end sub

Function ExePathW() as WString ptr
    static zpath as WString * MAX_PATH
    static done as long
    if (done = 0) then
        ClearMemory(@zpath, MAX_PATH * 2)
        if (GetModuleFileNameW(0, @zpath, MAX_PATH)) then
            dim nLen as long = lstrlenW(zpath)
            dim nPos as long = nLen
            dim anti as Wstring * 2 => "\"
            for K as long = 1 to nLen
                if (lstrcmpiW(@zpath[nPos - K], @anti) <> 0) then
                    zpath[nPos - K] = 0
                else
                    exit for
                end if
            next
        end if
        done = -1
    end if
    return @zpath
end Function

function Path_Combine(zResource as wstring ptr, path as wstring ptr, combine as wstring ptr) as wstring ptr
    dim as long nLenPath = lstrlenW(path)
    dim as long nLenCombine = lstrlenW(combine)
    ClearMemory(zResource, MAX_PATH * sizeof(wstring))
    if nLenPath < MAX_PATH then
        MoveMemory(zResource, path, nLenPath * sizeof(wstring))
        if (nLenPath + nLenCombine) < MAX_PATH then
            MoveMemory(@zResource[nLenPath], combine, nLenCombine * sizeof(wstring))
        end if
    end if
    return zResource
end function

Function EXEresource() as WString ptr
    Static zpath as WString * MAX_PATH
    Static done as Long

    if done = 0 then
        ClearMemory(@zpath, SizeOf(zpath))
        dim zResource as WString * MAX_PATH => "Resource\" 
        Path_Combine(@zpath, ExePathW(), @zResource)
        done = -1
    end if
    return @zpath
end Function

function Resource(sFile as wstring ptr) as wstring ptr
    static szresource as wstring * MAX_PATH
    Path_Combine(@szresource, EXEresource(), sFile)
    return @szresource
end function

sub List_DeleteAll(hList as HWND)
    if IsWindow(hList) then
        SendMessage(hList, LB_RESETCONTENT, 0, 0)
    end if
end sub

' ListBox: Select item with 1-based index
sub List_SelectPlus(hList as HWND, nIndex as long)
    if IsWindow(hList) then
        if nIndex < 0 then
            hList = 0
        end if
        if hList <> 0 then
            if nIndex > 0 then nIndex -= 1 ' ListBox is 0-based
            SendMessage(hList, LB_SETCURSEL, nIndex, 0)
        end if
    end if
end sub

' ListBox: Add string
function List_Add(hList as HWND, zTmp as wstring ptr) as LRESULT
    dim as LRESULT nIndex = 0
    if IsWindow(hList) then
        if lstrlenW(zTmp) > 0 then
            nIndex = SendMessageW(hList, LB_ADDSTRING, 0, cast(LPARAM, zTmp))
        end if
    end if
    return nIndex
end function

Function List_GetCursel(hList as HWND) as Long
    dim nIndex as Long = SendMessage(hList, LB_GETCURSEL, 0, 0)
    if nIndex > -1 then nIndex += 1 ' Because ListBox is zero-based
    return nIndex
end Function

Function List_Count(hList as HWND) as Long
    return SendMessage(hList, LB_GETCOUNT, 0, 0)
end Function

Function List_GetText(hList as HWND, nItem as Long) as WString ptr
    Static szData as WString * MAX_PATH
    ClearMemory(@szData, SizeOf(szData))

    if nItem > 0 then
        nItem -= 1
        SendMessageW(hList, LB_GETTEXT, nItem, Cast(LPARAM, @szData))
    end if

    return @szData
end Function

Sub SkinDetect(byval hParent as HWND, byval zSkinFile as WString ptr)
    dim fd as WIN32_FIND_DATAW
    dim zBackground as WString * MAX_PATH
    dim zPath as WString * MAX_PATH

    gP.hList = GetDlgItem(hParent, IDC_SKIN_LIST)
    if gP.hList = 0 then
        gP.hList = CreateWindowExW(0, "LISTBOX", NULL, WS_CHILD Or LBS_SORT, 0, -1000, 0, 0, hParent, Cast(HMENU, IDC_SKIN_LIST), GetModuleHandleW(NULL), NULL)
    else
        List_DeleteAll(gP.hList)
    end if

    CharLowerW(zSkinFile)
    
    dim pattern as WString * 260 => "*.sks"
    dim hFind as HANDLE = FindFirstFileW(@pattern, @fd)
    dim nCount as Long = 0
    dim nUseItem as Long = 0

    if hFind <> INVALID_HANDLE_VALUE then
        Do
            if (fd.dwFileAttributes And FILE_ATTRIBUTE_DIRECTORY) = 0 then
                if List_Add(gP.hList, @fd.cFileName) > -1 then
                    nCount += 1
                    CharLowerW(@fd.cFileName)
                    if (lstrcmpiW(@fd.cFileName, zSkinFile) = 0) And (nUseItem = 0) then
                        nUseItem = nCount
                    end if
                end if
            end if
        Loop While FindNextFileW(hFind, @fd) <> 0
        FindClose(hFind)
    end if

    if nCount > 0 then
        if nUseItem = 0 then nUseItem = 1
        List_SelectPlus(gP.hList, nUseItem)
    end if

    skSkinDisable(gP.hList)
end Sub

Sub AddChildControls(hWnd as HWND)
    dim zSkinFile as WString * 260 => "Sony.sks"

    SkinDetect(hWnd, @zSkinFile)
    ' List_SelectPlus(hList, nItem)

    dim zUserKey as WString * MAX_PATH => ""
    dim zSysButTip as WString * MAX_PATH => ""
    if skInitEngine(@zSkinFile, @zUserKey) then
        if skSkinWindow(hWnd, @zSysButTip) then
            dim r as RECT
            GetWindowRect(hWnd, @r)
            gP.minWidth = r.Right - r.Left
            gP.minHeight = r.Bottom - r.Top
        end if

        dim hCtrl as HWND

        hCtrl = skButtonImage(hWnd, Resource("BTN_Left.png"), 216-57, 300+12-20, IDC_BTN_LEFT, 5)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER_HORZ_BOTTOM)
        dim zTip as WString * 260 => "Previous skin theme"
        skCreateToolTip(hCtrl, @zTip)

        hCtrl = skButtonImage(hWnd, Resource("BTN_Play.png"), 216, 300-20, IDC_BTN_PLAY, 5)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER_HORZ_BOTTOM)

        hCtrl = skButtonImage(hWnd, Resource("BTN_Right.png"), 216+49, 300+12-20, IDC_BTN_RIGHT, 5)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER_HORZ_BOTTOM)
        zTip = "Next skin theme"
        skCreateToolTip(hCtrl, @zTip)

        dim zStr as WString * 260 => "BS_LEFT"
        hCtrl = skPushButtonImage(hWnd, Resource("Check_32.png"), @zStr, 22, 30, 140, 50, IDC_BTN_CHECK, BS_LEFT Or BS_VCENTER)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER_HORZ)

        zStr = "BS_CENTER"
        hCtrl = skPushButtonImage(hWnd, Resource("Delete_32.png"), @zStr, 22 + 140 + 8, 30, 140, 50, IDC_BTN_DELETE, BS_CENTER Or BS_VCENTER)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER_HORZ)

        zStr = "BS_RIGHT"
        hCtrl = skPushButtonImage(hWnd, Resource("Recycle_32.png"), @zStr, 22 + (140 + 8) * 2, 30, 140, 50, IDC_BTN_RECYCLE, BS_RIGHT Or BS_VCENTER)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER_HORZ)

        zStr = "BS_TOP"
        hCtrl = skPushButtonImage(hWnd, Resource("Print_32.png"), @zStr, 22, 20 + 70, 140, 50, IDC_BTN_PRINT, BS_TOP)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER)

        zStr = "Pristina font"
        hCtrl = skPushButtonImage(hWnd, NULL, @zStr, 22 + 140 + 8, 20 + 70, 140, 50, IDC_BTN_TEXT, BS_CENTER Or BS_VCENTER)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER)
        skSetLabelFont(hCtrl, Resource("Pristina.ttf"), 28, skARGB(255, 255, 255, 0), FontStyleRegular, 0)

        zStr = "BS_BOTTOM"
        hCtrl = skPushButtonImage(hWnd, Resource("Save_32.png"), @zStr, 22 + (140 + 8) * 2, 20 + 70, 140, 50, IDC_BTN_SAVE, BS_BOTTOM)
        skSetAnchorCtrl(hCtrl, ANCHOR_CENTER)

        For i as Integer = 0 To 9
            hCtrl = skPushButtonImage(hWnd, Resource("0" & Str(i) & "_32.png"), NULL, 40 + (40 * i), 20 + 140, 38, 38, IDC_BTN_00 + i, 0)
            skSetAnchorCtrl(hCtrl, ANCHOR_CENTER)
        Next

        hCtrl = skClockCtrl(hWnd, Resource("Clock_08_92.png"), 14, 258, 0, 0, IDC_CLOCK, &h7FCB0000, &h7F8F8F8F, 0)
        skSetAnchorCtrl(hCtrl, ANCHOR_BOTTOM)

        hCtrl = skButtonImage(hWnd, Resource("64bit_80.png"), 380, 264, IDC_BTN_64BIT, 5)
        skSetAnchorCtrl(hCtrl, ANCHOR_BOTTOM_RIGHT)

        hCtrl = skStaticImage(hWnd, Resource("Aero.png"), 10, 10, 460, 208, IDC_FRAME)
        skSetZorder(hCtrl, HWND_BOTTOM)
        skSetAnchorCtrl(hCtrl, ANCHOR_HEIGHT_WIDTH)

        hCtrl = skStaticImage(hWnd, Resource("makeuplips.png"), -20, -70, 200, 134, IDC_LIPS)
        skSetZorder(hCtrl, HWND_TOP)

        'hCtrl = skButtonImage(hWnd, Resource("BTN_Help.png"), 216, 228, IDC_BTN_HELP, 5)
        'skSetAnchorCtrl(hCtrl, ANCHOR_CENTER_HORZ_BOTTOM)
        'zTip = "Open WinLFT.chm"
        'skCreateToolTip(hCtrl, @zTip)
    end if
end Sub

Function WndProc(byval hWnd as HWND, byval uMsg as UINT, byval wParam as WPARAM, byval lParam as LPARAM) as LRESULT
    dim nRet as LRESULT = -1
    dim pMM as MINMAXINFO ptr
   
    select case uMsg
    case WM_CREATE
        ' Initialization can go here if needed.
        AddChildControls(hWnd)

    case WM_GETMINMAXINFO
        pMM = Cast(MINMAXINFO ptr, lParam)
        pMM->ptMinTrackSize.x = gP.minWidth
        pMM->ptMinTrackSize.y = gP.minHeight

    case WM_COMMAND     
        dim wID as Long = LoWord(wParam)
        dim wEvent as Long = HiWord(wParam)
                        
        select case wID 

        case IDC_BTN_LEFT, IDC_BTN_RIGHT
            dim zSkinFile as WString * MAX_PATH
            dim nItem as long = List_GetCursel(gP.hList)
            dim nCount as long = List_Count(gP.hList)

            if wID = IDC_BTN_LEFT then
                if nItem > 1 then
                    nItem -= 1
                else
                    nItem = nCount
                end if
            else
                if nItem < nCount then
                    nItem += 1
                else
                    nItem = 1
                end if
            end if
    
            List_SelectPlus(gP.hList, nItem)
            zSkinFile = *List_GetText(gP.hList, nItem)
   
            if UCase(zSkinFile) = "SONY.SKS" then
                ShowWindow(GetDlgItem(hWnd, IDC_LIPS), SW_SHOW)
            else
                ShowWindow(GetDlgItem(hWnd, IDC_LIPS), SW_HIDE)
            end if
    
            skSkinChange(@zSkinFile)
            return 0

        case IDC_BTN_64BIT
            dim zCaption as Wstring * 260 => "About"
            dim zMessage as Wstring * 260 => !"\nWinLIFT 7.00\n\nModal dialog box\nto popup a specific message."
            dim zButton as Wstring * 260 => "OK"
            skDialogInfo(@zCaption, @zMessage, @zButton)
            return 0

        case IDC_BTN_HELP
            ShellExecuteA(NULL, "open", "winlift.chm", NULL, NULL, SW_SHOW)
            ' skAbout() optional alternative
            return 0

        end Select

    case WM_SIZE
        InvalidateRect(hWnd, NULL, TRUE)
        nRet = 0

    case WM_PAINT
        dim ps as PAINTSTRUCT
        dim hDC as HDC = BeginPaint(hWnd, @ps)
        ' Paint window content here
        EndPaint(hWnd, @ps)
        nRet = 0

    case WM_DESTROY
        PostQuitMessage(0)
        nRet = 0
    end Select

    if nRet = -1 then nRet = DefWindowProc(hWnd, uMsg, wParam, lParam)
    return nRet
end Function

Function WinMain(byval hInstance as HINSTANCE, byval hPrevInstance as HINSTANCE, byval lpCmdLine as WString ptr, byval nCmdShow as Integer) as Integer
    dim nRet as Integer = 0
    dim szClass as ZString * 20 = "SkinTHEMES"

    ' Detect process without using a Mutex
    dim hFound as HWND = FindWindow(szClass, NULL)
    if hFound <> NULL then
        if IsIconic(hFound) then ShowWindow(hFound, SW_RESTORE)
        SetForegroundWindow(hFound)
        return nRet
    end if

    dim wcx as WNDCLASSEX
    wcx.cbSize = Len(wcx)
    dim IsInitialized as Long = GetClassInfoEx(hInstance, szClass, @wcx)

    if IsInitialized = 0 then
        wcx.style         = CS_HREDRAW Or CS_VREDRAW
        wcx.lpfnWndProc   = @WndProc
        wcx.cbClsExtra    = 0
        wcx.cbWndExtra    = 0
        wcx.hInstance     = hInstance
        wcx.hIcon         = LoadIcon(NULL, IDI_APPLICATION)
        wcx.hCursor       = LoadCursor(NULL, IDC_ARROW)
        wcx.hbrBackground = Cast(HBRUSH, COLOR_BTNFACE + 1) ' Fix: Windows expects a valid brush
        wcx.lpszMenuName  = NULL
        wcx.lpszClassName = @szClass
        wcx.hIconSm       = wcx.hIcon
       
        if RegisterClassEx(@wcx) then IsInitialized = -1
    end if

    if IsInitialized then
        dim dwExStyle as DWORD = 0
        dim dwStyle as DWORD = WS_OVERLAPPEDWINDOW Or WS_CLIPSIBLINGS Or WS_CLIPCHILDREN
        dim lpr as RECT
        SetRect(@lpr, 0, 0, CLIENT_WIDTH, CLIENT_HEIGHT)
        AdjustWindowRectEx(@lpr, dwStyle, FALSE, dwExStyle)

        gP.minWidth = rWidth(lpr)
        gP.minHeight = rHeight(lpr)

        dim x as Long = Max((GetSystemMetrics(SM_CXSCREEN) - gP.minWidth) \ 2, 0)
        dim y as Long = Max((GetSystemMetrics(SM_CYSCREEN) - gP.minHeight) \ 2, 0)

        gP.hMain = CreateWindowEx(dwExStyle, szClass, szClass, dwStyle, x, y, gP.minWidth, gP.minHeight, NULL, NULL, hInstance, NULL)

        if gP.hMain <> NULL then

            ShowWindow(gP.hMain, nCmdShow)
            SetForegroundWindow(gP.hMain) ' Slightly Higher Priority

            dim msg as MSG
            While GetMessage(@msg, NULL, 0, 0)
                TranslateMessage(@msg)
                DispatchMessage(@msg)
            Wend
            nRet = msg.wParam

        end if

        UnregisterClassW(szClass, hInstance)
    end if

    return nRet
end Function

end WinMain(GetModuleHandle(NULL), NULL, NULL, SW_SHOWDEFAULT)