
/***************************************************************************
**
**    $Header:   D:/PICSLDV/SRC/LOG/STKLOCAL.CPP   1.14   09 Jan 1997 14:25:36   ZJRD  $
**
**    $Log:   D:/PICSLDV/SRC/LOG/STKLOCAL.CPP  $
** 
**    Rev 1.14   09 Jan 1997 14:25:36   ZJRD
** No change.
** 
**    Rev 1.13   13 Dec 1996 11:20:28   ZJRD
** No change.
** 
**    Rev 1.12   22 Nov 1996 11:01:28   ZJRD
** No change.
** 
**    Rev 1.11   11 Nov 1996 12:48:44   ZJRD
** No change.
** 
**    Rev 1.10   06 Nov 1996 13:00:42   ZJRD
** No change.
** 
**    Rev 1.9   02 Nov 1996 09:46:10   ZJRD
** No change.
** 
**    Rev 1.8   30 Oct 1996 12:48:24   ZJRD
** No change.
** 
**    Rev 1.7   28 Oct 1996 09:41:22   ZJRD
** PIC/SLD Version 0.92
** 
**    Rev 1.6   21 Oct 1996 09:15:44   ZJRD
** No change.
** 
**    Rev 1.5   09 Oct 1996 13:45:18   ZJRD
** No change.
** 
**    Rev 1.4   23 Sep 1996 10:31:40   ZJRD
** No change.
** 
**    Rev 1.3   06 Sep 1996 13:45:26   ZJRD
** No change.
** 
**    Rev 1.2   02 Sep 1996 11:26:32   ZJRD
** No change.
** 
**    Rev 1.1   15 Aug 1996 10:05:06   ZJRD
** No change.
** 
**    Rev 1.0   13 Aug 1996 09:17:58   ZJRD
** Initial revision.
** 
****************************************************************************/

/////////////////////////////////////////////////////////////////////////////
//
//  File name: STKLOCAL.CPP
//
//  Description: The implementation file for the class: CStackLocalView.
//
//  Author: Chen Jun
//
//  Date: 07/29/95
//
//  Modification:
//      1. 07/29/95, Initial version of the class: CStackLocalView.
//
/////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////
// Include files.
#include "stdafx.h"
#include "resource.h"

#include <limits.h>

#include "symblsvr.h"
#include "stkinit.h"
#include "varinit.h"

#include "stkobj.h"

#include "varpane.h"

#include "stkdoc.h"
#include "stklocal.h"
#include "stkfrm.h"
#include "stkexp.h"

#include "colors.h"
#include "hosterrs.h"

/////////////////////////////////////////////////////////////////////////////
// Debug flags.
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif


/////////////////////////////////////////////////////////////////////////////
// External variables.
extern CStringList* pStkSearchPatternList;  // Defined in VARSRCH.CPP
extern CStringList* pStkTypeCastList;       // Defined in VARCAST.CPP
extern CPtrList* pStkAddLocalList;          // Defined in STKSELE.CPP
extern CPtrList* pStkRemoveLocalList;       // Defined in STKSELE.CPP


extern CMDIChildWnd* pStackWnd;


/////////////////////////////////////////////////////////////////////////////
// Global variables.
CStackLocalView* pStkLocal;
BOOL bStkLocalGetFocus = FALSE;


/////////////////////////////////////////////////////////////////////////////
// Global function prototypes.
void StkUpdateLocalWindow(int nLevel);
void StkSimpleUpdateLocalWindow(void);

BOOL StkIsEmpty(void);
void StkGetHighlightName(CString& strName);
void StkGetHighlightValue(CString& strValue);
BOOL StkModifyValue(CString& strNewValue);


/////////////////////////////////////////////////////////////////////////////
// External function prototypes.


/////////////////////////////////////////////////////////////////////////////
// Public functions.

// Update only when deleting or adding a local variable.
void StkSimpleUpdateLocalWindow(void)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );
    
    // Update Local window.
    ::pStkLocal->UpdateLocalWindow();
}

// Update the local window according to level number.
void StkUpdateLocalWindow(int nLevel)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );
    
    // Reset all local & Update the local window.
    ::pStkLocal->UpdateLocalWindow(nLevel);
}

// Detect is the Local window empty?
BOOL StkIsEmpty(void)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );
    
    // Detect is the Variable window empty?
    return ( ::pStkLocal->IsEmpty() );
}

// Get the highlight variable name.
void StkGetHighlightName(CString& strName)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );
    
    // Get the highlight variable name.
    ::pStkLocal->GetHighlightName(strName);
}

// Get the highlight variable value.
void StkGetHighlightValue(CString& strValue)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );
    
    // Get the highlight variable value.
    ::pStkLocal->GetHighlightValue(strValue);
}

// Modify the variable's value and update the whole Variable window.
BOOL StkModifyValue(CString& strNewValue)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );
    
    // Modify the value.
    return (::pStkLocal->ModifyValue(strNewValue));
}

// Search the token from head.
void StkSearchToken(void)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );
    
    // Search the token.
    if ( !::pStkSearchPatternList->IsEmpty() ) {
        ::pStkLocal->SearchToken(::pStkSearchPatternList->GetTail());
    }
}

// Search the token from the next line.
void StkSearchNextToken(void)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );
    
    // Search the token.
    if ( !::pStkSearchPatternList->IsEmpty() ) {
        ::pStkLocal->SearchNextToken(::pStkSearchPatternList->GetTail());
    }
}

// Type casing.
void StkTypeCast(void)
{
    // Assertion of the input parameters.
    ASSERT( ::pStkLocal );

    // Type casting.
    if ( !::pStkTypeCastList->IsEmpty() ) {
        ::pStkLocal->TypeCast(::pStkTypeCastList->GetTail());
    }
}


/////////////////////////////////////////////////////////////////////////////
// CStackLocalView

IMPLEMENT_DYNCREATE(CStackLocalView, CView)

/////////////////////////////////////////////////////////////////////////////
// CStackLocalView construction, initialization and destruction.

// Load bitmaps.
BOOL CStackLocalView::AllocateBitmap(void)
{
    // Assertion of the input parameters.

    // Load Invalid bitmap.
    if( !m_bmpInvalid.LoadBitmap(IDB_VAR_INVALID) ) {
        m_nErrorID = bmpLoad;
        DisplayErrorMessage();
        ASSERT( FALSE );
        return (FALSE);
    }

    // Load Simple bitmap.
    if( !m_bmpSimple.LoadBitmap(IDB_VAR_SIMPLE) ) {
        m_nErrorID = bmpLoad;
        DisplayErrorMessage();
        ASSERT( FALSE );
        return (FALSE);
    }

    // Load Open bitmap.
    if( !m_bmpOpen.LoadBitmap(IDB_VAR_OPEN) ) {
        m_nErrorID = bmpLoad;
        DisplayErrorMessage();
        ASSERT( FALSE );
        return (FALSE);
    }

    // Load Close bitmap.
    if( !m_bmpClose.LoadBitmap(IDB_VAR_CLOSE) ) {
        m_nErrorID = bmpLoad;
        DisplayErrorMessage();
        ASSERT( FALSE );
        return (FALSE);
    }
    
    return (TRUE);
}

// Release bitmaps.
void CStackLocalView::ReleaseBitmap(void)
{
    // Assertion of the input parameters.

    // Delete Invalid bitmap.
    ASSERT( m_bmpInvalid.m_hObject );
    if ( m_bmpInvalid.m_hObject ) {
        m_bmpInvalid.DeleteObject();
    }

    // Delete Simple bitmap.
    ASSERT( m_bmpSimple.m_hObject );
    if ( m_bmpSimple.m_hObject ) {
        m_bmpSimple.DeleteObject();
    }

    // Delete Open bitmap.
    ASSERT( m_bmpOpen.m_hObject );
    if ( m_bmpOpen.m_hObject ) {
        m_bmpOpen.DeleteObject();
    }

    // Delete Close bitmap.
    ASSERT( m_bmpClose.m_hObject );
    if ( m_bmpClose.m_hObject ) {
        m_bmpClose.DeleteObject();
    }
}

// Initialize the class member.
void CStackLocalView::InitialMember(void)
{
    // Initial class member.
    m_nErrorID = noError;
    
    m_nMaxRow = 0;
    m_nMaxCol = 0;
    
    m_nCurCol = 0;
    m_nCurRow = 0;

// m_nRowSize;
// m_nColSize;
   m_nStartRow = 0;
   m_nStartCol = 0;
                  
    m_xCaret = 0;
    m_yCaret = 0;
    
    m_nClientLine = 0;
    
    m_bCaretOn = FALSE;

    m_nScrollBox = 0;
    m_bAdjustScrollBox = FALSE;
    m_bAdjustScrollRange = FALSE;

    m_nHScroll = 0;
    m_nVScroll = 0;   
}

void CStackLocalView::ClearMember(void)
{
    // Initial class member.
    m_nErrorID = noError;
    
    m_nMaxRow = 0;
    m_nMaxCol = 0;
    
    m_nCurCol = 0;
    m_nCurRow = 0;

// m_nRowSize;
// m_nColSize;
   m_nStartRow = 0;
   m_nStartCol = 0;
                  
    m_xCaret = 0;
    m_yCaret = 0;
    
    m_nClientLine = 0;
}

// Construction.
CStackLocalView::CStackLocalView()
{
    // Initial.
    m_nFocus = 0;
    InitialMember();
    
    // Load bitmap.
    if ( !AllocateBitmap() ) {
        m_nErrorID = insufficientMemory;
        DisplayErrorMessage();
        ASSERT( FALSE );
        return;
    }

    // Set the global pointer.
    ::pStkLocal = this;
}

// Destruction.
CStackLocalView::~CStackLocalView()
{
    // Release bitmap.
    ReleaseBitmap();
}


/////////////////////////////////////////////////////////////////////////////
// Public implementation.

// Reset all the local variables & Update local window.
void CStackLocalView::UpdateLocalWindow(int nLevel)
{
    // Assertion of the input parameters.
    ASSERT( nLevel >= -1 );
    
    // Delete all local variables.
    ::Stk_DeleteAllLocal();

    // Remove all the local list.
    if ( !::pStkAddLocalList->IsEmpty() ) {
        ::pStkAddLocalList->RemoveAll();
    }

    if ( !::pStkRemoveLocalList->IsEmpty() ) {
        ::pStkRemoveLocalList->RemoveAll();
    }

    // Get the current local variables.
    if ( nLevel > -1 ) {
        // Initialize the Stack server.
        if ( 0 != ::Stk_InitStkLocalVar(nLevel) ) {
            //m_nErrorID = invalidLevel;
            //DisplayErrorMessage();
        }
        else {
            // Get the local variables' information.
            SYM_DESCRIPTOR ulDesc;
            CString strName;
            while ( 0 == ::Stk_GetStkLocalVar(ulDesc, strName) ) {
                if ( 0 != ulDesc ) {
                    ::pStkAddLocalList->AddTail(new CStkObject(ulDesc, strName));
                    if ( 0 != ::Stk_AddLocalVar(ulDesc, strName) ) {
                        ::pStkAddLocalList->RemoveTail();
                    }
                }
                else {
                    break;
                }
            }
        }
    }
        
    // Refresh the whole window.
    ClearMember();
    m_bAdjustScrollRange = TRUE;
   ResizeScroll();
    
    // Set focus to the upper pane.
//    ((CStackFrame* )((CSplitterWnd* )GetParent())->GetParent())->
//    SetActiveView((CView* )((((CStackFrame* )
//    ((CSplitterWnd* )GetParent())->GetParent())->m_wndSplitter).GetPane(0,0)));
}

// Update Local window only.
void CStackLocalView::UpdateLocalWindow(void)
{
    // Assertion of the input parameters.

    // Refresh the whole window.
    ClearMember();
    m_bAdjustScrollRange = TRUE;
   ResizeScroll();
}

// Delete a boot node.
void CStackLocalView::DeleteOneNode(void)
{
    // Assertion of the input parameters.
    ASSERT( m_nCurRow >= 0 );
    
    // Get the current node.
    if ( 0 >= m_nMaxRow ) return;
    if ( 0 == ::Stk_VarLineToNode(m_nCurRow, m_pVarNode) ) {
        if ( m_pVarNode ) {
            int nDelLine = m_pVarNode->line;
            int nListPos;
            ::Stk_NodeToPos(m_pVarNode,nListPos);
            if ( 0 != ::Stk_Delete(m_pVarNode) ) {
                return;
            }
            else {
                // Global list operation.
//                POSITION pos = ::pStkAddLocalList->FindIndex(m_nCurRow);
                POSITION pos = ::pStkAddLocalList->FindIndex(nListPos);
                CStkObject* pObj = 
                    (CStkObject* )::pStkAddLocalList->GetAt(pos);
                ::pStkRemoveLocalList->AddTail(pObj);
                ::pStkAddLocalList->RemoveAt(pos);
                // Update m_nCurRow.
                if (0 != ::Stk_VarLineToNode(m_nCurRow, m_pVarNode)) {
                    m_nCurRow = max(m_nCurRow-1, 0);
                    if ( m_nCurRow < m_nStartRow ) {
                     m_nStartRow = m_nCurRow;
                    }
                }
             m_nCurCol = 0;
             m_bAdjustScrollRange = TRUE;
            ResizeScroll();
            }
        }
    }
}

// Detect is the Variable window empty?
BOOL CStackLocalView::IsEmpty(void)
{
    // Assertion of the input parameters.
    
    // Detect the maximum number of the variables in the Variable window.
    return ( (0>=m_nMaxRow) );
}

// Get the highlight variable name.
void CStackLocalView::GetHighlightName(CString& strName)
{
    // Assertion of the input parameters.
    
    // Get the current node.
    if ( 0 == ::Stk_VarLineToNode(m_nCurRow, m_pVarNode) ) {
        if ( m_pVarNode ) {
            strName = m_pVarNode->name;
        }
        else {
            strName = "";
        }
    }
    else {
        strName = "";
    }
}

// Get the highlight variable value.
void CStackLocalView::GetHighlightValue(CString& strValue)
{
    // Assertion of the input parameters.
    
    // Get the current node.
    if ( 0 == ::Stk_VarLineToNode(m_nCurRow, m_pVarNode) ) {
        if ( m_pVarNode ) {
            strValue = m_pVarNode->value;
        }
        else {
            strValue = "";
        }
    }
    else {
        strValue = "";
    }
}

// Search the token.
void CStackLocalView::SearchToken(CString& strToken)
{
    // Assertion of the input parameters.
    
    // Search the token from the Variable server.
    int nLastLine;
    char* pszToken = strToken.GetBuffer(strToken.GetLength());
    if ( 0 == ::Stk_Search(pszToken, nLastLine) ) {
        if ( m_nCurRow != nLastLine ) {
         m_nCurRow = nLastLine;
         if ( (m_nStartRow>nLastLine) || (m_nStartRow+m_nRowSize<=nLastLine) ) {
            m_nStartRow = nLastLine;
         }
          m_bAdjustScrollBox = TRUE;
         ResizeScroll();
        }
    }
    else {
        ::AfxMessageBox("No token matched.");
    }
}

// Search the next token.
void CStackLocalView::SearchNextToken(CString& strToken)
{
    // Assertion of the input parameters.
    ASSERT( m_nCurRow >= 0 );

    // Search the token from the Variable server.
    char* pszToken = strToken.GetBuffer(strToken.GetLength());
    int nLastLine;
    if ( 0 == ::Stk_SearchNext(pszToken, m_nCurRow+1, nLastLine) ) {
        if ( m_nCurRow != nLastLine ) {
         m_nCurRow = nLastLine;
         if ( (m_nStartRow>nLastLine) || (m_nStartRow+m_nRowSize<=nLastLine) ) {
            m_nStartRow = nLastLine;
         }
          m_bAdjustScrollBox = TRUE;
         ResizeScroll();
        }
    }
    else {
        ::AfxMessageBox("No token matched.");
    }
}

// Modify the variable's value and update the whole Variable window.
BOOL CStackLocalView::ModifyValue(CString& strNewValue)
{
    // Assertion of the input parameters.
    ASSERT( m_nCurRow >= 0 );

    // Modify the value by calling Variable's server.
    if ( 0 == ::Stk_IsLegalValue(m_nCurRow, strNewValue) ) {
        ::Stk_ModifyValue(m_nCurRow, strNewValue);
        
        // Update all the local variables by calling from Stack server.
        ::Stk_UpdataAllLocal();
    
        // Update the whole Variable window.
        m_bAdjustScrollRange = TRUE;
      ResizeScroll();
        return (TRUE);
    }
    else {
        AfxMessageBox("Illegal value.");
        return (FALSE);
    }
}

// Type casing.
void CStackLocalView::TypeCast(CString& /* strTypeCast */)
{
    // Assertion of the input parameters.
    ASSERT( m_nCurRow >= 0 );

}


/////////////////////////////////////////////////////////////////////////////
// Common function.

// Show the specific bitmap.
void CStackLocalView::DisplayBitmap(CDC* pDC, const int xPos, const int yPos)
{
    // Assertion of the input parameters.
    ASSERT( pDC );

    // Prepare DC.
    CDC dc;
    CBitmap* bmpOld;
    dc.CreateCompatibleDC(pDC);

    // Select the bitmap.
    if ( invalidSign == m_nBmpStatus ) {
        bmpOld = dc.SelectObject(&m_bmpInvalid);
    }
    else if ( simpleSign == m_nBmpStatus ) {
        bmpOld = dc.SelectObject(&m_bmpSimple);
    }
    else if ( openSign == m_nBmpStatus ) {
        bmpOld = dc.SelectObject(&m_bmpOpen);
    }
    else if ( closeSign == m_nBmpStatus ) {
        bmpOld = dc.SelectObject(&m_bmpClose);
    }

    // Draw the bitmap.
    pDC->BitBlt(xPos, yPos, bmpWidth, bmpHeight, &dc, 0, 0, SRCCOPY);
    dc.SelectObject(bmpOld);
}

// Display the error message.
void CStackLocalView::DisplayErrorMessage(void) const
{
    // Assertion of the input parameters.
    ASSERT( m_nErrorID > noError && m_nErrorID < maxError );
    
    // Create the error message table.
//    char* pszErrorMessage[] = {
//        "",
//        "Insufficient memory.",
//        "Cannot load bitmap for the Variable window.",
//        "Invalid trace back level.",
//        "Error Status."
//    };
    
    // Show the error message.
//    AfxMessageBox(pszErrorMessage[m_nErrorID]);
    CString strErr;
    if ( GOOD == ErrGetErrorText(ER_STK_BASE+m_nErrorID, strErr) ) {
//        ShowLine(strErr.GetBuffer( strErr.GetLength() ) );
    	AfxMessageBox( strErr );
    }
}

// Detect the variable format by referring to Variable server.
void CStackLocalView::DetectVarFormat(void)
{
    // Assertion of the input parameters.
    
    // Detect the variable format.
    if ( !m_pVarNode ) {
        m_nBmpStatus = invalidSign;
      return;
    }
      
    int nFlag = m_pVarNode->flag;
    if ( nFlag & VAR_NOVALID ) {
        m_nBmpStatus = invalidSign;
    }
    else if ( nFlag & VAR_COMPLEX ) {
        if ( nFlag & VAR_EXTEND ) {
            m_nBmpStatus = openSign;
        }
        else {
            m_nBmpStatus = closeSign;
        }
    }
    else {
        m_nBmpStatus = simpleSign;
    }
}

void CStackLocalView::ResizeScrollBarRange(void)
{
    unsigned int nRow = 0;
    m_pVarNode = ::Stk_GetListHeader();
    m_nMaxCol = 0;
    while ( m_pVarNode ) {
        // Detect the variable format.
        DetectVarFormat();
            
        // Get name from Variable server.
        m_strName = m_pVarNode->name;
    
        // Get value from Variable server.
        if ( invalidSign == m_nBmpStatus ) {
            m_strValue = "Invalid Variable.";
        }
        else {
            m_strValue = m_pVarNode->value;
        }
    
        // Add to the whole result string.
        m_strResult = m_strName + " = " + m_strValue;
    
        // Adjust to the begin column.
        int nIndent = m_pVarNode->num * 2;
            
        // Adjust the scroll size.
        m_nMaxCol = max(m_nMaxCol, nIndent + 2 + m_strResult.GetLength());
        nRow++;
    
        // Point to the son node.
        if ( m_pVarNode->son ) {
            m_pVarNode = m_pVarNode->son;
            continue;
        }
            
        // Point to the next node.
        if ( m_pVarNode->next ) {
            m_pVarNode = m_pVarNode->next;
        }
        else {
            while ( m_pVarNode = m_pVarNode->father ) {
                if ( m_pVarNode->next ) {
                    m_pVarNode = m_pVarNode->next;
                    break;
                }
            }
        }
    }
    m_nMaxRow = nRow;
}

void CStackLocalView::ResizeScroll()
{
   int nFlag = 0;
   
    if ( m_bAdjustScrollRange ) {
      ResizeScrollBarRange();
      nFlag = 1;
    }
    if ( m_bAdjustScrollBox ) {
      nFlag = 1;
    }

    if ( m_nRowSize >= m_nMaxRow ) {
        if ( m_nVScroll ) {
            ShowScrollBar(SB_VERT , FALSE);
            m_nVScroll = 0;
            m_nStartRow = 0;
        } 
    }   
    else {
        ShowScrollBar(SB_VERT , TRUE);
        SetScrollRange(SB_VERT,0,m_nMaxRow-1,TRUE);
        SetScrollPos(SB_VERT,m_nStartRow,TRUE);
        m_nVScroll = 1;
    }
    
    if ( m_nColSize >= m_nMaxCol ) {
        if ( m_nHScroll ) {
            m_nHScroll = 0;  
            m_nStartCol = 0;
            ShowScrollBar(SB_HORZ , FALSE);
        }
    }
    else {
        m_nHScroll = 1;
        ShowScrollBar(SB_HORZ , TRUE);
        SetScrollRange(SB_HORZ,0,m_nMaxCol,TRUE);
        SetScrollPos(SB_HORZ,m_nStartCol,TRUE);
    }             
    m_bAdjustScrollRange = FALSE;
    m_bAdjustScrollBox = FALSE;
    if ( nFlag ) {
      Invalidate();
    }           
}

// Output all the variable to the window.
void CStackLocalView::OutputAllVariable(CDC* pDC)
{
    // Assertion of the input parameters.
    ASSERT( m_pVarNode );
    ASSERT( m_nMaxRow >= 0 );
    ASSERT( m_nMaxCol >= 0 );
   int nLine = 0;
   int xPos , yPos;
    
    if ( 0 != ::Stk_VarLineToNode(m_nStartRow, m_pVarNode) ) {
      return ;
    } 

   TurnOffCaret();

    // Loop until end or limit.
    while ( m_pVarNode && nLine <= m_nRowSize ) {
        // Detect the variable format.
        DetectVarFormat();
            
        // Get name from Variable server.
        m_strName = m_pVarNode->name;
    
        // Get value from Variable server.
        if ( invalidSign == m_nBmpStatus ) {
            m_strValue = "Invalid Variable.";
        }
        else {
            m_strValue = m_pVarNode->value;
        }
    
        // Add to the whole result string.
        m_strResult = m_strName + " = " + m_strValue;
    
        // Adjust to the begin column.
        int nIndent = m_pVarNode->num * 2;
            
        // Show the bitmap.
        xPos = m_nFontWidth*(nIndent-m_nStartCol);
        yPos = m_nFontHeight*nLine;
        DisplayBitmap(pDC, xPos , yPos);
    
        // Show the result.
        xPos = m_nFontWidth*(nIndent+2-m_nStartCol);
        if ( m_nStartRow+nLine == m_nCurRow ) {
            // Mark the variable token.
            pDC->SetBkColor(PALETTEINDEX(COLOR_YELLOW));
            pDC->SetTextColor(PALETTEINDEX(COLOR_BLACK));
            pDC->TextOut(xPos , yPos , m_strName);
            pDC->SetBkColor(PALETTEINDEX(COLOR_WHITE));
            pDC->SetTextColor(PALETTEINDEX(COLOR_BLACK));
           xPos = m_nFontWidth*(nIndent+2+m_strName.GetLength()-m_nStartCol);
            pDC->TextOut(xPos,yPos," = " + m_strValue);
            // Update the bottom pane to show Address & Type.
            UpdateBottomPane();
        }
        else {
            // Show the whole line.
            pDC->SetBkColor(PALETTEINDEX(COLOR_WHITE));
            pDC->SetTextColor(PALETTEINDEX(COLOR_BLACK));
            pDC->TextOut(xPos , yPos , m_strResult);
        }
    
      nLine++;
        // Point to the son node.
        if ( m_pVarNode->son ) {
            m_pVarNode = m_pVarNode->son;
            continue;
        }
            
        // Point to the next node.
        if ( m_pVarNode->next ) {
            m_pVarNode = m_pVarNode->next;
        }
        else {
            while ( m_pVarNode = m_pVarNode->father ) {
                if ( m_pVarNode->next ) {
                    m_pVarNode = m_pVarNode->next;
                    break;
                }
            }
        }
    }
    TurnOnCaret();
}

// Output all the variable to the window.
void CStackLocalView::OutputOneVariable(CDC* pDC , int nLine)
{
    // Assertion of the input parameters.
    ASSERT( m_nMaxRow >= 0 );
    ASSERT( m_nMaxCol >= 0 );
   int xPos , yPos;
   CFont *pOldFont = (CFont *)pDC->SelectStockObject(ANSI_FIXED_FONT);
    if ( 0 != ::Stk_VarLineToNode(nLine, m_pVarNode) ) {
      return ;
    } 
    // Detect the variable format.
    DetectVarFormat();
    // Get name from Variable server.
    m_strName = m_pVarNode->name;
    // Get value from Variable server.
    if ( invalidSign == m_nBmpStatus ) {
        m_strValue = "Invalid Variable.";
    }
    else {
        m_strValue = m_pVarNode->value;
    }
    // Add to the whole result string.
    m_strResult = m_strName + " = " + m_strValue;
    // Adjust to the begin column.
    int nIndent = m_pVarNode->num * 2;
    // Show the bitmap.
    xPos = m_nFontWidth*(nIndent-m_nStartCol);
    yPos = m_nFontHeight*(nLine-m_nStartRow);
    DisplayBitmap(pDC, xPos , yPos);
    
    // Show the result.
    xPos = m_nFontWidth*(nIndent+2-m_nStartCol);
//    if ( m_nStartRow+nLine == m_nCurRow ) {
    if ( nLine == m_nCurRow ) {
        // Mark the variable token.
        pDC->SetBkColor(PALETTEINDEX(COLOR_YELLOW));
        pDC->SetTextColor(PALETTEINDEX(COLOR_BLACK));
        pDC->TextOut(xPos , yPos , m_strName);
        pDC->SetBkColor(PALETTEINDEX(COLOR_WHITE));
        pDC->SetTextColor(PALETTEINDEX(COLOR_BLACK));
        xPos = m_nFontWidth*(nIndent+2+m_strName.GetLength()-m_nStartCol);
        pDC->TextOut(xPos,yPos," = " + m_strValue);
        // Update the bottom pane to show Address & Type.
        UpdateBottomPane();
    }
    else {
        // Show the whole line.
        pDC->SetBkColor(PALETTEINDEX(COLOR_WHITE));
        pDC->SetTextColor(PALETTEINDEX(COLOR_BLACK));
        pDC->TextOut(xPos , yPos , m_strResult);
    }
   pDC->SelectObject(pOldFont);
    
}

// Update the bottom pane.
void CStackLocalView::UpdateBottomPane(void)
{
    // Assertion of the input parameters.

    // Set the empty to the bottom pane.
    if ( !m_pVarNode ) {
        (((CStackFrame* )((CSplitterWnd* )GetParent())->GetParent())->m_wndBottomPane).m_strAddress = "";
        (((CStackFrame* )((CSplitterWnd* )GetParent())->GetParent())->m_wndBottomPane).m_strType = "";
    }
    else {
        // Get the address & type information of the current node.
        ::Stk_AddrToStr(m_pVarNode->addr, 
            (((CStackFrame* )((CSplitterWnd* )GetParent())->GetParent())->m_wndBottomPane).m_strAddress);
        (((CStackFrame* )((CSplitterWnd* )GetParent())->GetParent())->m_wndBottomPane).m_strType = 
            m_pVarNode->typeName;
    }

    // Set the bottom pane invalidate rectangle.
    (((CStackFrame* )((CSplitterWnd* )GetParent())->GetParent())->m_wndBottomPane).Invalidate();
    (((CStackFrame* )((CSplitterWnd* )GetParent())->GetParent())->m_wndBottomPane).UpdateWindow();
}

// Turn on the caret if it was off.
void CStackLocalView::TurnOnCaret(void)
{
    // Assertion of the input parameters.
    if ( !(::bStkLocalGetFocus) ) {
      return;
    }
    
    // Adjust the caret position.
    m_yCaret = (m_nCurRow-m_nStartRow)*m_nFontHeight;
    m_xCaret = (m_nCurCol-m_nStartCol)*m_nFontWidth;

    if ( !m_nFocus ) return;

    SetCaretPos(CPoint(m_xCaret, m_yCaret));

//    if ( !(::bStkLocalGetFocus) ) {
//     CreateSolidCaret(caretWidth, m_nFontHeight);
//    }

    // Show the caret.
    if ( FALSE == m_bCaretOn ) {
        ShowCaret();
        m_bCaretOn = TRUE;
    }
}

// Turn off the caret if it was on.
void CStackLocalView::TurnOffCaret(void)
{
    // Assertion of the input parameters.
    if ( !m_nFocus ) return;

    // Hide the caret.
    if ( TRUE == m_bCaretOn ) {
        HideCaret();
        m_bCaretOn = FALSE;
    }
}


/////////////////////////////////////////////////////////////////////////////
// CStackLocalView drawing and updating.

BOOL CStackLocalView::PreCreateWindow(CREATESTRUCT& cs)
{
    // Register a custom WndClass and create a window.
    // This must be done because CVarFrame has a custom icon.
    const char* pszVariableViewClass =
        AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS,
            LoadCursor(NULL, IDC_IBEAM),
            (HBRUSH) (COLOR_WINDOW+1),
            LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_VARIABLE)));
    
    // Set to CREATESTRUCT.
    ASSERT( NULL == cs.lpszClass );
    cs.lpszClass = pszVariableViewClass;

    return TRUE;
}

void CStackLocalView::OnInitialUpdate()
{
    CView::OnInitialUpdate();

    // TODO: calculate the total size of this view.
}


void CStackLocalView::OnPrepareDC(CDC* pDC, CPrintInfo* /* pInfo */)
{
    CView::OnPrepareDC(pDC);

    // TODO: add extra preparing DC code here
    
    // Set the font.
    pDC->SelectStockObject(ANSI_FIXED_FONT);
}

void CStackLocalView::OnDraw(CDC* pDC)
{
    CDocument* pDoc = GetDocument();

    // TODO: add draw code here

    if ( m_bAdjustScrollRange || m_bAdjustScrollBox ) {
      ResizeScroll();
    }
    
    // Set the variable node.
    m_pVarNode = ::Stk_GetListHeader();
    
    // Output the contents.
    if ( m_pVarNode ) {
        AfxGetApp()->DoWaitCursor(1);
        OutputAllVariable(pDC);
        AfxGetApp()->DoWaitCursor(-1);
    }
    else {
        UpdateBottomPane();
    }
}

CStackDoc* CStackLocalView::GetDocument() // non-debug version is inline
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CStackDoc)));
    return (CStackDoc*)m_pDocument;
}


/////////////////////////////////////////////////////////////////////////////
// Message map

BEGIN_MESSAGE_MAP(CStackLocalView, CView)
    //{{AFX_MSG_MAP(CStackLocalView)
    ON_WM_CREATE()
    ON_WM_SIZE()
    ON_WM_SETFOCUS()
    ON_WM_KILLFOCUS()
    ON_WM_KEYDOWN()
    ON_WM_LBUTTONDOWN()
    ON_WM_LBUTTONDBLCLK()
    ON_WM_SETCURSOR()
    ON_WM_RBUTTONDOWN()
    ON_WM_HSCROLL()
    ON_WM_VSCROLL()
    ON_WM_PAINT()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()


/////////////////////////////////////////////////////////////////////////////
// CStackLocalView message handlers

int CStackLocalView::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    // Set standard font.
    CDC* pDC = GetDC();
    pDC->SelectStockObject(ANSI_FIXED_FONT);

    // Get the font size.
    TEXTMETRIC tm;
    pDC->GetTextMetrics(&tm);
    m_nFontWidth = tm.tmAveCharWidth;
    m_nFontHeight = tm.tmHeight + tm.tmExternalLeading;

    ReleaseDC(pDC);

    // Update the caret position.
    TurnOnCaret();
    
    // Set the limit row in this view: 2048 rows.
    m_nLimitLine = INT_MAX / m_nFontHeight - limitRow;
    m_nLimitColumn = INT_MAX / m_nFontWidth - limitColumn;

    return 0;
}

void CStackLocalView::OnSize(UINT nType, int cx, int cy)
{
    CView::OnSize(nType, cx, cy);
    
    // TODO: Add your message handler code here
    
    // Set the minimum size.
    int cyCur, cyMin;
    ((CSplitterWnd* )GetParent())->GetRowInfo(1, cyCur, cyMin);
    if ( cyCur > 0 && cyCur < 20 ) {
        ((CSplitterWnd* )GetParent())->SetRowInfo(1, cyCur, 20);
        ((CSplitterWnd* )GetParent())->RecalcLayout();
    }
    
    // Get the client area size.
    GetClientRect(m_rectClient);
    
    // Adjust the local pane size.
    if ( 0 == (m_rectClient.Height() % m_nFontHeight) ) {
        m_rectClient.bottom += 1;
    }
    if ( 0 == (m_rectClient.Width() % m_nFontWidth) ) {
        m_rectClient.right += 1;
    }
    m_nRowSize = (m_rectClient.Height() / m_nFontHeight);
    m_nColSize = (m_rectClient.Width() / m_nFontWidth);
    
    // Adjust the scroll range.
    ResizeScroll();
    
    // Hide the caret.
//    TurnOffCaret();
}

void CStackLocalView::OnSetFocus(CWnd* pOldWnd)
{
    CView::OnSetFocus(pOldWnd);
    
    // TODO: Add your message handler code here
    
    // Set the flag to indicate the Local pane has got the input focus.
    ::bStkLocalGetFocus = TRUE;
    
    // Create the caret shape.
    CreateSolidCaret(caretWidth, m_nFontHeight);
    m_nFocus = 1;

    // Show the caret.
    TurnOnCaret();
}

void CStackLocalView::OnKillFocus(CWnd* pNewWnd)
{
    CView::OnKillFocus(pNewWnd);
    
    // TODO: Add your message handler code here
    
    // Set the flag to indicate the Local pane has lost the input focus.
    ::bStkLocalGetFocus = FALSE;
    
    // Hide the caret.
    TurnOffCaret();
    
    // Destroy the caret.
    DestroyCaret();
    m_nFocus = 0;
}

void CStackLocalView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    // TODO: Add your message handler code here and/or call default
    
    // Assertion of the input parameters.
    ASSERT( m_nMaxRow >= 0 );
    ASSERT( m_nMaxCol >= 0 );
    ASSERT( m_nCurRow >= 0 );
    ASSERT( m_nCurCol >= 0 );
    
    // Process the keyboard event.
    switch ( nChar ) {
        case VK_UP:
         if ( m_nCurRow ) {
            m_nCurRow--;
            if ( (m_nCurRow >= m_nStartRow)&&
               (m_nCurRow <= m_nStartRow+m_nRowSize-1) ) {
               TurnOffCaret();
                CDC *pDC = GetDC();
               OutputOneVariable(pDC, m_nCurRow+1);
               OutputOneVariable(pDC, m_nCurRow);
//               UpdateBottomPane();
               ReleaseDC(pDC);
                TurnOnCaret();
            }
            else {    
               m_nStartRow = m_nCurRow;
               m_bAdjustScrollBox = TRUE;
               ResizeScroll();
            }              
         }       
         else if ( m_nCurRow < m_nStartRow ) {
            m_nStartRow = m_nCurRow;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case VK_DOWN:
         if ( m_nCurRow < m_nMaxRow-1 ) {
            m_nCurRow++;
            if ( (m_nStartRow+m_nRowSize-1>m_nCurRow) &&
               (m_nStartRow<=m_nCurRow) ) {
               TurnOffCaret();
                CDC *pDC = GetDC();
               OutputOneVariable(pDC, m_nCurRow-1);
               OutputOneVariable(pDC, m_nCurRow);
               ReleaseDC(pDC);
                TurnOnCaret();
            }
            else {    
               m_nStartRow = max( m_nCurRow-m_nRowSize+1, 0 );
               m_bAdjustScrollBox = TRUE;
               ResizeScroll();
            }
         }
         else if ( m_nStartRow > m_nCurRow ) {
            m_nStartRow = max( m_nCurRow-m_nRowSize+1, 0 );
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case VK_PRIOR:
         if ( m_nCurRow ) {
            int nOldRow = m_nCurRow;
            m_nCurRow = max( m_nCurRow-m_nRowSize+1 , 0 );
            if ( (m_nCurRow >= m_nStartRow)&&
               (m_nCurRow <= m_nStartRow+m_nRowSize-1) ) {
               TurnOffCaret();
                CDC *pDC = GetDC();
               OutputOneVariable(pDC, nOldRow);
               OutputOneVariable(pDC, m_nCurRow);
               ReleaseDC(pDC);
                TurnOnCaret();
            }
            else {    
               m_nStartRow = m_nCurRow;
               m_bAdjustScrollBox = TRUE;
               ResizeScroll();
            }              
         }
         else if ( m_nCurRow < m_nStartRow ) {
            m_nStartRow = m_nCurRow;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case VK_NEXT:
         if ( m_nCurRow < m_nMaxRow-1 ) {
            int nOldRow = m_nCurRow;
            m_nCurRow = min( m_nCurRow+m_nRowSize-1 , m_nMaxRow-1 );
            if ( (m_nStartRow+m_nRowSize-1>m_nCurRow) &&
               (m_nStartRow<=m_nCurRow) ) {
               TurnOffCaret();
                CDC *pDC = GetDC();
               OutputOneVariable(pDC, nOldRow);
               OutputOneVariable(pDC, m_nCurRow);
               ReleaseDC(pDC);
                TurnOnCaret();
            }
            else {    
               m_nStartRow = max( m_nCurRow-m_nRowSize+1, 0 );
               m_bAdjustScrollBox = TRUE;
               ResizeScroll();
            }
         }
         else if ( m_nStartRow < m_nCurRow ) {
            m_nStartRow = max( m_nCurRow-m_nRowSize+1, 0 );
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case VK_LEFT:
            if ( (GetKeyState(VK_SHIFT) & 0x8000) ) {
                // Left drag.
            }
            else {
            if ( m_nCurCol ) {
               m_nCurCol--;
               if ( (m_nCurCol >= m_nStartCol)&&
                  (m_nCurCol <= m_nStartCol+m_nColSize-1) ) {
                   TurnOnCaret();
               }
               else {    
                  m_nStartCol = m_nCurCol;
                  m_bAdjustScrollBox = TRUE;
                  ResizeScroll();
               }              
            }       
            else if ( m_nCurCol < m_nStartCol ) {
               m_nStartCol = m_nCurCol;
               m_bAdjustScrollBox = TRUE;
               ResizeScroll();
            }
            }
            break;         
        case VK_RIGHT:
            if ( (GetKeyState(VK_SHIFT) & 0x8000) ) {
                // Right drag.
            }
            else {
            if ( m_nCurCol < m_nMaxCol ) {
               m_nCurCol++;
               if ( (m_nStartCol+m_nColSize-1>m_nCurCol) &&
                  (m_nStartCol<=m_nCurCol) ) {
                   TurnOnCaret();
               }
               else {    
                  m_nStartCol = max( m_nCurCol-m_nColSize+1, 0 );
                  m_bAdjustScrollBox = TRUE;
                  ResizeScroll();
               }
            }
            else if ( m_nStartCol > m_nCurCol ) {
               m_nStartCol = max( m_nCurCol-m_nColSize+1, 0 );
               m_bAdjustScrollBox = TRUE;
               ResizeScroll();
            }
               break;
            }
            break;
        case VK_HOME:
         if ( m_nStartCol ) {
            m_nStartCol = 0;
            m_nCurCol = 0;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
         else {
            m_nCurCol = 0;
            TurnOnCaret();
         }                 
            break;
        case VK_END:
         m_nCurCol = m_nMaxCol;
         if ( (m_nStartCol+m_nColSize-1>m_nCurCol) &&
            (m_nStartCol<=m_nCurCol) ) {
            TurnOnCaret();
         }
         else {
            m_nStartCol = max(m_nMaxCol-m_nColSize,0);
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case VK_RETURN:
            // LB Double click.
            SendMessage(WM_LBUTTONDBLCLK);
            break;
        case VK_DELETE:
            // Delete a variable.
            DeleteOneNode();
            break;
        default:
            break;
    }
    
    // Update the caret position.
//    TurnOnCaret();
    
    CView::OnKeyDown(nChar, nRepCnt, nFlags);
}

void CStackLocalView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    
    // If there is any variables?
    if ( m_nMaxRow > 0 ) {
        // Get the old caret position.
        int nOldRow = m_nCurRow;
    
        // Get the caret position.
        m_xCaret = point.x / m_nFontWidth;
        m_yCaret = point.y / m_nFontHeight;
    
      if ( m_nStartRow+m_yCaret<m_nMaxRow ) {
         m_nCurRow = m_nStartRow+m_yCaret;
         m_nCurCol = m_xCaret; 
         TurnOffCaret();
           if ( m_nCurRow != nOldRow ) {     
            CDC *pDC = GetDC();
            OutputOneVariable(pDC, nOldRow);
            OutputOneVariable(pDC, m_nCurRow);
            ReleaseDC( pDC );
           }
           TurnOnCaret();
      }
    
        // Update the caret position.
        SetFocus();
    }

    CView::OnLButtonDown(nFlags, point);
}

void CStackLocalView::OnLButtonDblClk(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default
    
    // Expand or collapse the bitmap.
    ASSERT( m_nCurRow >= 0 );
    if ( 0 == ::Stk_VarLineToNode(m_nCurRow, m_pVarNode) ) {
        if ( m_pVarNode ) {
            DetectVarFormat();
            if ( openSign == m_nBmpStatus ) {
                if ( 0 == ::Stk_CompressNode(m_pVarNode) ) {
                    // Set the invalidate region.
               m_bAdjustScrollRange = TRUE;
               ResizeScroll();
                }
            }
            else if ( closeSign == m_nBmpStatus ) {
                if ( 0 == ::Stk_ExtendedNode(m_pVarNode) ) {
                    // Get the maximum line.
                    ::Stk_MaxLine(m_nMaxRow);
                    // Set the invalidate region.
               m_bAdjustScrollRange = TRUE;
               ResizeScroll();
                }
            }
        }
    }
    
    CView::OnLButtonDblClk(nFlags, point);
}

void CStackLocalView::OnRButtonDown(UINT nFlags, CPoint point)
{
    // TODO: Add your message handler code here and/or call default

    // Assume Local window is active.
    BOOL bActiveThis = FALSE;
    if ( !::bStkLocalGetFocus ) {
        ::bStkLocalGetFocus = TRUE;
        bActiveThis = TRUE;
    }

    // Create the local menu.
    CMenu* pLocalMenu = new CMenu;
    ASSERT( NULL != pLocalMenu );
    if ( !pLocalMenu ) {
        m_nErrorID = insufficientMemory;
        DisplayErrorMessage();
        return;
    }
    pLocalMenu->CreatePopupMenu();

    // Define the local menu.
    pLocalMenu->AppendMenu(MF_STRING, ID_EDIT_SELECTLOCALVARIABLES, "Select Local &Variables...");
    pLocalMenu->AppendMenu(MF_STRING, ID_EDIT_MODIFYVALUES, "&Modify Value...");
    pLocalMenu->AppendMenu(MF_SEPARATOR);

    pLocalMenu->AppendMenu(MF_STRING, ID_EDIT_SEARCH, "&Search...");
    pLocalMenu->AppendMenu(MF_STRING, ID_EDIT_SEARCHNEXT, "Search &Next");
    
    
    // Adjust the cursor location.
    if ( 0 == point.x && 0 == point.y ) {
        CRect rect;
        GetClientRect(&rect);
        point = CPoint(rect.left+rect.Width()/2, rect.top+rect.Height()/2);
    }

    // Display the local menu.
    ClientToScreen(&point);

    // Active the local menu.
    pLocalMenu->TrackPopupMenu(TPM_LEFTALIGN, point.x, point.y, 
                               GetParent()->GetParent());

    // Release the allocated buffer.
//    delete pPopupMenu;
    delete pLocalMenu;

    // Retrieve the Local window status.
    if ( bActiveThis ) {
        ::bStkLocalGetFocus = FALSE;
    }
    
    CView::OnRButtonDown(nFlags, point);
}

BOOL CStackLocalView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
    // TODO: Add your message handler code here and/or call default

    // Disable parent window from pre-handling the WM_RBUTTONDOWN message.
    // MAINFRM latch WM_RBUTTONDOWN to implement local menu.
    if ( message == WM_RBUTTONDOWN ) {
        return FALSE;
    }

    return CView::OnSetCursor(pWnd, nHitTest, message);
}

void CStackLocalView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    // TODO: Add your message handler code here and/or call default
    
    // Disable the thumb direct operation.
    switch ( nSBCode ) {
        case SB_LINELEFT :
         if ( m_nStartCol ) {
            m_nStartCol--;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_LINERIGHT: 
         if ( m_nStartCol < m_nMaxCol ) {
            m_nStartCol++;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_PAGELEFT:
         if ( m_nStartCol ) {
            m_nStartCol = max( m_nStartCol-m_nColSize+1,0);
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_PAGERIGHT: 
         if ( m_nStartCol < m_nMaxCol ) {
            m_nStartCol = min( m_nStartCol+m_nColSize-1,m_nMaxCol);
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_TOP:
         if ( m_nStartCol ) {
            m_nStartCol = 0;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_BOTTOM:
         if ( m_nStartCol < m_nMaxCol ) {
            m_nStartCol = m_nMaxCol;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_THUMBPOSITION:       
        case SB_THUMBTRACK:       
         if ( m_nStartCol != (int)nPos ) {
            m_nStartCol = max(0 , (int)nPos);
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        default:
            return;
    }

    CView::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CStackLocalView::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    // TODO: Add your message handler code here and/or call default

    // Disable the thumb direct operation.
    switch ( nSBCode ) {
        case SB_LINEUP:
         if ( m_nStartRow ) {
            m_nStartRow--;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_LINEDOWN:
         if ( m_nStartRow < m_nMaxRow-1 ) {
            m_nStartRow++;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_PAGEUP:
         if ( m_nStartRow ) {
            m_nStartRow = max( m_nStartRow-m_nRowSize+1 , 0 );
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_PAGEDOWN:
         if ( m_nStartRow < m_nMaxRow-1 ) {
            m_nStartRow = min( m_nStartRow+m_nRowSize-1 , m_nMaxRow-1 );
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_TOP:    
         if ( m_nStartRow ) {
            m_nStartRow = 0;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_BOTTOM:    
         if ( m_nStartRow < m_nMaxRow-1 ) {
            m_nStartRow = m_nMaxRow-1;
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        case SB_THUMBPOSITION:       
        case SB_THUMBTRACK:       
         if ( m_nStartRow != (int)nPos ) {
            m_nStartRow = max(0 , (int)nPos);
            m_bAdjustScrollBox = TRUE;
            ResizeScroll();
         }
            break;
        default:
            return;
    }

    CView::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CStackLocalView::OnPaint()
{
    CPaintDC dc(this); // device context for painting
    
    // TODO: Add your message handler code here
    
    // Standard paint routine.
    OnPrepareDC(&dc);
    OnDraw(&dc);
    // Do not call CView::OnPaint() for painting messages
}
