//*******************************************************************************
// COPYRIGHT NOTES
// ---------------
// You may use, compile or redistribute this source as part of your application
// for free. You may not redistribute it as a part of a software development
// library without the agreement of the author. If the sources are
// distributed along with the application, you should leave the original
// copyright notes in the source code without any changes.
// This code can be used WITHOUT ANY WARRANTIES on your own risk.
//
// Nick Hodapp
// nhodapp@codeveloper.com
//
//*******************************************************************************
// ClassView.cpp : implementation file
//
#include "stdafx.h"
#include "ClassView.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// CClassView
IMPLEMENT_DYNCREATE(CClassView, CScrollView)
CClassView::CClassView()
{
}
CClassView::~CClassView()
{
}
BEGIN_MESSAGE_MAP(CClassView, CScrollView)
//{{AFX_MSG_MAP(CClassView)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CClassView::Render(CDC* pDC, CString strRoot, CRect& rclRet, DWORD dwRow)
{
// My quick & dirty tree layout algorithm.
// If this had been an assignment in college, I'm sure I would have failed.
// The algorithm tends to draw the tree left-side heavy. But I don't feel like fixing it.
//
typedef std::vector<CPoint> V_Point;
V_Point vecPts;
long lMin(INT_MAX),
lMax(0);
// render children:
for (M_String::iterator it = m_pmapClasses->find(strRoot) ; it != m_pmapClasses->end() && it->first == strRoot ; it++)
{
// draw child -- rclRet holds position on return:
CRect rclChild;
Render(pDC, it->second, rclChild, dwRow+1);
// track pos of child
vecPts.push_back(CPoint(rclChild.CenterPoint().x, rclChild.top));
// track width of child span
lMin = min(rclChild.left, lMin);
lMax = max(rclChild.right, lMax);
}
// root element?
if (strRoot == "")
return;
// determine our own size / position:
rclRet.SetRect(0,0,0,0);
DrawTextEx(pDC->GetSafeHdc(), (char*)(const char*)strRoot, strRoot.GetLength(), &rclRet, DT_CALCRECT | DT_SINGLELINE, NULL);
// determine our position:
long offsetx = (lMin == INT_MAX ? m_alRowInsert[dwRow] : ((lMin+lMax)/2)),
offsety = dwRow * (rclRet.Height() * 4);
// adjust...
while (offsetx < m_alRowInsert[dwRow])
offsetx+=8;
// move into position:
rclRet.OffsetRect(offsetx, offsety);
// record that we're here:
m_alRowInsert[dwRow] = rclRet.right + 8;
m_alRowInsert[dwRow+1] += 8;
// draw
rclRet.InflateRect(2,2);
pDC->Draw3dRect(&rclRet, RGB(255,000,000), RGB(255,000,000));
rclRet.InflateRect(-2,-2);
DrawTextEx(pDC->GetSafeHdc(), (char*)(const char*)strRoot, strRoot.GetLength(), &rclRet, DT_SINGLELINE | DT_NOCLIP, NULL);
// draw lines from chidren to us:
CPoint ptParent(rclRet.CenterPoint().x, rclRet.bottom);
for (V_Point::iterator itPt = vecPts.begin() ; itPt != vecPts.end() ; itPt++)
{
CPoint& ptChild = *itPt;
pDC->MoveTo(ptChild);
pDC->LineTo(ptParent);
}
// ensure scroll bars work
// lame, but it works: (slows us down quite a bit...)
int mm;
CSize size, sizePage, sizeLine;
GetDeviceScrollSizes(mm, size, sizePage, sizeLine);
SetScrollSizes(MM_TEXT, CSize(max(size.cx, rclRet.right), max(size.cy, rclRet.bottom)));
}
/////////////////////////////////////////////////////////////////////////////
// CClassView drawing
void CClassView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();
CSize sizeTotal;
sizeTotal.cx = 100;
sizeTotal.cy = 100;
SetScrollSizes(MM_TEXT, sizeTotal);
ModifyStyle(0, WS_HSCROLL | WS_VSCROLL);
}
void CClassView::OnDraw(CDC* pDC)
{
/*
//
// SMALL TREE FOR TESTING PURPOSES
//
M_String map;
map.insert(M_String::value_type(CString(""), CString("a")));
map.insert(M_String::value_type(CString("a"), CString("b")));
map.insert(M_String::value_type(CString("a"), CString("c")));
map.insert(M_String::value_type(CString("a"), CString("d")));
map.insert(M_String::value_type(CString("a"), CString("e")));
map.insert(M_String::value_type(CString("a"), CString("f")));
map.insert(M_String::value_type(CString("b"), CString("g")));
map.insert(M_String::value_type(CString("b"), CString("h")));
map.insert(M_String::value_type(CString("c"), CString("i")));
map.insert(M_String::value_type(CString("c"), CString("j")));
map.insert(M_String::value_type(CString("d"), CString("k")));
map.insert(M_String::value_type(CString("d"), CString("l")));
map.insert(M_String::value_type(CString("d"), CString("m")));
map.insert(M_String::value_type(CString("e"), CString("n")));
map.insert(M_String::value_type(CString("e"), CString("o")));
map.insert(M_String::value_type(CString("e"), CString("p")));
m_pmapClasses = ↦
*/
// reset our row tracking data:
memset(m_alRowInsert, 0x00, sizeof(m_alRowInsert));
// render:
CFont font; font.CreatePointFont(100, "Courier New");
CGdiObject* pOldFont = pDC->SelectObject(&font);
Render(pDC, "", CRect(0,0,0,0), 1);
pDC->SelectObject(pOldFont);
}
/////////////////////////////////////////////////////////////////////////////
// CClassView diagnostics
#ifdef _DEBUG
void CClassView::AssertValid() const
{
CScrollView::AssertValid();
}
void CClassView::Dump(CDumpContext& dc) const
{
CScrollView::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CClassView message handlers
void CClassView::PostNcDestroy()
{
// do not delete off the heap...
// CScrollView::PostNcDestroy();
}