panthema / 2007 / stx-btree / stx-btree-0.8 / wxbtreedemo / WTreeDrawing.cpp (Download File)
// $Id: WTreeDrawing.cpp 58 2007-05-13 10:51:17Z tb $

/*
 * STX B+ Tree Demo Program v0.8
 * Copyright (C) 2007 Timo Bingmann
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "WTreeDrawing.h"
#include "WMain.h"

#include <vector>

WTreeDrawing::WTreeDrawing(wxWindow *parent, int id)
    : wxScrolledWindow(parent, id),
      wmain(NULL)
{
    SetWindowStyle(wxWANTS_CHARS);
    SetSize(300, 300);

    scalefactor = 1.0;
    hasfocus = false;
}

void WTreeDrawing::SetWMain(WMain *wm)
{
    wmain = wm;
}

void WTreeDrawing::OnPaint(wxPaintEvent &)
{
    wxPaintDC dc(this);

    DoPrepareDC(dc);
    dc.SetUserScale(scalefactor, scalefactor);

    DrawBTree(dc);
}

void WTreeDrawing::OnSize(wxSizeEvent &se)
{
    Refresh();
}

void WTreeDrawing::OnSetFocus(wxFocusEvent &fe)
{
    hasfocus = true;
    Refresh();
}

void WTreeDrawing::OnKillFocus(wxFocusEvent &fe)
{
    hasfocus = false;
    Refresh();
}

void WTreeDrawing::OnMouseWheel(wxMouseEvent &me)
{
    scalefactor += 0.05 * me.GetWheelRotation() / me.GetWheelDelta();
    scalefactor = std::max(0.1, scalefactor);
    Refresh();
}

template <class BTreeType>
wxSize WTreeDrawing::BTreeOp_Draw::draw_node(int offsetx, int offsety, const class BTreeType::btree_impl::node* node)
{
    typedef class BTreeType::btree_impl btree_impl;

    static const wxColor colorMark1 = wxColor(128, 179, 255);
    static const wxColor colorMark2 = wxColor(128, 255, 128);
    static const wxColor colorUnfocused = *wxWHITE;
    static const wxColor colorFocused = wxColor(255, 255, 253);

    const int textpadding = 3;
    const int nodepadding = 10;

    if (node->isleafnode())
    {
	const typename btree_impl::leaf_node *leafnode = static_cast<const typename btree_impl::leaf_node*>(node);

	int textx = 0, texty = 0;
	int maxh = 0;
	for (unsigned int slot = 0; slot < leafnode->slotuse; ++slot)
	{
	    int textkeyw, textkeyh;
	    int textvalw, textvalh;

	    wxString textkey;
	    textkey << leafnode->slotkey[slot];
	    dc.GetTextExtent(textkey, &textkeyw, &textkeyh);

	    wxString textval;
	    textval << leafnode->slotdata[slot];
	    dc.GetTextExtent(textval, &textvalw, &textvalh);

	    int maxw = std::max(textkeyw, textvalw);
	    int addkeyw = (maxw - textkeyw) / 2;
	    int addvalw = (maxw - textvalw) / 2;

	    if (offsetx >= 0)
	    {
		if (tb.isMark1(leafnode, slot))
		{
		    dc.SetBrush(colorMark1);
		}
		else if (tb.isMark2(leafnode, slot))
		{
		    dc.SetBrush(colorMark2);
		}
		else
		{
		    if (w.hasfocus) {
			dc.SetBrush(colorFocused);
		    }
		    else {
			dc.SetBrush(colorUnfocused);
		    }
		}

		dc.DrawRectangle(offsetx + textx, offsety + texty,
				 maxw + 2*textpadding, textkeyh + 2*textpadding);

		dc.DrawText(textkey,
			    offsetx + textx + textpadding + addkeyw,
			    offsety + texty + textpadding);

		dc.DrawRectangle(offsetx + textx, offsety + texty + textkeyh + 2*textpadding - 1,
				 maxw + 2*textpadding, textvalh + 2*textpadding);

		dc.DrawText(textval,
			    offsetx + textx + textpadding + addvalw,
			    offsety + texty + textkeyh + 2*textpadding + textpadding - 1);
	    }

	    textx += maxw + 2*textpadding - 1;
	    maxh = std::max(maxh, textkeyh + 2*textpadding + textvalh + 2*textpadding - 1);
	}

	return wxSize(textx, maxh);
    }
    else
    {
	const typename btree_impl::inner_node *innernode = static_cast<const typename btree_impl::inner_node*>(node);

	const int childnum = (innernode->slotuse + 1);
	// find the maximium width and height of all children
	int childmaxw = 0, childmaxh = 0;

	std::vector<int> childw;
	for (unsigned int slot = 0; slot <= innernode->slotuse; ++slot)
	{
	    wxSize cs = draw_node<BTreeType>(-1, -1, innernode->childid[slot]);
	    childmaxw = std::max(childmaxw, cs.GetWidth());
	    childmaxh = std::max(childmaxh, cs.GetHeight());
	    childw.push_back(cs.GetWidth());
	}

	int textx = 0, texty = 0;
	int childx = 0, childy = 60;
	int maxh = 0;

	// calculate width of box.
	int allchildw = (childnum + 1) * (childmaxw / 2 + nodepadding) - 2*nodepadding;

	if (childnum % 2 == 0)
	{
	    allchildw += (childmaxw / 2 + nodepadding);
	}

	// debug rectangle for total size of children dc.DrawRectangle(childx, childy-5, allchildw, 2);

	// calc width of node's keys box
	int keyboxh = 0;
	for (unsigned int slot = 0; slot < innernode->slotuse; ++slot)
	{
	    int textkeyw, textkeyh;

	    wxString textkey;
	    textkey << innernode->slotkey[slot];
	    dc.GetTextExtent(textkey, &textkeyw, &textkeyh);

	    textx += textkeyw + 2*textpadding - 1;
	    keyboxh = std::max(keyboxh, textkeyh + 2*textpadding - 1);
	}

	textx = std::max(0, allchildw - textx) / 2;
	childy = keyboxh + nodepadding*4;

	for (unsigned int slot = 0; slot < innernode->slotuse; ++slot)
	{
	    int textkeyw, textkeyh;

	    wxString textkey;
	    textkey << innernode->slotkey[slot];
	    dc.GetTextExtent(textkey, &textkeyw, &textkeyh);

	    if (offsetx >= 0)
	    {
		if (w.hasfocus) {
		    dc.SetBrush(colorFocused);
		}
		else {
		    dc.SetBrush(colorUnfocused);
		}

		dc.DrawRectangle(offsetx + textx, offsety + texty,
				 textkeyw + 2*textpadding, textkeyh + 2*textpadding);
		dc.DrawText(textkey,
			    offsetx + textx + textpadding,
			    offsety + texty + textpadding);

		// draw child
		if (innernode->level == 1)
		{
		    // draw leaf node with border to see free slots
		    dc.DrawRectangle(offsetx + childx, offsety + childy,
				     childmaxw, childmaxh);

		    draw_node<BTreeType>(offsetx + childx, offsety + childy, innernode->childid[slot]);
		}
		else
		{
		    // draw centered inner node
		    draw_node<BTreeType>(offsetx + childx + std::max(0, (childmaxw - childw[slot]) / 2),
					 offsety + childy,
					 innernode->childid[slot]);
		}

		// calculate spline from key anchor to middle of child's box
		wxPoint splinept[4];
		splinept[0] = wxPoint(offsetx + textx, offsety + texty + keyboxh);
		splinept[1] = wxPoint(offsetx + textx, offsety + texty + keyboxh + 20);

		splinept[2] = wxPoint(offsetx + childx + childmaxw / 2, offsety + childy - 20);
		splinept[3] = wxPoint(offsetx + childx + childmaxw / 2, offsety + childy);

		dc.DrawSpline(4, splinept);
	    }

	    // advance text position
	    textx += textkeyw + 2*textpadding - 1;

	    // advance child position
	    childx += childmaxw / 2 + nodepadding;

	    if ((slot+1) * 2 == childnum)
	    {
		childx += childmaxw / 2 + nodepadding;
	    }
	    else if (slot < innernode->slotuse / 2)
	    {
		childy += childmaxh + nodepadding;
	    }
	    else
		childy -= childmaxh + nodepadding;

	    maxh = std::max(maxh, childy + childmaxh);
	}

	if (offsetx >= 0)
	{
	    // draw child
	    if (innernode->level == 1)
	    {
		// draw leaf node with border to see free slots
		if (w.hasfocus) {
		    dc.SetBrush(colorFocused);
		}
		else {
		    dc.SetBrush(colorUnfocused);
		}

		dc.DrawRectangle(offsetx + childx, offsety + childy,
				 childmaxw, childmaxh);

		draw_node<BTreeType>(offsetx + childx, offsety + childy, innernode->childid[innernode->slotuse]);
	    }
	    else
	    {
		// draw centered inner node
		draw_node<BTreeType>(offsetx + childx + std::max(0, (childmaxw - childw[innernode->slotuse]) / 2),
				     offsety + childy,
				     innernode->childid[innernode->slotuse]);
	    }

	    // calculate spline from key anchor to middle of child's box
	    wxPoint splinept[4];
	    splinept[0] = wxPoint(offsetx + textx, offsety + texty + keyboxh);
	    splinept[1] = wxPoint(offsetx + textx, offsety + texty + keyboxh + 20);

	    splinept[2] = wxPoint(offsetx + childx + childmaxw / 2, offsety + childy - 20);
	    splinept[3] = wxPoint(offsetx + childx + childmaxw / 2, offsety + childy);

	    dc.DrawSpline(4, splinept);
	}

	return wxSize(allchildw, maxh);
    }
}

template <class BTreeType>
wxSize WTreeDrawing::BTreeOp_Draw::draw_tree(BTreeType &bt)
{
    dc.SetPen(*wxBLACK_PEN);

    if (bt.tree.root)
    {
	// draw tree data items in different font sizes depending on the depth
	// of the tree
	if (bt.tree.root->level <= 1)
	{
	    dc.SetFont(wxFont(14, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
	}
	else if (bt.tree.root->level <= 2)
	{
	    dc.SetFont(wxFont(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
	}
	else if (bt.tree.root->level <= 3)
	{
	    dc.SetFont(wxFont(10, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
	}
	else {
	    dc.SetFont(wxFont(8, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL));
	}

	const int offsety = 4;

	// calculate width of the drawn tree
	wxSize ts = draw_node<BTreeType>(-1, -1, bt.tree.root);

	if (ts.GetWidth() < w.GetSize().GetWidth())
	{
	    // center small trees on the current view area
	    ts = draw_node<BTreeType>((w.GetSize().GetWidth() - ts.GetWidth()) / 2, offsety, bt.tree.root);
	}
	else
	{
	    ts = draw_node<BTreeType>(0, offsety, bt.tree.root);
	}

	if (ts != w.oldTreeSize || w.scalefactor != w.oldscalefactor)
	{
	    // set scroll bar extents
	    int scrx, scry;
	    w.GetViewStart(&scrx, &scry);
	    w.SetScrollbars(10, 10,
			    int(ts.GetWidth() / 10 * w.scalefactor),
			    int(ts.GetHeight() / 10 * w.scalefactor), scrx, scry);
	    w.oldTreeSize = ts;
	    w.oldscalefactor = w.scalefactor;
	}
    }
    else
    {
	w.SetScrollbars(10, 10, 0, 0);
    }
}

template <class BTreeType>
wxSize WTreeDrawing::BTreeOp_Draw::opInteger(BTreeType &bt)
{
    draw_tree(bt);
}

template <class BTreeType>
wxSize WTreeDrawing::BTreeOp_Draw::opIntegerMulti(BTreeType &bt)
{
    draw_tree(bt);
}

template <class BTreeType>
wxSize WTreeDrawing::BTreeOp_Draw::opString(BTreeType &bt)
{
    draw_tree(bt);
}

template <class BTreeType>
wxSize WTreeDrawing::BTreeOp_Draw::opStringMulti(BTreeType &bt)
{
    draw_tree(bt);
}

void WTreeDrawing::DrawBTree(wxDC &dc)
{
    if (!wmain) return;
    
    BTreeOp_Draw drawop(*this, dc, wmain->treebundle);
    wmain->treebundle.run(drawop);
}

BEGIN_EVENT_TABLE(WTreeDrawing, wxScrolledWindow)

    EVT_PAINT		(WTreeDrawing::OnPaint)
    EVT_SIZE		(WTreeDrawing::OnSize)
    EVT_MOUSEWHEEL	(WTreeDrawing::OnMouseWheel)

    EVT_SET_FOCUS	(WTreeDrawing::OnSetFocus)
    EVT_KILL_FOCUS	(WTreeDrawing::OnKillFocus)

END_EVENT_TABLE()