//************************************************************************
//  The Logitech LCD SDK, including all acompanying documentation,
//  is protected by intellectual property laws.  All use of the Logitech
//  LCD SDK is subject to the License Agreement found in the
//  "Logitech LCD SDK License Agreement" file and in the Reference Manual.  
//  All rights not expressly granted by Logitech are reserved.
//************************************************************************
 
//************************************************************************
//
// LCDStreamingText.cpp
//
// The CLCDStreamingText class draws streaming text onto the LCD.
// Streaming text is a single line of text that is repeatedly streamed
// horizontally across the LCD.
// 
// Logitech LCD SDK
//
// Copyright 2010 Logitech Inc.
//************************************************************************
 
#include "LCDUI.h"
 
//************************************************************************
//
// CLCDStreamingText::CLCDStreamingText
//
//************************************************************************
 
CLCDStreamingText::CLCDStreamingText()
{
    m_sText.erase(m_sText.begin(), m_sText.end());
    m_sGapText.erase(m_sGapText.begin(), m_sGapText.end());
 
    m_sGapText.assign(_T("   "));
    m_hFont = NULL;
    m_nTextAlignment = DT_LEFT;
 
    m_bRedoColors = TRUE;
}
 
 
//************************************************************************
//
// CLCDStreamingText::~CLCDStreamingText
//
//************************************************************************
 
CLCDStreamingText::~CLCDStreamingText()
{
    RemoveAllText();
    if (m_hFont)
    {
        DeleteObject(m_hFont);
        m_hFont = NULL;
    }
}
 
 
//************************************************************************
//
// CLCDStreamingText::Initialize
//
//************************************************************************
 
HRESULT CLCDStreamingText::Initialize(void)
{
    m_eState = STATE_DELAY;
    m_dwStartDelay  = 2000;
    m_dwSpeed       = 7;
	m_dwStepInPixels = 7;
    m_dwEllapsedTime = 0;
    m_dwLastUpdate = GetTickCount();
    m_bRecalcExtent = FALSE;
    m_pQueueHead = NULL;
    m_fFractDistance = 0.0f;
 
    m_hFont = (HFONT) GetStockObject(DEFAULT_GUI_FONT);
    if(NULL != m_hFont)
    {
        SetFontPointSize(DEFAULT_POINTSIZE);
    }
 
    //return CLCDCollection::Initialize();
    return ERROR_SUCCESS;
}
 
 
//************************************************************************
//
// CLCDStreamingText::ResetUpdate
//
//************************************************************************
 
void CLCDStreamingText::ResetUpdate(void)
{
    m_eState = STATE_DELAY;
    m_dwEllapsedTime = 0;
    m_dwLastUpdate = GetTickCount();
    m_pQueueHead = NULL;
    m_fFractDistance = 0.0f;
    
    // remove, re-add, and recalculate the text
    m_bRecalcExtent = TRUE;
    RemoveAllText();
    AddText(m_sText.c_str());
}
 
 
//************************************************************************
//
// CLCDStreamingText::Show
//
//************************************************************************
 
void CLCDStreamingText::Show(BOOL bShow)
{
    CLCDCollection::Show(bShow);
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetText
//
//************************************************************************
 
void CLCDStreamingText::SetText(LPCTSTR szText)
{
    LCDUIASSERT(NULL != szText);
    if(szText && _tcscmp(m_sText.c_str(), szText))
    {
        m_sText.assign(szText);
        m_bRecalcExtent = TRUE;
        ResetUpdate();
    }
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetOrigin
//
//************************************************************************
 
void CLCDStreamingText::SetOrigin(POINT pt)
{
	m_Origin = pt;
	SetOrigin(pt.x, pt.y);
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetOrigin
//
//************************************************************************
 
void CLCDStreamingText::SetOrigin(int nX, int nY)
{
	m_Origin.x = nX;
	m_Origin.y = nY;
 
    LCD_OBJECT_LIST::iterator it = m_Objects.begin();
	while(it != m_Objects.end())
	{
        CLCDBase *pObject = *it++;
        LCDUIASSERT(NULL != pObject);
 
		POINT ptOldOrigin = pObject->GetOrigin();
		pObject->SetOrigin(nX, nY);
 
		if ( (ptOldOrigin.x != nX) && (ptOldOrigin.y != nY) )
        {
            ResetUpdate();
            break;
        }
    }
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetSize
//
//************************************************************************
 
void CLCDStreamingText::SetSize(SIZE& size)
{
    CLCDBase::SetSize(size);
 
    LCD_OBJECT_LIST::iterator it = m_Objects.begin();
	if (it != m_Objects.end())
	{
        CLCDBase *pObject = *it;
		pObject->SetSize(size);
    }
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetSize
//
//************************************************************************
 
void CLCDStreamingText::SetSize(int nCX, int nCY)
{
    SIZE size = { nCX, nCY };
    SetSize(size);  
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetBackgroundMode
//
//************************************************************************
 
void CLCDStreamingText::SetBackgroundMode(int nMode)
{
    m_bRedoColors = TRUE;
    CLCDBase::SetBackgroundMode(nMode);
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetForegroundColor
//
//************************************************************************
 
void CLCDStreamingText::SetForegroundColor(COLORREF crForeground)
{
    m_bRedoColors = TRUE;
    CLCDBase::SetForegroundColor(crForeground);
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetBackgroundColor
//
//************************************************************************
 
void CLCDStreamingText::SetBackgroundColor(COLORREF crBackground)
{
    m_bRedoColors = TRUE;
    CLCDBase::SetBackgroundColor(crBackground);
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetGapText
//
//************************************************************************
 
void CLCDStreamingText::SetGapText(LPCTSTR szGapText)
{
    LCDUIASSERT(NULL != szGapText);
    if(szGapText && _tcscmp(m_sGapText.c_str(), szGapText))
    {
        m_sGapText.assign(szGapText);
        m_bRecalcExtent = TRUE;
    }
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetFont
//
//************************************************************************
 
void CLCDStreamingText::SetFont(LOGFONT& lf)
{
    if (m_hFont)
    {
        DeleteObject(m_hFont);
        m_hFont = NULL;
    }
 
    m_hFont = CreateFontIndirect(&lf);
    m_bRecalcExtent = TRUE;
}
 
 
//************************************************************************
//
// CLCDStreamingText::GetFont
//
//************************************************************************
 
HFONT CLCDStreamingText::GetFont()
{
    return m_hFont;
}
 
 
//************************************************************************
//
// CLCDText::SetFontFaceName
//
//************************************************************************
 
void CLCDStreamingText::SetFontFaceName(LPCTSTR szFontName)
{
    // if NULL, uses the default gui font
    if (NULL == szFontName)
        return;
 
    LOGFONT lf;
    ZeroMemory(&lf, sizeof(lf));
    GetObject(m_hFont, sizeof(LOGFONT), &lf);
 
    DeleteObject(m_hFont);
    m_hFont = NULL;
 
    LCDUI_tcsncpy(lf.lfFaceName, szFontName, LF_FACESIZE);
 
    m_hFont = CreateFontIndirect(&lf);
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetFontPointSize
//
//************************************************************************
 
void CLCDStreamingText::SetFontPointSize(int nPointSize)
{
    LOGFONT lf;
    ZeroMemory(&lf, sizeof(lf));
    GetObject(m_hFont, sizeof(LOGFONT), &lf);
 
    DeleteObject(m_hFont);
    m_hFont = NULL;
 
    lf.lfHeight = -MulDiv(nPointSize, DEFAULT_DPI, 72);
 
    m_hFont = CreateFontIndirect(&lf);
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetFontWeight
//
//************************************************************************
 
void CLCDStreamingText::SetFontWeight(int nWeight)
{
    LOGFONT lf;
    ZeroMemory(&lf, sizeof(lf));
    GetObject(m_hFont, sizeof(LOGFONT), &lf);
 
    DeleteObject(m_hFont);
    m_hFont = NULL;
 
    lf.lfWeight = nWeight;
 
    m_hFont = CreateFontIndirect(&lf);
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetFontColor
//
//************************************************************************
 
void CLCDStreamingText::SetFontColor(COLORREF color)
{
    SetForegroundColor(color);
 
    // Apply it to any existing text objects
    for (size_t i = 0; i < m_Objects.size(); i++)
    {
        m_Objects[i]->SetForegroundColor(color);
    }
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetAlignment
//
// only relevant if no streaming
//************************************************************************
 
void CLCDStreamingText::SetAlignment(int nAlignment)
{
    m_nTextAlignment = nAlignment;
}
 
 
//************************************************************************
//
// CLCDStreamingText::AddText
//
//************************************************************************
 
int CLCDStreamingText::AddText(LPCTSTR szText)
{
 
    CLCDText* pText = new CLCDText;
    pText->Initialize();
    pText->SetText(szText);
    pText->SetOrigin(GetOrigin().x, GetOrigin().y);
    pText->SetLogicalOrigin(GetLogicalOrigin().x, GetLogicalOrigin().y);
    pText->SetSize(GetWidth(), GetHeight());
    pText->SetBackgroundMode(m_nBkMode);
    pText->SetBackgroundColor(m_crBackgroundColor);
    pText->SetFontColor(m_crForegroundColor);
 
    LOGFONT lf;
    GetObject(m_hFont, sizeof(LOGFONT), &lf);
    pText->SetFont(lf);
 
    m_bRecalcExtent = TRUE;
 
    AddObject(pText);
 
 
    if (NULL == m_pQueueHead)
    {
        m_pQueueHead = pText;
    }
 
    // return the zero-based index
    return (int)(m_Objects.size()-1);
}
 
 
//************************************************************************
//
// CLCDStreamingText::RemoveText
//
//************************************************************************
 
void CLCDStreamingText::RemoveText(int nIndex)
{
    LCD_OBJECT_LIST::iterator it = m_Objects.begin();
    int i = 0;
    while(it != m_Objects.end())
    {
        CLCDBase *pObject = *it;
        LCDUIASSERT(NULL != pObject);
 
        if (i == nIndex)
        {
            m_Objects.erase(it);
            if(NULL != pObject)
            {
                delete pObject;
            }
            break;
        }
        
        ++it;
        ++i;
    }
}
 
 
//************************************************************************
//
// CLCDStreamingText::RemoveAllText
//
//************************************************************************
 
void CLCDStreamingText::RemoveAllText()
{
    LCD_OBJECT_LIST::iterator it = m_Objects.begin();
    while(it != m_Objects.end())
    {
        // we need to delete all the text objects that were placed in to m_Objects
        CLCDBase *pObject = *it;
        LCDUIASSERT(NULL != pObject);
 
        if(NULL != pObject)
        {
            delete pObject;
        }
        ++it;
    }
    m_Objects.erase(m_Objects.begin(), m_Objects.end());
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetStartDelay
//
//************************************************************************
 
void CLCDStreamingText::SetStartDelay(DWORD dwMilliseconds)
{
    m_dwStartDelay = dwMilliseconds;
}
 
 
//************************************************************************
//
// CLCDStreamingText::SetSpeed
//
//************************************************************************
 
void CLCDStreamingText::SetSpeed(DWORD dwSpeed)
{
    m_dwSpeed = dwSpeed;
}
 
//************************************************************************
//
// CLCDStreamingText::SetScrollingStep: sets the number of pixels the text 
// will jump when it scrolls
//
//************************************************************************
 
void CLCDStreamingText::SetScrollingStep(DWORD dwStepInPixels)
{
    m_dwStepInPixels = dwStepInPixels;
}
 
//************************************************************************
//
// CLCDStreamingText::OnUpdate
//
//************************************************************************
 
void CLCDStreamingText::OnUpdate(DWORD dwTimestamp)
{
    m_dwEllapsedTime = (dwTimestamp - m_dwLastUpdate);
}
 
 
//************************************************************************
//
// CLCDStreamingText::OnDraw
//
//************************************************************************
 
void CLCDStreamingText::OnDraw(CLCDGfxBase &rGfx)
{
 
    if (m_bRecalcExtent)
    {
        // this just recalculates the text extents
        RecalcTextBoxes(rGfx);
        m_bRecalcExtent = FALSE;
        return;
    }
 
    switch(m_eState)
    {
    case STATE_DELAY:
        if (m_dwEllapsedTime > m_dwStartDelay)
        {
            m_eState = STATE_SCROLL;
            m_dwEllapsedTime = 0;
            m_dwLastUpdate = GetTickCount();
        }
        break;
    case STATE_SCROLL:
        {
            // update the positions
            float fDistance = (float)(m_dwSpeed * m_dwEllapsedTime) / 1000.0f;
            m_dwLastUpdate = GetTickCount();
 
            if (m_Objects.size() > 1)
            {
                // extract any previous fractional remainder
                // and add it to the current distance
                float fTotDistance = (fDistance + m_fFractDistance);
                m_fFractDistance = (fTotDistance >= (float)m_dwStepInPixels) ? (float)(fTotDistance - (int)fTotDistance) : fTotDistance;
 
                if (fTotDistance < 0.0f)
                    fTotDistance = 0.0f;
                if (m_fFractDistance < 0.0f)
                    m_fFractDistance = 0.0f;
 
				if (fTotDistance >= (float)m_dwStepInPixels)
                    ApplyOrigins(-1 * (int)fTotDistance); // left
            }
        }
        break;
    default:
        break;
    }
 
    if( m_bRedoColors )
    {
        LCD_OBJECT_LIST::iterator it = m_Objects.begin();
        while(it != m_Objects.end())
        {
            // we need to delete all the text objects that were placed in to m_Objects
            CLCDBase *pObject = *it;
            LCDUIASSERT(NULL != pObject);
 
            if(NULL != pObject)
            {
                pObject->SetBackgroundMode(m_nBkMode);
                pObject->SetBackgroundColor(m_crBackgroundColor);
                pObject->SetForegroundColor(m_crForegroundColor);
            }
            ++it;
        }
 
        m_bRedoColors = FALSE;
    }
 
    CLCDCollection::OnDraw(rGfx);
}
 
 
//************************************************************************
//
// CLCDStreamingText::C
//
//************************************************************************
 
BOOL CLCDStreamingText::RecalcTextBoxes(CLCDGfxBase &rGfx)
{
 
    // check if we need to add another text box
    LCD_OBJECT_LIST::iterator it = m_Objects.begin();
 
    if (it == m_Objects.end())
        return FALSE;
 
    CLCDBase* pObject = *it;
    CLCDText* pText = (CLCDText*)pObject;
    LCDUIASSERT(NULL != pObject);
    
    LOGFONT lf;
    GetObject(m_hFont, sizeof(LOGFONT), &lf);
    pText->SetFont(lf);
 
    // this will re-evaluate the main text object
    LCDUIASSERT(m_Objects.size() == 1);
    pText->CalculateExtent(TRUE);
 
    if (it != m_Objects.end())
    {
        if (pText->GetHExtent().cx > GetWidth())
        {
 
            pText->SetAlignment(DT_LEFT);
 
            // add a gap
            AddText(m_sGapText.c_str());
            // add another text
            AddText(m_sText.c_str());
            // add last gap
            AddText(m_sGapText.c_str());
        }
        else
        {
            pText->SetAlignment(m_nTextAlignment);
        }
    }
 
    // this will re-evaluate the other text objects
    CLCDCollection::OnDraw(rGfx);
    RecalcTextBoxOrigins();
 
    return TRUE;
}
 
 
//************************************************************************
//
// CLCDStreamingText::RecalcTextBoxOrigins
//
// Puts all the text boxes in order next to each other
//************************************************************************
 
void CLCDStreamingText::RecalcTextBoxOrigins()
{
 
    if (m_Objects.size() <= 1)
        return;
 
    // draw everyone to the left by the offset
    int nOrgOffset = 0;
    LCD_OBJECT_LIST::iterator it = m_Objects.begin();
    while(it != m_Objects.end())
    {
        CLCDBase* pObject = *it;
        CLCDText* pText = (CLCDText*)pObject;
        LCDUIASSERT(NULL != pObject);
 
        pText->SetLogicalSize(pText->GetHExtent().cx, pText->GetHExtent().cy);
 
        // string can be empty which generates zero logical space
        //LCDUIASSERT(pText->GetLogicalSize().cx);
        //LCDUIASSERT(pText->GetLogicalSize().cy);
 
        POINT& ptOrigin = pText->GetLogicalOrigin();
 
		if (nOrgOffset == 0)
		{
			nOrgOffset = pText->GetLogicalOrigin().x;
		}
 
        pText->SetLogicalOrigin(nOrgOffset, ptOrigin.y);
        nOrgOffset += pText->GetHExtent().cx;
 
        ++it;
    }
 
}
 
 
//************************************************************************
//
// CLCDStreamingText::ApplyOrigins
//
//************************************************************************
 
void CLCDStreamingText::ApplyOrigins(int nOffset)
{
    
    // draw everyone to the left by the offset
    LCD_OBJECT_LIST::iterator it = m_Objects.begin();
    while(it != m_Objects.end())
    {
        CLCDBase* pObject = *it;
        CLCDText* pText = (CLCDText*)pObject;
        LCDUIASSERT(NULL != pObject);
 
        POINT& ptOrigin = pText->GetLogicalOrigin();
 
        pText->SetLogicalOrigin(ptOrigin.x + nOffset, ptOrigin.y);
 
        ++it;
    }
 
    // If the active box is no longer visible, 
    // pop it off the push it to the end of the list
    if (abs(m_pQueueHead->GetLogicalOrigin().x) >= m_pQueueHead->GetHExtent().cx)
    {
        RemoveObject(0);
        AddObject(m_pQueueHead);
        RecalcTextBoxOrigins();
        m_pQueueHead = (CLCDText*)RetrieveObject(0);
    }
}
 
 
//** end of LCDStreamingText.cpp *******************************************

V526 The 'wcscmp' function returns 0 if corresponding strings are equal. Consider examining the condition for mistakes.

V526 The 'wcscmp' function returns 0 if corresponding strings are equal. Consider examining the condition for mistakes.

V730 Not all members of a class are initialized inside the constructor. Consider inspecting: m_dwEllapsedTime, m_dwStartDelay, m_dwSpeed, m_dwStepInPixels, m_dwLastUpdate, m_eState, ...

V807 Decreased performance. Consider creating a reference to avoid using the 'pText->GetHExtent()' expression repeatedly.