Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Undocumented Visual C++

, 17 Sep 2000
Spelunking in the Badlands of MSDEV
/*****************************************************************************
    COPYRIGHT (C) 2000, Ken Bertelson <kenbertelson@hotmail.com>


*****************************************************************************/
#include "stdafx.h"
#include "TreeColumn.h"

#include "GridTreeCell.H"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CTreeColumn::CTreeColumn()
{
    // tree cell data initialization
    m_nFormat = DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS;
    m_crBkClr = CLR_DEFAULT;
    m_crFgClr = CLR_DEFAULT;
    m_lParam  = 0;
    m_plfFont = NULL;

    // tree properties initialization
    m_pGrid = NULL;
    m_iColumnWithTree = -1; // no column draws the tree
    m_bTreeUsesImages = FALSE;
    m_bShowTreeLines = TRUE;

    m_iFixedRowCount = -1;
    m_iRowCount = -1;
    m_bAllowDraw = FALSE;   // prevents crash during reset
    m_crTreeLineColour = ::GetSysColor(COLOR_3DSHADOW);
    m_nDefTreeIndent = 10;
    m_pEditWnd = NULL;
}

CTreeColumn::~CTreeColumn()
{
    delete m_plfFont;
}

/*****************************************************************************
Sets up a column to be drawn with + - tree controls w/ indenting.  Text
is automatically indented.  Rows expand and collapse as appropriate

*****************************************************************************/
BOOL CTreeColumn::TreeSetup(  CGridCtrl* apGridCtrl,  // tree acts on a column in this grid
                            int aiColumn,       // which column has tree
                            int aiTotalRows,    // total number of rows if tree totally expanded
                            int aiFixedRowCount,// Set fixed row count now, too
                            const unsigned char* apucTreeLevelAry,    // Tree Level data array --
                                                            //  must have aiTotalRows of entries
                            BOOL abShowTreeLines,   // T=show tree (not grid) lines; F=no tree lines
                            BOOL abUseImages,   // T=use 1st 3 images from already set image list
                                                //  to display folder graphics
                            CRuntimeClass* apRuntimeClass)   // can use your special
                                                //  CGridTreeCellBase-derived class
// returns:  success / fail
{
    ASSERT( apGridCtrl != NULL);
    ASSERT( aiColumn >= apGridCtrl->GetFixedColumnCount()
            && aiColumn < apGridCtrl->GetColumnCount());

    if( apRuntimeClass == NULL)  // default can hold data
       apRuntimeClass = RUNTIME_CLASS(CGridTreeCell);
    if (!apRuntimeClass->IsDerivedFrom(RUNTIME_CLASS(CGridTreeCellBase)))
    {
        ASSERT( FALSE);
        return FALSE;
    }

    m_pGrid = apGridCtrl;
    m_bAllowDraw = FALSE;   // prevents crash during reset
    BOOL bRC = TRUE;

    m_iFixedRowCount = aiFixedRowCount;
    m_iRowCount = aiTotalRows + aiFixedRowCount;
    m_iColumnWithTree = aiColumn;

    m_pGrid->SetFixedRowCount( m_iFixedRowCount);
    m_pGrid->SetRowCount( m_iRowCount);

    if( aiTotalRows > 0)
    {
        ASSERT( apucTreeLevelAry != NULL);

        // retain old cell properties:  store one time in CTreeColumn
        CGridTreeCell GridCellCopy;
        GridCellCopy.SetTreeColumn( this);
        CGridCellBase* pCurrCell = m_pGrid->GetCell( m_iFixedRowCount, m_iColumnWithTree);
        if (pCurrCell)
            GridCellCopy = *pCurrCell;

        // copy all data while replacing cells to tree cell type
        for( int i1=0; i1 < aiTotalRows; i1++)
        {
            int iCellRow = m_iFixedRowCount + i1;


            if( !m_pGrid->SetCellType(   iCellRow,
                                m_iColumnWithTree,
                                apRuntimeClass ) )
            {
                bRC = FALSE;
                break;
            }

            CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( iCellRow, m_iColumnWithTree);
            if( pGridTreeCell != NULL)
            {
                pGridTreeCell->SetTreeColumn( this);
                pGridTreeCell->SetLevelAndHide( *apucTreeLevelAry );
            }

            apucTreeLevelAry++;
        }

        SetTreeUsesImages( abUseImages);   // T=use images
        SetTreeLines( abShowTreeLines);    // T=show tree lines
    }

    m_bAllowDraw = TRUE;
    m_pGrid->Invalidate();
    return bRC;
}

void CTreeColumn::SetTreeUsesImages( BOOL abUseImages)   // T=use images
{
#ifdef _DEBUG
    ASSERT( m_iColumnWithTree >= 0);
    ASSERT( m_pGrid != NULL);

    if( abUseImages
        && m_pGrid->GetImageList() == NULL)
        ASSERT( FALSE);     // must call SetImageList() first
#endif

    m_bTreeUsesImages = abUseImages;

    if( abUseImages)
    {
        // make sure that items have an image associated with it for
        //  autosize column width calculations
        ASSERT( m_iRowCount > 0); // call SetRowCount() before this
        ASSERT( m_iColumnWithTree >= 0);
        for( int i1=m_pGrid->GetFixedColumnCount(); i1 < m_iRowCount; i1++)
        {
            m_pGrid->SetItemImage( i1, m_iColumnWithTree, 0);
        }
    }
}

void CTreeColumn::SetTreeLines( BOOL abShowTreeLines)   // T=show tree lines
{
    ASSERT( m_iColumnWithTree >= 0);
    m_bShowTreeLines = abShowTreeLines;
}

unsigned char CTreeColumn::GetTreeLevel(  int aiRow)  // row
// returns:  tree level, =0 if invalid input
{
    ASSERT( m_pGrid != NULL);
    ASSERT( aiRow >= 0
            && aiRow < m_iRowCount );
    ASSERT( m_iColumnWithTree >= 0);

    CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( aiRow, m_iColumnWithTree);
    if( pGridTreeCell != NULL)
        return pGridTreeCell->GetLevel();

    return 0;
}

BOOL CTreeColumn::IsTreeRowDisplayed(  int aiRow)  // row
// returns:  T=tree row is displayed
{
    ASSERT( m_pGrid != NULL);
    ASSERT( aiRow >= 0
            && aiRow < m_iRowCount );
    ASSERT( m_iColumnWithTree >= 0);

    CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( aiRow, m_iColumnWithTree);
    if( pGridTreeCell != NULL)
        return pGridTreeCell->IsViewable();

    return FALSE;
}

unsigned char CTreeColumn::GetLevelAndHide( int aiRow)
// returns:  tree level, =0 if invalid input
{
    ASSERT( m_pGrid != NULL);
    ASSERT( aiRow >= 0
            && aiRow < m_iRowCount );
    ASSERT( m_iColumnWithTree >= 0);

    CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( aiRow, m_iColumnWithTree);
    if( pGridTreeCell != NULL)
        return pGridTreeCell->GetLevelAndHide();

    return 0;
}

/*****************************************************************************
Examines saved array show / hide bit to determine if a row should display.
There is a one-for-one mapping of these bits to total rows

*****************************************************************************/
void CTreeColumn::TreeRefreshRows()
{
    ASSERT( m_pGrid != NULL);
    ASSERT( m_iColumnWithTree >= 0);

    // hide / show each row
    for( int i1=m_iFixedRowCount; i1 < m_iRowCount; i1++)
    {
        CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( i1, m_iColumnWithTree);
        if( pGridTreeCell == NULL)
            return;

        if( pGridTreeCell->IsViewable() )
        {
            if( m_pGrid->GetRowHeight( i1) < m_pGrid->GetDefCellHeight() )
                m_pGrid->SetRowHeight( i1, m_pGrid->GetDefCellHeight() );
        }
        else
        {
            if( m_pGrid->GetRowHeight( i1) > 0 )
                m_pGrid->SetRowHeight( i1, 0);
        }

    }

    if (::IsWindow( m_pGrid->m_hWnd) && m_bAllowDraw)
    {
        m_pGrid->Invalidate();
    }

}
/*****************************************************************************
Callable by class consumer to prepare one's own data array of tree info to
display something reasonable.   High order Bits of the tree display data are
modified.

*****************************************************************************/
void CTreeColumn::TreeDataPrepOutline(unsigned char aucLevel, // level to display >= 0x80 displays all
                                    int aiIndex,            // Index to tree display data to modify
                                    int aiNbrElements)      // nbr of elements in tree display data
{
    ASSERT( aucLevel > 0);
    ASSERT( m_iColumnWithTree >= 0);
    ASSERT( aiIndex >= 0 );
    ASSERT( aiNbrElements > 0);
    ASSERT( m_pGrid != NULL);
    ASSERT( aiIndex + aiNbrElements <= m_iRowCount - m_iFixedRowCount );

    int i1;

    if( aucLevel >= 0x80)
    {
        for( i1=0; i1 < aiNbrElements; i1++)
        {
            int iCellRow = m_iFixedRowCount + aiIndex + i1;

            CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( iCellRow, m_iColumnWithTree);
            if( pGridTreeCell == NULL)
                return;

            pGridTreeCell->SetViewable( TRUE);
        }
    }
    else
    {
        for( i1=0; i1 < aiNbrElements; i1++)
        {
            int iCellRow = m_iFixedRowCount + aiIndex + i1;

            CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( iCellRow, m_iColumnWithTree);
            if( pGridTreeCell == NULL)
                return;

            if( pGridTreeCell->GetLevel() <= aucLevel)
                pGridTreeCell->SetViewable( TRUE);
            else
                pGridTreeCell->SetViewable( FALSE);
        }
    }
}

/*****************************************************************************
Like MS-Word's outline feature.  Can show 1st level of outline all the
way up to 126 levels or all

*****************************************************************************/
void CTreeColumn::TreeDisplayOutline( unsigned char aucLevel )    // level to display >= 0x80 displays all
{
    ASSERT( aucLevel > 0);
    ASSERT( m_pGrid != NULL);
    ASSERT( m_iColumnWithTree >= 0);
    if( m_iColumnWithTree < 0)
        return;

    TreeDataPrepOutline(aucLevel,                   // level to display >= 0x80 displays all
                        0,                          // Index to tree display data to modify
                        m_iRowCount - m_iFixedRowCount);    // nbr of elements in tree display data

    TreeRefreshRows();

}

/*****************************************************************************
Expands a branch 1 and only 1 level -- like when a user clicks on "+" box
in windows explorer tree.  Note that it doesn't check whether adjacent nodes
are properply represented, it just does the expand

*****************************************************************************/
void CTreeColumn::TreeDataExpandOneLevel( int aiGridRow)
{
    ASSERT( m_iColumnWithTree >= 0);
    ASSERT( m_pGrid != NULL);

    ASSERT( aiGridRow >= m_iFixedRowCount
            && aiGridRow < m_iRowCount);

    // display desired node and save its level and make visible
    CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( aiGridRow, m_iColumnWithTree);
    if( pGridTreeCell == NULL)
        return;

    unsigned char ucLevelCurrent = pGridTreeCell->GetLevel();
    if( ucLevelCurrent <= 0)
        return; // don't expand or collapse items not part of the tree
    pGridTreeCell->SetViewable( TRUE);

    unsigned char ucLevel;
    int i1;
    for( i1 = aiGridRow + 1; i1 < m_iRowCount; i1++)
    {
        CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( i1, m_iColumnWithTree);
        if( pGridTreeCell == NULL)
            return;

        ucLevel = pGridTreeCell->GetLevel();
        if( ucLevel <= ucLevelCurrent)
            break;

        if( ucLevel == ucLevelCurrent + 1)
            pGridTreeCell->SetViewable( TRUE);
        else
            pGridTreeCell->SetViewable( FALSE);

    }

}

/*****************************************************************************
Collapses all levels of a branch -- not just 1 level.   Using this in
conjunction with TreeDataExpandOneLevel will get a tree grid that performs like
VC++ watch window.   Windows explorer tree differs:  it "remembers" last
expansion.

Note that it doesn't check whether adjacent nodes are properply represented, it
just does the collapse.

*****************************************************************************/
void CTreeColumn::TreeDataCollapseAllSubLevels( int aiGridRow)
{
    ASSERT( m_iColumnWithTree >= 0);
    ASSERT( m_pGrid != NULL);

    ASSERT( aiGridRow >= m_iFixedRowCount
            && aiGridRow < m_iRowCount);

    // display desired node and save its level and make visible
    CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( aiGridRow, m_iColumnWithTree);
    if( pGridTreeCell == NULL)
        return;

    unsigned char ucLevelCurrent = pGridTreeCell->GetLevel();
    if( ucLevelCurrent <= 0)
        return; // don't expand or collapse items not part of the tree
    pGridTreeCell->SetViewable( TRUE);

    unsigned char ucLevel;
    int i1;
    for( i1 = aiGridRow + 1; i1 < m_iRowCount; i1++)
    {
        CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( i1, m_iColumnWithTree);
        if( pGridTreeCell == NULL)
            return;

        ucLevel = pGridTreeCell->GetLevel();
        if( ucLevel <= ucLevelCurrent)
            break;

        pGridTreeCell->SetViewable( FALSE);

    }
}

/*****************************************************************************
Toggles tree expansion and collapsing.  Intended to be connected to a user
event

*****************************************************************************/
void CTreeColumn::TreeExpandCollapseToggle( int aiGridRow) // Grid row of node to toggle
{
    ASSERT( m_pGrid != NULL);
    ASSERT( aiGridRow >= m_iFixedRowCount
            && aiGridRow < m_iRowCount);
    ASSERT( m_bAllowDraw);
    ASSERT( m_iColumnWithTree >= 0);
    if( m_iColumnWithTree < 0)
        return;

    // if last element, forget it
    if( aiGridRow + 1 >= m_iRowCount)
        return;

    // see if it needs to be expanded by looking at hide/show state of first neighbor.
    //  If first neighbor is hidden, should expand
    CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( aiGridRow + 1, m_iColumnWithTree);
    if( pGridTreeCell == NULL)
        return;

    if( pGridTreeCell->IsViewable() )
    {
        TreeDataCollapseAllSubLevels( aiGridRow);
    }
    else
    {
        TreeDataExpandOneLevel( aiGridRow);
    }
    TreeRefreshRows();

}

/*****************************************************************************
Should the cell identified by the row have a "+" or "-" graphic?

*****************************************************************************/
BOOL CTreeColumn::TreeCellHasPlusMinus(   int aiRow,          // row of Cell to check
                                        BOOL* apbIsPlus,    // returns:  T=Is a plus
                                        BOOL* apbIsMinus,   // returns:  T=Is a minus
                                        BOOL* apbIsLastLeaf)// returns:  T=Is Last Leaf
// returns:  T=cell has a plus or minus;  F=not
{
    ASSERT( m_pGrid != NULL);
    ASSERT( aiRow >= m_iFixedRowCount
            && aiRow < m_iRowCount);
    ASSERT( m_iColumnWithTree >= 0);
    ASSERT( apbIsPlus != NULL);
    ASSERT( apbIsMinus != NULL);
    ASSERT( apbIsLastLeaf != NULL);

    *apbIsPlus = FALSE;
    *apbIsMinus = FALSE;
    *apbIsLastLeaf = FALSE;

    int iStartPt = aiRow + 1;
    int i1;

    // get current level
    CGridTreeCell* pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( aiRow, m_iColumnWithTree);
    if( pGridTreeCell == NULL)
        return FALSE;
    unsigned char ucLevelCurrent = pGridTreeCell->GetLevel();
    if( ucLevelCurrent <= 0)
        return FALSE;     // no tree

    BOOL bIsNextShowing = FALSE;
    unsigned char ucLevelNext = 0;


    for( i1=iStartPt; i1 < m_iRowCount; i1++)
    {
        pGridTreeCell = (CGridTreeCell*)m_pGrid->GetCell( i1, m_iColumnWithTree);
        if( pGridTreeCell == NULL)
            return FALSE;

        bIsNextShowing = pGridTreeCell->IsViewable();
        ucLevelNext = pGridTreeCell->GetLevel();

        if( ucLevelCurrent >= ucLevelNext )
        {
            break;
        }

        if( !bIsNextShowing
            && ucLevelNext == ucLevelCurrent + 1)
        {
            *apbIsPlus = TRUE;
        }

    }

    // final attribute setting now that enough data has been examined
    if( i1 > iStartPt
        && !*apbIsPlus)
    {
        *apbIsMinus = TRUE;
    }

    if( !bIsNextShowing
        || ucLevelCurrent > ucLevelNext
        || i1 >= m_iRowCount )  // hit last element
        *apbIsLastLeaf = TRUE;

    return *apbIsMinus || *apbIsPlus;
}

BOOL CTreeColumn::GetTreeUsesNbrImages() const
{
    return CGridTreeCellBase::TREE_IMAGE_FENCE;
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Nick Hodapp
Web Developer
United States United States
No Biography provided

| Advertise | Privacy | Mobile
Web04 | 2.8.140926.1 | Last Updated 18 Sep 2000
Article Copyright 2000 by Nick Hodapp
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid