Hide

SftTree/DLL 7.5 - Tree Control

Display
Print

OwnerDraw Sample (C++)

This sample illustrates owner-draw cells and column headers.

The source code is located at C:\Program Files (x86)\Softelvdm\SftTree DLL 7.5\Samples\MFC\OwnerDraw\SamplVw.cpp or C:\Program Files\Softelvdm\SftTree DLL 7.5\Samples\MFC\OwnerDraw\SamplVw.cpp (on 32-bit Windows versions).

/****************************************************************************/
/* SftTree/DLL 7.5 - Tree Control for C/C++                                 */
/* Copyright (C) 1995, 2016  Softel vdm, Inc. All Rights Reserved.          */
/****************************************************************************/

#include "stdafx.h"
#include "OwnerDraw.h"

#include "sampldoc.h"
#include "samplvw.h"

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

/////////////////////////////////////////////////////////////////////////////
// CSampleView

IMPLEMENT_DYNCREATE(CSampleView, CView)

BEGIN_MESSAGE_MAP(CSampleView, CView)
    //{{AFX_MSG_MAP(CSampleView)
    ON_WM_CREATE()
    ON_WM_SIZE()
    //}}AFX_MSG_MAP
    ON_SFTTREEN_LBUTTONDBLCLK_TEXT(IDC_TREE, OnLButtonExpandCollapse)
    ON_SFTTREEN_LBUTTONDOWN_BUTTON(IDC_TREE, OnLButtonExpandCollapse)
    ON_SFTTREEN_LBUTTONDBLCLK_BUTTON(IDC_TREE, OnLButtonExpandCollapse)
    ON_SFTTREEN_EXPANDALL(IDC_TREE, OnExpandAll)
    ON_SFTTREEN_LBUTTONDBLCLK_COLUMNRES(IDC_TREE, OnLButtonDblClkColumnResize)
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CSampleView construction/destruction

CSampleView::CSampleView()
{
    // create a vertical font
    LOGFONT lf;

    CFont* pGuiFont = CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT));
    pGuiFont->GetLogFont(&lf);
    lf.lfEscapement = 900;
    lf.lfOutPrecision = OUT_TT_ONLY_PRECIS;
    lf.lfCharSet = DEFAULT_CHARSET;
    lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
    lf.lfQuality = DEFAULT_QUALITY;
    lf.lfPitchAndFamily = FF_DONTCARE;
    lstrcpy(lf.lfFaceName, _T("Arial"));
    m_hFontVertical.CreateFontIndirect(&lf);

    // load sample bitmap
    m_hBitmap.LoadBitmap(IDB_LOGO);
}

CSampleView::~CSampleView()
{
}

/////////////////////////////////////////////////////////////////////////////
// CSampleView drawing

void CSampleView::OnDraw(CDC* pDC)
{
    CSampleDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
}

/////////////////////////////////////////////////////////////////////////////
// CSampleView diagnostics

#ifdef _DEBUG
void CSampleView::AssertValid() const
{
    CView::AssertValid();
}

void CSampleView::Dump(CDumpContext& dc) const
{
    CView::Dump(dc);
}

CSampleDoc* CSampleView::GetDocument() // non-debug version is inline
{
    ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSampleDoc)));
    return (CSampleDoc*)m_pDocument;
}
#endif //_DEBUG

/////////////////////////////////////////////////////////////////////////////
// CSampleView message handlers

int CSampleView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CView::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    if (!m_Tree.CreateEx(
        WS_EX_CLIENTEDGE,
        SFTTREESTYLE_NOTIFY |            /* Notify parent window */
        SFTTREESTYLE_VARIABLE |          /* Variable height items */
        SFTTREESTYLE_LEFTBUTTONONLY |    /* Only respond to left mouse button */
        SFTTREESTYLE_SCROLL |            /* Honor WS_H/VSCROLL */
        WS_HSCROLL | WS_VSCROLL |        /* Vertical and horizontal scrollbars */
        WS_VISIBLE | WS_CHILD,           /* Visible, child window */
        CRect(0,0,0,0),                  /* Location */
        this,                            /* Parent window */
        IDC_TREE))                       /* Tree control ID */
            return -1;
    
    m_Tree.SetShowHeader(TRUE);          /* Show column headers */

    /* Register the item pictures.  These pictures are used    */
    /* for all items in the tree control. All three pictures   */
    /* must be the same size.                                  */
    {
        SFT_PICTURE aPic[3];
        Sft_InitPicture(&aPic[0]);
        Sft_InitPicture(&aPic[1]);
        Sft_InitPicture(&aPic[2]);
        m_aThreeItemBitmaps[0].LoadBitmap(IDB_EXPANDABLE);/* Expandable bitmap */
        Sft_SetPictureBitmap(&aPic[0], m_aThreeItemBitmaps[0]);/* Assign bitmap */
        m_aThreeItemBitmaps[1].LoadBitmap(IDB_EXPANDED);/* Expanded bitmap */
        Sft_SetPictureBitmap(&aPic[1], m_aThreeItemBitmaps[1]);/* Assign bitmap */
        m_aThreeItemBitmaps[2].LoadBitmap(IDB_LEAF);/* Leaf bitmap */
        Sft_SetPictureBitmap(&aPic[2], m_aThreeItemBitmaps[2]);/* Assign bitmap */
        m_Tree.SetPictures(aPic);        /* Use item pictures */
    }
    m_Tree.SetItemPictureAlign(TRUE);    /* Align item bitmaps */
    m_Tree.SetTreeLineStyle(SFTTREE_TREELINE_AUTOMATIC0);/* Dotted tree lines (incl. level 0) */
    m_Tree.SetShowButtons(TRUE);         /* Expand/collapse buttons (level 1..n) */
    m_Tree.SetShowButton0(TRUE);         /* Show expand/collapse buttons (level 0) */
    m_Tree.SetButtons(SFTTREE_BUTTON_AUTOMATIC3);/* Automatic button style 3 */
    m_Tree.SetShowGrid(TRUE);            /* Show grid */
    m_Tree.SetGridStyle(SFTTREE_GRID_BOTH_DOT);/* Dotted grid lines */
    m_Tree.SetShowTruncated(TRUE);       /* Show ... if truncated */
    m_Tree.SetSelectionStyle(SFTTREE_SELECTION_CELL1 | SFTTREE_SELECTION_OUTLINE);/* Select first cell only using outline */
    m_Tree.SetSelectionArea(SFTTREE_SELECTIONAREA_ALLCELLS);/* Selection changes by clicking on an item's cells */
    m_Tree.SetFlyby(TRUE);               /* Flyby highlighting */
    m_Tree.SetUpdateCaretExpandCollapse(FALSE);/* don't update caret location when expand/collapse button clicked */
    m_Tree.SetScrollTips(TRUE);          /* Show Scrolltips */
    m_Tree.SetInheritBgColor(TRUE);      /* Inherit background color of first cell */
    m_Tree.SetReorderColumns(TRUE);      /* Column reordering */
    m_Tree.SetOpenEnded(TRUE);           /* Last column width */
    m_Tree.SetShowHeaderButtons(TRUE);   /* Show column header as buttons */
    /* Define columns */
    {
        SFTTREE_COLUMN_EX aCol[3] = {
          { 0, 0,                        /* Reserved */
            221,                         /* Width (in pixels) */
            ES_LEFT | SFTTREE_TOOLTIP,   /* Cell alignment */
            ES_LEFT | SFTTREE_HEADER_UP, /* Title style */
            _T("Vertical Titles"),       /* Column header title */
            NULL, NULL,                  /* Reserved field and bitmap handle */
            SFTTREE_BMP_RIGHT,           /* Picture alignment */
            0, 0, 0,                     /* Reserved fields */
            SFTTREE_NOCOLOR,             /* Cell background color */
            SFTTREE_NOCOLOR,             /* Cell foreground color */
            SFTTREE_NOCOLOR,             /* Selected cell background color */
            SFTTREE_NOCOLOR,             /* Selected cell foreground color */
            0,                           /* Real column position (set by SetColumns call) */
            0,                           /* Display column number (display position) */
            0,                           /* Column flag */
            0,                           /* Minimum column width */
          },                             
          { 0, 0,                        /* Reserved */
            253,                         /* Width (in pixels) */
            ES_LEFT | SFTTREE_TOOLTIP,   /* Cell alignment */
            ES_LEFT | SFTTREE_HEADER_UP, /* Title style */
            _T("Second Column"),         /* Column header title */
            NULL, NULL,                  /* Reserved field and bitmap handle */
            SFTTREE_BMP_RIGHT,           /* Picture alignment */
            0, 0, 0,                     /* Reserved fields */
            SFTTREE_NOCOLOR,             /* Cell background color */
            SFTTREE_NOCOLOR,             /* Cell foreground color */
            SFTTREE_NOCOLOR,             /* Selected cell background color */
            SFTTREE_NOCOLOR,             /* Selected cell foreground color */
            0,                           /* Real column position (set by SetColumns call) */
            1,                           /* Display column number (display position) */
            0,                           /* Column flag */
            0,                           /* Minimum column width */
          },                             
          { 0, 0,                        /* Reserved */
            100,                         /* Width (in pixels) */
            ES_LEFT | SFTTREE_TOOLTIP,   /* Cell alignment */
            ES_LEFT | SFTTREE_HEADER_UP, /* Title style */
            _T("Third Column"),          /* Column header title */
            NULL, NULL,                  /* Reserved field and bitmap handle */
            SFTTREE_BMP_RIGHT,           /* Picture alignment */
            0, 0, 0,                     /* Reserved fields */
            SFTTREE_NOCOLOR,             /* Cell background color */
            SFTTREE_NOCOLOR,             /* Cell foreground color */
            SFTTREE_NOCOLOR,             /* Selected cell background color */
            SFTTREE_NOCOLOR,             /* Selected cell foreground color */
            0,                           /* Real column position (set by SetColumns call) */
            2,                           /* Display column number (display position) */
            0,                           /* Column flag */
            0,                           /* Minimum column width */
          }                              
        };
        m_Tree.SetColumns(3, aCol);      /* Set column attributes */
    }

    m_Tree.SetShowRowHeader(SFTTREE_ROWSTYLE_BUTTONCOUNT1);/* Row style */
    m_Tree.SetRowColHeaderText(_T("?"));       /* Row/column header text */
    m_Tree.SetRowColHeaderStyle(ES_LEFT | SFTTREE_HEADER_UP);/* Row/column header style */
    m_Tree.SetCharSearchMode(SFTTREE_CHARSEARCH_ALLCHARS, -1);/* Consider all characters typed */
    /* Use an ownerdraw callback routine */
    {
        SFTTREE_OWNERDRAWPARM Parm;      /* Parameter list */
        Parm.lpfnOwnerDrawProc = (LPFNSFTTREE_OWNERDRAWPROC) CSampleView::OwnerDrawCallback;/* User supplied drawing routine */
        Parm.OwnerDrawUserData = (SFTTREE_DWORD_PTR)this;/* User supplied data */
        m_Tree.SetOwnerDrawCallback(&Parm);
    }
    /* Change the default colors */
    {
        SFTTREE_COLORS Colors;
        m_Tree.GetCtlColors(&Colors);    /* Get current color settings */
        Colors.colorTreeLines = COLOR_3DDKSHADOW | 0x80000000L;/* Tree line color */
        Colors.colorSelBgNoFocus = COLOR_BTNFACE | 0x80000000L;/* Selection background color (no input focus) */
        Colors.colorSelFgNoFocus = COLOR_BTNTEXT | 0x80000000L;/* Selection foreground color (no input focus) */
        m_Tree.SetCtlColors(&Colors);    /* Set new colors */
    }

    /*------------------------------------------------------------------------------*/
    /* Add a few items.                                                             */
    /*------------------------------------------------------------------------------*/
    
    for (int i = 0 ; i < 50 ; ++i) {
        int index;

        index = m_Tree.AddString(_T("An item"));/* Add an item */
        m_Tree.SetText(index, 1, _T("2nd Column"));/* Set text in next column */
        m_Tree.SetText(index, 2, _T("3nd Column"));/* Set text in next column */
        index = m_Tree.AddString(_T("Another item"));/* Add another item */
        m_Tree.SetItemLevel(index, 1);/* change level */
        m_Tree.SetText(index, 1, _T("2nd Column"));/* Set text in next column */
        m_Tree.SetText(index, 2, _T("3nd Column"));/* Set text in next column */
        /* Set an item's row header text using: */
        m_Tree.SetRowText(index, _T("?"));/* Row header text */

        index = m_Tree.AddString(_T("A third item"));/* Add another item */
        m_Tree.SetItemLevel(index, 2);/* change level */
        m_Tree.SetText(index, 1, _T("2nd Column"));/* Set text in next column */
        m_Tree.SetText(index, 2, _T("3nd Column"));/* Set text in next column */
        index = m_Tree.AddString(_T("A fourth item"));/* Add another item */
        m_Tree.SetItemLevel(index, 1);/* change level */
        m_Tree.SetText(index, 1, _T("2nd Column"));/* Set text in next column */
        m_Tree.SetText(index, 2, _T("3nd Column"));/* Set text in next column */

    }

    /*------------------------------------------------------------------------------*/
    /* Once ALL TREE CONTROL ITEMS HAVE BEEN ADDED, you can set additional tree     */
    /* control attributes.                                                          */
    /*------------------------------------------------------------------------------*/
    
    /* Make all column widths optimal, so text and pictures are */
    /* not clipped horizontally.                               */
    m_Tree.MakeColumnOptimal(-1, 0, FALSE);/* Make column widths optimal */
    /* Make row header width optimal, so text and pictures are  */
    /* not clipped horizontally.                               */
    m_Tree.MakeRowHeaderOptimal(0, FALSE);/* Make row header width optimal */
    m_Tree.RecalcHorizontalExtent(0, FALSE);/* Update horizontal scroll bar */

    m_Tree.SetCurSel(4); // select the 4th item
    m_Tree.SetCaretIndex(4); // and make it current
    m_Tree.SetTopIndex(0); // show the first item

    return 0;
}

void CSampleView::OnSize(UINT nType, int cx, int cy) 
{
    CView::OnSize(nType, cx, cy);
    m_Tree.MoveWindow(0, 0, cx, cy);
}

/* Respond to events as the user clicks on different tree  */
/* components.  The events handled here can be changed to  */
/* suit your application.                                  */

void CSampleView::OnLButtonExpandCollapse()
{
    /* get index of item to expand/collapse */
    int index = m_Tree.GetExpandCollapseIndex();
    /* get current expand/collapsed status */
    BOOL fExpanded = m_Tree.GetItemExpand(index);
    /* if control key is used we'll expand all dependents */
    BOOL fDepth = (::GetKeyState(VK_CONTROL)&0x8000);

    if (fExpanded)
        m_Tree.Collapse(index, TRUE);
    else
        m_Tree.Expand(index, TRUE, fDepth);
}

/* Respond to numeric keypad multiply key.                 */

void CSampleView::OnExpandAll()
{
    /* get index of item to expand/collapse */
    int index = m_Tree.GetExpandCollapseIndex();

   m_Tree.Expand(index, TRUE, TRUE);
}

/* Respond to events as the user double-clicks on the      */
/* column resizing area.  The events handled here can be   */
/* changed to suit your application.                       */

void CSampleView::OnLButtonDblClkColumnResize()
{
    /* Resize column optimally */
    int realCol = m_Tree.GetResizeColumn();
    if (realCol >= 0) {
        m_Tree.MakeColumnOptimal(realCol);/* Make column width optimal */
        m_Tree.RecalcHorizontalExtent(0, FALSE);/* Update horizontal scroll bar */
    }
}

/**********************************************************************/
/*                         Ownerdraw Callback                         */
/**********************************************************************/

void CSampleView::OwnerDraw_Column_Header(LPSFTTREE_OWNERDRAW lpInfo)
{
    // This function paints the column header using a vertical font
    // it even takes themes into consideration, so it's a bit more complicated
    // than the average application.

    // get general information about tree control and columns
    LPSFTTREE_COLUMN_EX lpCol;
    int nCols = m_Tree.GetColumns(&lpCol);/* Get column attributes */ 

    // Set up CDC
    CDC* pDC = CDC::FromHandle(lpInfo->hDC);

    // calculate the text size
    RECT rect, textRect;
    int len = lstrlen(lpCol[lpInfo->col].lpszTitle); // column title
    COLORREF clrFg = pDC->SetTextColor(GetSysColor(COLOR_BTNTEXT));
    int mode = pDC->SetBkMode(TRANSPARENT);

    CFont* pOldFont = (CFont*) pDC->SelectObject(&m_hFontVertical);

    SetRectEmpty(&rect);
    pDC->DrawText(lpCol[lpInfo->col].lpszTitle, len, &rect, DT_CALCRECT|DT_LEFT);
    SetRect(&textRect, rect.top, rect.left, rect.bottom, rect.right);

    if (lpInfo->style == SFTTREE_OD_CALC) {
        // we need to return just the size of the header we want to paint
        rect = textRect;
        if (lpInfo->fThemed) {
            // Windows themes are used
            RECT extentRect;
            GetThemeBackgroundExtent(lpInfo->hThemeHeader, lpInfo->hDC, HP_HEADERITEM, HIS_NORMAL, &rect, &extentRect);
            rect = extentRect;
            InflateRect(&rect, 3, 6); // leave a slight gap
        } else {
            // No windows themes
            InflateRect(&rect, 4, 4); // leave a slight gap
        }
        lpInfo->DrawRect.bottom = rect.bottom-rect.top;
        lpInfo->DrawRect.right = rect.right-rect.left;
    } else {
        // we need to paint the header
        int xOffs, yOffs;
        if (lpInfo->fThemed) {
            // Windows themes are used
            int iPartId = HP_HEADERITEM;
            int iStateId = HIS_NORMAL;
            if (lpInfo->fSelected)
                iStateId = HIS_PRESSED;
            if (lpInfo->fFlyby)
                iStateId = HIS_HOT;
            DrawThemeBackground(lpInfo->hThemeHeader, lpInfo->hDC, iPartId, iStateId, &lpInfo->DrawRect, &lpInfo->DrawRect);
            GetThemeBackgroundContentRect(lpInfo->hThemeHeader, lpInfo->hDC, iPartId, iStateId, &lpInfo->DrawRect, &rect);
        } else {
            // No windows themes
            pDC->DrawFrameControl(&lpInfo->DrawRect, DFC_BUTTON, DFCS_BUTTONPUSH | (lpInfo->fSelected ? DFCS_PUSHED : 0));
            rect = lpInfo->DrawRect;
        }
        xOffs = max(0, ((lpInfo->DrawRect.right-lpInfo->DrawRect.left) - (textRect.right-textRect.left))/2);
        yOffs = max(0, ((lpInfo->DrawRect.bottom-lpInfo->DrawRect.top) - (textRect.bottom-textRect.top))/2);
        if (lpInfo->fSelected) {
            xOffs++;
            yOffs--;
        }
        pDC->ExtTextOut(lpInfo->DrawRect.left+xOffs, lpInfo->DrawRect.bottom-yOffs, 0, &lpInfo->DrawRect, lpCol[lpInfo->col].lpszTitle, len, NULL);
    }
    pDC->SelectObject(pOldFont);

    pDC->SetBkMode(mode);
    pDC->SetTextColor(clrFg);
}

void CSampleView::OwnerDraw_Cell(LPSFTTREE_OWNERDRAW lpInfo)
{
    // calculate the cell size
    RECT rect;
    BITMAP bm;

    // Set up CDC
    CDC* pDC = CDC::FromHandle(lpInfo->hDC);

    CDC DCTemp;
    DCTemp.CreateCompatibleDC(pDC);
    CBitmap* pOldBitmap = (CBitmap*) DCTemp.SelectObject(&m_hBitmap);   // Select the bitmap

    m_hBitmap.GetObject(sizeof(BITMAP), &bm);
    SetRect(&rect, 0, 0, bm.bmWidth, bm.bmHeight);

    if (lpInfo->style == SFTTREE_OD_CALC) {
        // we need to return just the size of the cell we want to paint
        lpInfo->DrawRect = rect;
    } else {
        // we need to paint the cell
        ::FillRect(*pDC, &lpInfo->DrawRect, (HBRUSH) (COLOR_WINDOW+1));
        pDC->BitBlt(lpInfo->DrawRect.left, lpInfo->DrawRect.top, lpInfo->DrawRect.right-lpInfo->DrawRect.left, lpInfo->DrawRect.bottom-lpInfo->DrawRect.top, 
            &DCTemp, 0, 0, SRCCOPY);
    }

    DCTemp.SelectObject(pOldBitmap);
}

void CSampleView::OwnerDraw_Text(LPSFTTREE_OWNERDRAW lpInfo)
{
    CString strText;

    // Set up CDC for painting
    CDC* pDC = CDC::FromHandle(lpInfo->hDC);

    CSftTree* pTree = (CSftTree*) CWnd::FromHandle(lpInfo->hwndCtl);
    ASSERT(pTree);

    CFont* pFont = pTree->GetFont();
    CFont* pOldFont = pDC->SelectObject(pFont);
    
    pOldFont = pDC->SelectObject(pFont);

    pTree->GetText(lpInfo->index, lpInfo->col, strText); // retrieve the cell text

    if (lpInfo->style == SFTTREE_OD_CALC) {
        // we need to return just the size of the cell we want to paint
        RECT r;
        SetRectEmpty(&r);
        pDC->DrawText(strText, &r, DT_CALCRECT | DT_LEFT | DT_SINGLELINE);
        InflateRect(&r, lpInfo->gap/2, 0); 
        lpInfo->DrawRect = r;
    } else {
        // we need to paint the cell
        RECT r;
        CBrush br;
        br.CreateSysColorBrush(COLOR_3DFACE);
        pDC->FillRect(&lpInfo->DrawRect, &br);
        if (lpInfo->fSelected)
            pTree->DrawSelectionOutline(lpInfo->hDC, &lpInfo->DrawRect, RGB(128,0,0), RGB(255,100,100), RGB(255,255,255), RGB(255,0,0));
        r = lpInfo->DrawRect;
        InflateRect(&r, -lpInfo->gap/2, 0);
        pDC->DrawText(strText, &r, DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS);
    }

    pDC->SelectObject(pOldFont);
}

BOOL CALLBACK CSampleView::OwnerDrawCallback(HWND hwnd, LPSFTTREE_OWNERDRAW lpInfo, SFTTREE_DWORD_PTR UserData)
{
    CSampleView* pThis = (CSampleView*) UserData; // we defined CSampleView this pointer as user data in SetOwnerDrawCallback call
    if (lpInfo->itemType == SFTTREE_OD_COLHEADER) {
        pThis->OwnerDraw_Column_Header(lpInfo);
        return TRUE;
    }
    if ((lpInfo->itemType == SFTTREE_OD_CELL || lpInfo->itemType == SFTTREE_OD_CELLTOOLTIP) && lpInfo->col == 1) {
        if ((lpInfo->index % 5) == 0) {
            pThis->OwnerDraw_Cell(lpInfo);
            return TRUE;
        } else if ((lpInfo->index % 4) == 0) {
            pThis->OwnerDraw_Text(lpInfo);
            return TRUE;
        }
    }
    return FALSE;
}