//------------------------------------------------------------------------------
// File: ControlHelp.cpp
//
// Desc: DirectShow sample code - implementation of CSliderValue and
//       CRadioChoice classes.
//
// Copyright (c) 2000-2001 Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------------------------


#include <windows.h>
#include "ControlHelp.h"
#include <commctrl.h>
#include <stdio.h>
#include <tchar.h>
#include <atlbase.h>

//////////////////////////////////////////////////////////////////////////////
// CSliderValue

const short g_sMaxContinuousTicks = 100;
const int g_iMaxCharBuffer = 50; // # characters big enough to hold -FLT_MAX with room to spare

CSliderValue::CSliderValue()
  : m_fInit(false)
{
}

void CSliderValue::Init(
        HWND        hwndSlider,
        HWND        hwndEdit,
        float       fMin, 
        float       fMax, 
        bool        fDiscrete)
{
    m_hwndSlider = hwndSlider;
    m_hwndEdit = hwndEdit;
    m_fMin = fMin;
    m_fMax = fMax;
    m_fDiscrete = fDiscrete;

    short sMin;
    short sMax;
    short sTicks = 4; // Lots of ticks become less useful as guides.  Use quarters for fine-grained sliders.
    if (m_fDiscrete) 
    {
        sMin = static_cast<short>(fMin);
        sMax = static_cast<short>(fMax);
        if (sMax - sMin <= 10)
            sTicks = (short) (sMax - sMin);
    }
    else
    {
        sMin = 0;
        sMax = g_sMaxContinuousTicks;
    }
    
    SendMessage(m_hwndSlider, TBM_SETRANGE, TRUE, MAKELONG(sMin, sMax));
    SendMessage(m_hwndSlider, TBM_SETTICFREQ, (sMax - sMin) / sTicks, 0);
    m_fInit = true;
}

void CSliderValue::SetValue(float fPos)
{
    if (!m_fInit)
        return;

    UpdateEditBox(fPos);
    UpdateSlider();
}

float CSliderValue::GetValue()
{
    if (!m_fInit)
        return 0;

    LRESULT lrLen = SendMessage(m_hwndEdit, WM_GETTEXTLENGTH, 0, 0);
    if (lrLen >= g_iMaxCharBuffer)
        return 0;

    TCHAR szText[g_iMaxCharBuffer] = TEXT("");
    SendMessage(m_hwndEdit, WM_GETTEXT, g_iMaxCharBuffer, reinterpret_cast<LPARAM>(szText));

    USES_CONVERSION;
    float fVal = static_cast<float>(m_fDiscrete ? _ttoi(szText) : atof(T2A(szText)));

    if (fVal < m_fMin) fVal = m_fMin;
    if (fVal > m_fMax) fVal = m_fMax;
    return fVal;
}

float CSliderValue::GetSliderValue()
{
    short sPos = static_cast<short>(SendMessage(m_hwndSlider, TBM_GETPOS, 0, 0));
    if (m_fDiscrete)
    {
        return sPos;
    }

    float fRet = (m_fMax - m_fMin) * sPos / g_sMaxContinuousTicks + m_fMin;
    return fRet;
}

LRESULT CSliderValue::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    if (!m_fInit)
        return FALSE;

    bHandled = FALSE;

    switch (uMsg)
    {
    case WM_HSCROLL:
        if (reinterpret_cast<HWND>(lParam) == m_hwndSlider && LOWORD(wParam) >= TB_LINEUP && LOWORD(wParam) <= TB_ENDTRACK)
        {
            UpdateEditBox(GetSliderValue());
            bHandled = TRUE;
        }
        break;

    case WM_COMMAND:
        if (HIWORD(wParam) == EN_KILLFOCUS && reinterpret_cast<HWND>(lParam) == m_hwndEdit)
        {
            UpdateSlider();
            bHandled = TRUE;
        }
        break;
    }

    return 0;
}

void CSliderValue::UpdateEditBox(float fPos)
{
    TCHAR szText[g_iMaxCharBuffer] = TEXT("");

    if (m_fDiscrete)
    {
        short sPos = static_cast<short>(fPos);
        wsprintf(szText, TEXT("%hd"), sPos);
    }
    else
    {
        wsprintf(szText, TEXT("%.3hf"), fPos);
    }

    SendMessage(m_hwndEdit, WM_SETTEXT, 0, reinterpret_cast<LPARAM>(szText));
}

void CSliderValue::UpdateSlider()
{
    float fVal = GetValue();
    short sPos = static_cast<short>(m_fDiscrete ? fVal : g_sMaxContinuousTicks * ((fVal - m_fMin) / (m_fMax - m_fMin)));
    SendMessage(m_hwndSlider, TBM_SETPOS, TRUE, sPos);
    UpdateEditBox(fVal); // this resets the input box back to the set float value in case the input was invalid
}

//////////////////////////////////////////////////////////////////////////////
// CSliderValue

CRadioChoice::CRadioChoice(const ButtonEntry *pButtonInfo)
  : m_pButtonInfo(pButtonInfo)
{
}

void CRadioChoice::SetChoice(HWND hDlg, LONG lValue)
{
    for (const ButtonEntry *p = m_pButtonInfo; p->nIDDlgItem; ++p)
    {
        if (p->lValue == lValue)
        {
            CheckDlgButton(hDlg, p->nIDDlgItem, BST_CHECKED);
            return;
        }
    }
}

LONG CRadioChoice::GetChoice(HWND hDlg)
{
    for (const ButtonEntry *p = m_pButtonInfo; p->nIDDlgItem; ++p)
    {
        if (BST_CHECKED == IsDlgButtonChecked(hDlg, p->nIDDlgItem))
        {
            return p->lValue;
        }
    }

    return 0;
}

LRESULT CRadioChoice::MessageHandler(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    bHandled = FALSE;
    UNREFERENCED_PARAMETER(lParam);

    if (uMsg == WM_COMMAND && HIWORD(wParam) == BN_CLICKED)
    {
        for (const ButtonEntry *p = m_pButtonInfo; p->nIDDlgItem; ++p)
        {
            if (p->nIDDlgItem == LOWORD(wParam))
            {
                bHandled = TRUE;
                return 0;
            }
        }
    }

    return 0;
}

//////////////////////////////////////////////////////////////////////////////
// MessageHandlerChain

LRESULT MessageHandlerChain(Handler **ppHandlers, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
    LRESULT lr = 0;
    bHandled = FALSE;

    for (Handler **pp = ppHandlers; *pp && !bHandled; ++pp)
    {
        lr = (*pp)->MessageHandler(uMsg, wParam, lParam, bHandled);
    }
    return lr;
}
