/******************************************************************************/
/* Bar.CPP: IMPLEMENTATION OF THE CStatBar (Status Bar) CLASS                 */
/*                                                                            */
/******************************************************************************/
/*                                                                            */
/* Methods in this file                                                       */
/*                                                                            */
/*    CStatBar::CStatBar                                                      */
/*    CStatBar::~CStatBar                                                     */
/*    CStatBar::Create                                                        */
/*    CStatBar::OnSetFont                                                     */
/*    CStatBar::DoPaint                                                       */
/*    CStatBar::DrawStatusText                                                */
/*    CStatBar::SetText                                                       */
/*    CStatBar::SetPosition                                                   */
/*    CStatBar::SetSize                                                       */
/*    CStatBar::ClearPosition                                                 */
/*    CStatBar::ClearSize                                                     */
/*    CStatBar::Reset                                                         */
/*    CStatBar::OnPaletteChanged                                                                                          */
/*                                                                            */
/*                                                                            */
/* Functions in this file                                                     */
/*                                                                            */
/*    ClearStatusBarSize                                                      */
/*    ClearStatusBarPosition                                                  */
/*    SetPrompt                                                               */
/*    SetPrompt                                                               */
/*    ShowStatusBar                                                           */
/*    IsStatusBarVisible                                                      */
/*    GetStatusBarHeight                                                      */
/*    InvalidateStatusBar                                                     */
/*    ClearStatusBarPositionAndSize                                           */
/*    ResetStatusBar                                                          */
/*    SetStatusBarPosition                                                    */
/*    SetStatusBarSize                                                        */
/*    SetStatusBarPositionAndSize                                             */
/*                                                                            */
/******************************************************************************/

#include "stdafx.h"
#include "global.h"
#include "pbrush.h"
#include "pbrusfrm.h"

#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif

IMPLEMENT_DYNAMIC( CStatBar, CStatusBar )

#include "memtrace.h"

CStatBar        *g_pStatBarWnd = NULL;

static UINT BASED_CODE indicators[] =
    {
    ID_SEPARATOR,           // status line indicator
    IDB_SBPOS,
    IDB_SBSIZE
    };

BEGIN_MESSAGE_MAP( CStatBar, CStatusBar )
                ON_WM_SYSCOLORCHANGE()
        ON_MESSAGE(WM_SETFONT, OnSetFont)
                ON_MESSAGE(WM_SIZEPARENT, OnSizeParent)
        ON_WM_NCDESTROY()
END_MESSAGE_MAP()

static int miSlackSpace;

/******************************************************************************/

CStatBar::CStatBar()
    {
    m_iBitmapWidth  = 0;
    m_iBitmapHeight = 0;
    miSlackSpace = 0;
    m_iSizeY = 0;
    }

CStatBar::~CStatBar()
        {
        // Ensure that the CControlBar doesn't assert trying access our parent
        // object (CPBFrame) during the destruction of our parent object.
        m_pDockSite = NULL;
        }

/******************************************************************************/

BOOL CStatBar::Create( CWnd* pParentWnd )
    {
    BOOL bRC = TRUE;
    int cxStatBar;  // width of a char in the status bar

    // Create the status Bar Window.
    bRC = CStatusBar::Create(pParentWnd);

    ASSERT (bRC != FALSE);

    if (bRC != FALSE)
        {
        // Set the Pane Indicators.
        bRC = SetIndicators( indicators, sizeof( indicators ) / sizeof( UINT ) );

        ASSERT( bRC != FALSE );

        if (bRC != FALSE)
            {
            TRY
                {
                // Load the Separator Strings
                VERIFY(m_cstringSizeSeparator.LoadString(IDS_SIZE_SEPARATOR));
                VERIFY(m_cstringPosSeparator.LoadString(IDS_POS_SEPARATOR));
                }
            CATCH(CMemoryException,e)
                {
                m_cstringSizeSeparator.Empty();
                m_cstringPosSeparator.Empty();
                }
            END_CATCH

            // Load the Position and Size Bitmaps
            VERIFY(m_posBitmap.LoadBitmap(IDB_SBPOS));
            VERIFY(m_sizeBitmap.LoadBitmap(IDB_SBSIZE));

            if ( (m_posBitmap.GetSafeHandle() != NULL) &&
                 (m_sizeBitmap.GetSafeHandle() != NULL)    )
                {
                //Calculate the size of the pane and set them

                CClientDC dc(this);

                /*DK* What font to select? */
                /*DK* What to do if in foreign Language, Size "0"? */

                cxStatBar = (dc.GetTextExtent(TEXT("0"), 1)).cx;
                BITMAP bmp;

                m_posBitmap.GetObject(sizeof (BITMAP), &bmp);

                m_iBitmapWidth  = bmp.bmWidth;
                m_iBitmapHeight = bmp.bmHeight;

                int iPaneWidth;
                UINT uiID, uiStyle;

                GetPaneInfo( 0, uiID, uiStyle, iPaneWidth) ;
                SetPaneInfo( 0, uiID, SBPS_NORMAL | SBPS_STRETCH, iPaneWidth );

                GetPaneInfo(1, uiID, uiStyle, iPaneWidth);

                if (iPaneWidth < bmp.bmWidth + (SIZE_POS_PANE_WIDTH * cxStatBar))
                    {
                    iPaneWidth = bmp.bmWidth + (SIZE_POS_PANE_WIDTH * cxStatBar);
                    SetPaneInfo(1, uiID, uiStyle, iPaneWidth);
                    }

                GetPaneInfo(2, uiID, uiStyle, iPaneWidth);

                if (iPaneWidth < bmp.bmWidth + (SIZE_POS_PANE_WIDTH * cxStatBar))
                    {
                    iPaneWidth = bmp.bmWidth + (SIZE_POS_PANE_WIDTH * cxStatBar);
                    SetPaneInfo(2, uiID, uiStyle, iPaneWidth);
                    }

                // force a height change
                CFont *pcFontTemp = GetFont();

                // initialize font height etc
                OnSetFont( (WPARAM)(HFONT)pcFontTemp->GetSafeHandle(), 0 );
                }
            else
                {
                bRC = FALSE;
                }
            }
        }
    return bRC;
    }

/******************************************************************************/

void CStatBar::OnNcDestroy( void )
    {
    m_posBitmap.DeleteObject();
    m_sizeBitmap.DeleteObject();

    m_posBitmap.m_hObject = NULL;
    m_sizeBitmap.m_hObject = NULL;

    m_cstringSizeSeparator.Empty();
    m_cstringPosSeparator.Empty();

    CStatusBar::OnNcDestroy();
    }

/******************************************************************************/

CSize CStatBar::CalcFixedLayout(BOOL bStretch, BOOL bHorz)
    {
    CSize size = CStatusBar::CalcFixedLayout( bStretch, bHorz );

    size.cy = m_iSizeY;

    return size;
    }

/******************************************************************************/
/* Change the height of the status bar to allow the bitmaps to   be painted   */
/* in the panes. The height is set in the OnSetFont  OnSetFont method.  Save  */
/* the current border values, change  then call OnSetFont and then reset the  */
/* border values                                                              */
/*                                                                            */
/* This will increase the height of the whole status bar  until the next      */
/* OnSetFont (font change) for the status bar.                                */
/*                                                                            */
/* In Barcore.cpp, the Height of the status bar is set in the  OnSetFont      */
/* method as follows:                                                         */
/*                                                                            */
/*  Height =  = (tm.tmHeight - tm.tmInternalLeading) +                        */
/*              CY_BORDER*4 (which is 2 extra on top, 2                       */
/*              on bottom) - rectSize.Height();                               */
/*                                                                            */
/*  This is really                                                            */
/*    Height = Height of Font + Border between Font and                       */
/*             Pane edges + Border between Pane edges and                     */
/*             Status bar window.                                             */
/*                                                                            */
/*  tm.tmHeight - tm.tmInternalLeading is Font Height CY_BORDER*4 is border   */
/*  between font and pane edges rectSize.Height is Neg of Border between Pane */
/*  and SBar rectSize is set to 0, then the deflated by the border size.      */
/*  Deflating from 0 => negative, and  - negative gives us a positive amount. */
/*                                                                            */
/*  by default m_cyBottomBorder = m_cyTopBorder = 1                           */
/******************************************************************************/
/* We only change the border sizes temporarily for the calculation of the     */
/* status bar height.  We really don't want to change the border sizes, but   */
/* are just using them as a way to affect the size of the whole bar.          */
/******************************************************************************/

LRESULT CStatBar::OnSetFont(WPARAM wParam, LPARAM lParam)
    {

    CRect rect;

    int iTmpcyTopBorder    = m_cyTopBorder;
    int iTmpcyBottomBorder = m_cyBottomBorder;

    m_cyTopBorder = m_cyBottomBorder = 2;
    miSlackSpace = 0;

    // Can't do this in MFC 4
//    lResult = CStatusBar::OnSetFont(wParam, lParam); //initialize font height etc

    rect.SetRectEmpty();
    CalcInsideRect( rect, TRUE ); // will be negative size

    int iBorder = CY_BORDER * 4 - rect.Height();
    int iSize   = m_iSizeY - iBorder;
    int cyTallest = m_iBitmapHeight;
    CDC dc;

    if( dc.CreateIC( TEXT("DISPLAY"), NULL, NULL, NULL ) )
        {
        TEXTMETRIC tm;
                tm.tmHeight=0;
        CFont *font = CFont::FromHandle( (HFONT)wParam );
                if ( font )
                        {
                        CFont *oldFont = dc.SelectObject(font);

                if( dc.GetTextMetrics( &tm ) && tm.tmHeight > cyTallest )
                    cyTallest = tm.tmHeight;

                        if (oldFont)
                                dc.SelectObject(oldFont);
                        }
                dc.DeleteDC();
        }

    if (cyTallest > iSize)
        m_iSizeY     = cyTallest + iBorder;

    if (m_iBitmapHeight > iSize)
        miSlackSpace = m_iBitmapHeight - iSize;

    m_cyTopBorder    = iTmpcyTopBorder;
    m_cyBottomBorder = iTmpcyBottomBorder;

    return 1L;
    }

/******************************************************************************/
/* This routine is overloaded to allow us to paint the bitmaps in the panes.  */
/* If this routine was not here, it would work fine, but no bitmaps would     */
/* appear in the status indicator panes.                                      */
/******************************************************************************/

void CStatBar::DoPaint( CDC* pDC )
    {
    BOOL     bRC;
    CString  cstringText_Pane1;
    CString  cstringText_Pane2;
    CRect    rect_Pane1;
    CRect    rect_Pane2;
    CRgn     cRgn_Pane1;
    CRgn     cRgn_Pane2;
    CBitmap* pOldBitmap;
    UINT     uiStyle_Pane1;
    UINT     uiStyle_Pane2;
    UINT     uiID;
    int      iPaneWidth;
    HDC      hdc = pDC->GetSafeHdc();

    GetItemRect( 1, &rect_Pane1 );  // get pane rect
    GetItemRect( 2, &rect_Pane2 );  // get pane rect

    pDC->ExcludeClipRect( &rect_Pane1 ); // exclude pane rect from paint
    pDC->ExcludeClipRect( &rect_Pane2 ); // exclude pane rect from paint

    CStatusBar::DoPaint( pDC ); // Let Parent Class paint remainder of status bar

    CFont* pfntOld = pDC->SelectObject( GetFont() );

    GetPaneText( 1, cstringText_Pane1 );  // Get the Text for the Pane
    GetPaneText( 2, cstringText_Pane2 );  // The status bar holds the text for us.

    GetPaneInfo( 1, uiID, uiStyle_Pane1, iPaneWidth );
    GetPaneInfo( 2, uiID, uiStyle_Pane2, iPaneWidth );

    uiStyle_Pane1 = SBPS_NORMAL;
    uiStyle_Pane2 = SBPS_NORMAL;

    CDC srcDC; // select current bitmap into a compatible CDC

    bRC = srcDC.CreateCompatibleDC( pDC );

    ASSERT( bRC != FALSE );

    if (bRC != FALSE)
        {
        // Set the Text and Background Colors for a Mono to Color Bitmap
        // Conversion.  These are also set in DrawStatusText, so should not
        // have to reset them for the other bitmap/pane
        COLORREF crTextColor = pDC->SetTextColor( GetSysColor( COLOR_BTNTEXT ) );
        COLORREF crBkColor   = pDC->SetBkColor  ( GetSysColor( COLOR_BTNFACE ) );

        bRC = cRgn_Pane1.CreateRectRgnIndirect( rect_Pane1 );

        ASSERT( bRC != FALSE );

        if (bRC != FALSE)
            {
            pDC->SelectClipRgn( &cRgn_Pane1 ); // set clip region to pane rect

            pOldBitmap = srcDC.SelectObject( &m_posBitmap );

            rect_Pane1.InflateRect( -CX_BORDER, -CY_BORDER ); // deflate => don't paint on the borders

            pDC->BitBlt( rect_Pane1.left,    rect_Pane1.top,
                         rect_Pane1.Width(), rect_Pane1.Height(),
                         &srcDC, 0, 0, SRCCOPY ); // BitBlt to pane rect
            srcDC.SelectObject( pOldBitmap );

            rect_Pane1.InflateRect( CX_BORDER, CY_BORDER ); // Inflate back for drawstatustext

            // paint the borders and the text.
            DrawStatusText( hdc, rect_Pane1, cstringText_Pane1, uiStyle_Pane1,
                                                           m_iBitmapWidth + 1 );
            }

        cRgn_Pane2.CreateRectRgnIndirect(rect_Pane2);

        ASSERT( bRC != FALSE );

        if (bRC != FALSE)
            {
            pDC->SelectClipRgn(&cRgn_Pane2); // set clip region to pane rect

            pOldBitmap = srcDC.SelectObject(&m_sizeBitmap);
            rect_Pane2.InflateRect(-CX_BORDER, -CY_BORDER); // deflate => don't paint on the borders
            pDC->BitBlt(rect_Pane2.left, rect_Pane2.top, rect_Pane2.Width(),
                        rect_Pane2.Height(), &srcDC, 0, 0, SRCCOPY); // BitBlt to pane rect
            srcDC.SelectObject(pOldBitmap);
            rect_Pane2.InflateRect(CX_BORDER, CY_BORDER); // Inflate back for drawstatustext
            // DrawStatusText will paint the borders and the text.
            DrawStatusText(hdc, rect_Pane2, cstringText_Pane2, uiStyle_Pane2, m_iBitmapWidth+1);
            }
        pDC->SetTextColor( crTextColor );
        pDC->SetBkColor  ( crBkColor   );
        }
    if (pfntOld != NULL)
        pDC->SelectObject( pfntOld );
    }

/******************************************************************************/
/* Partially taken from BARCORE.CPP DrawStatusText method of CStatusBar.      */
/* Last parameter was added                                                   */
/*                                                                            */
/* This will allow us to output the text indented the space amount for our    */
/* bitmap.  Normally, this routine puts the text left alligned to the pane.   */
/******************************************************************************/

void PASCAL CStatBar::DrawStatusText( HDC    hDC,
                                      CRect const& rect,
                                      LPCTSTR lpszText,
                                      UINT   nStyle,
                                      int    iIndentText )
    {
    ASSERT(hDC != NULL);

    CBrush* cpBrushHilite;
    CBrush* cpBrushShadow;
    HBRUSH  hbrHilite = NULL;
    HBRUSH  hbrShadow = NULL;

    if (! (nStyle & SBPS_NOBORDERS))
        {
        if (nStyle & SBPS_POPOUT)
            {
            // reverse colors
            cpBrushHilite = GetSysBrush( COLOR_BTNSHADOW    );
            cpBrushShadow = GetSysBrush( COLOR_BTNHIGHLIGHT );
            }
        else
            {
            // normal colors
            cpBrushHilite = GetSysBrush( COLOR_BTNHIGHLIGHT );
            cpBrushShadow = GetSysBrush( COLOR_BTNSHADOW    );
            }

        hbrHilite = (HBRUSH)cpBrushHilite->GetSafeHandle();
        hbrShadow = (HBRUSH)cpBrushShadow->GetSafeHandle();
        }

    // background is already grey
    UINT nOpts           = ETO_CLIPPED;
    int nOldMode         = SetBkMode   ( hDC, OPAQUE );
    COLORREF crTextColor = SetTextColor( hDC, GetSysColor( COLOR_BTNTEXT ) );
    COLORREF crBkColor   = SetBkColor  ( hDC, GetSysColor( COLOR_BTNFACE ) );

    // Draw the hilites
    if (hbrHilite)
        {
        HGDIOBJ hOldBrush = SelectObject( hDC, hbrHilite );

        if (hOldBrush)
            {
            PatBlt( hDC, rect.right, rect.bottom, -(rect.Width() - CX_BORDER),
                                                        -CY_BORDER, PATCOPY );
            PatBlt( hDC, rect.right, rect.bottom, -CX_BORDER,
                                      -(rect.Height() - CY_BORDER), PATCOPY );
            SelectObject( hDC, hOldBrush );
            }
        }

    if (hbrShadow)
        {
        HGDIOBJ hOldBrush = SelectObject( hDC, hbrShadow );

        if (hOldBrush)
            {
            PatBlt( hDC, rect.left, rect.top, rect.Width(), CY_BORDER, PATCOPY );
            PatBlt( hDC, rect.left, rect.top,
                                   CX_BORDER, rect.Height(), PATCOPY );
            SelectObject( hDC, hOldBrush );
            }
        }

    // We need to adjust the rect for the ExtTextOut, and then adjust it back
    // just support left justified text
    if (lpszText != NULL && !(nStyle & SBPS_DISABLED))
        {
        CRect rectText( rect );

        rectText.InflateRect( -2 * CX_BORDER, -CY_BORDER );

        // adjust left edge for indented Text
        rectText.left += iIndentText;

            // align on bottom (since descent is more important than ascent)
        SetTextAlign( hDC, TA_LEFT | TA_BOTTOM );

        if (miSlackSpace > 0)
            rectText.InflateRect( 0, -(miSlackSpace / 2) );

        ExtTextOut( hDC, rectText.left, rectText.bottom,
                 nOpts, &rectText, lpszText, lstrlen( lpszText ), NULL );
        }

    SetTextColor( hDC, crTextColor );
    SetBkColor  ( hDC, crBkColor   );
    }

/******************************************************************************/

BOOL CStatBar::SetText(LPCTSTR szText)
    {
    BOOL bRC = TRUE;

    if (theApp.InEmergencyState())
        {
        bRC = FALSE;
        }
    else
        {
        bRC = SetPaneText(0, szText);
        }

    return bRC;
    }

/******************************************************************************/

BOOL CStatBar::SetPosition(const CPoint& pos)
    {
    BOOL bRC = TRUE;
    int cch;
    TCHAR szBuf [20];

    cch = wsprintf(szBuf, TEXT("%d~%d"), pos.x, pos.y);

    for (int i = 0; i < cch; i++)
        if (szBuf[i] == TEXT('~'))
            {
            szBuf[i] = m_cstringPosSeparator[0];
            break;
            }

    ASSERT (cch != 0);

    if (cch != 0)
        {
        bRC = SetPaneText(1, szBuf);
        }
    else
        {
        bRC = FALSE;
        }

    return bRC;
    }

/******************************************************************************/

BOOL CStatBar::SetSize(const CSize& size)
    {
    BOOL bRC = TRUE;
    int cch;
    TCHAR szBuf [20];

    cch = wsprintf( szBuf, TEXT("%d~%d"), size.cx, size.cy );

    for (int i = 0; i < cch; i++)
        if (szBuf[i] == TEXT('~'))
            {
            szBuf[i] = m_cstringSizeSeparator[0];
            break;
            }

    ASSERT (cch != 0);

    if (cch != 0)
        bRC = SetPaneText(2, szBuf);
    else
        bRC = FALSE;

    return bRC;
    }

/******************************************************************************/

BOOL CStatBar::ClearPosition()
    {
    BOOL bRC = TRUE;
    bRC = SetPaneText(1, TEXT(""));  // clear the position
    return bRC;
    }

/******************************************************************************/

BOOL CStatBar::ClearSize()
    {
    BOOL bRC = TRUE;
    bRC = SetPaneText(2, TEXT(""));  // clear the size
    return bRC;
    }


/******************************************************************************/

BOOL CStatBar::Reset()
    {
    return ClearPosition() && ClearSize();
    }

/******************************************************************************/

void CStatBar::OnSysColorChange()
        {
        CStatusBar::OnSysColorChange();
        InvalidateRect(NULL,FALSE);
        }

/******************************************************************************/

void ClearStatusBarSize()
    {
        ASSERT(g_pStatBarWnd);
    g_pStatBarWnd->ClearSize();
    }

/******************************************************************************/

void ClearStatusBarPosition()
    {
        ASSERT(g_pStatBarWnd);
    g_pStatBarWnd->ClearPosition();
    }

/******************************************************************************/

void SetPrompt(LPCTSTR szPrompt, BOOL bRedrawNow)
    {
        ASSERT(g_pStatBarWnd);
    g_pStatBarWnd->SetText(szPrompt);
    if (bRedrawNow)
        g_pStatBarWnd->UpdateWindow();
    }

/******************************************************************************/

void SetPrompt(UINT nStringID, BOOL bRedrawNow)
    {
        ASSERT(g_pStatBarWnd);
    CString str;
    VERIFY(str.LoadString(nStringID));

    g_pStatBarWnd->SetText(str);

    if (bRedrawNow)
        g_pStatBarWnd->UpdateWindow();
    }

/******************************************************************************/

void ShowStatusBar(BOOL bShow /* = TRUE */)
    {
        ASSERT(g_pStatBarWnd);
    g_pStatBarWnd->ShowWindow(bShow ? SW_SHOWNOACTIVATE : SW_HIDE);
    }

/******************************************************************************/

BOOL IsStatusBarVisible()
    {
        ASSERT(g_pStatBarWnd);
    return (g_pStatBarWnd->GetStyle() & WS_VISIBLE) != 0;
    }

/******************************************************************************/

int GetStatusBarHeight()
    {
        ASSERT(g_pStatBarWnd);
    CRect rect;
    g_pStatBarWnd->GetWindowRect(rect);
    return rect.Height();
    }

/******************************************************************************/

void InvalidateStatusBar(BOOL bErase /* = FALSE */)
    {
        ASSERT(g_pStatBarWnd);
    g_pStatBarWnd->Invalidate(bErase);
    }

/******************************************************************************/

void ClearStatusBarPositionAndSize()
    {
        ASSERT(g_pStatBarWnd);
    g_pStatBarWnd->ClearSize();
    g_pStatBarWnd->ClearPosition();
    }

/******************************************************************************/

void ResetStatusBar()
    {
        ASSERT(g_pStatBarWnd);
    g_pStatBarWnd->Reset();
    }

/******************************************************************************/

void SetStatusBarPosition(const CPoint& pos)
    {
        ASSERT(g_pStatBarWnd);
        if ( ::IsWindow(g_pStatBarWnd->m_hWnd) )
        g_pStatBarWnd->SetPosition(pos);
    }

/******************************************************************************/

void SetStatusBarSize(const CSize& size)
    {
        ASSERT(g_pStatBarWnd);
    if ( ::IsWindow( g_pStatBarWnd->m_hWnd) )
        g_pStatBarWnd->SetSize(size);
    }

/******************************************************************************/

void SetStatusBarPositionAndSize(const CRect& rect)
    {
        ASSERT(g_pStatBarWnd);
    g_pStatBarWnd->SetPosition(((CRect&)rect).TopLeft());
    g_pStatBarWnd->SetSize(rect.Size());
    }

/******************************************************************************/

LRESULT CStatBar::OnSizeParent(WPARAM wParam, LPARAM lParam)
{
        LRESULT lRes = CStatusBar::OnSizeParent(wParam, lParam);

        return(lRes);
}