//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "editor/creator.h" #include "dgl/dgl.h" IMPLEMENT_CONOBJECT(CreatorTree); //------------------------------------------------------------------------------ // Class CreatorTree::Node //------------------------------------------------------------------------------ CreatorTree::Node::Node() : mFlags(0), mParent(0), mName(0), mValue(0), mId(0), mTab(0) { VECTOR_SET_ASSOCIATION(mChildren); } CreatorTree::Node::~Node() { for(U32 i = 0; i < mChildren.size(); i++) delete mChildren[i]; } //------------------------------------------------------------------------------ void CreatorTree::Node::expand(bool exp) { if(exp) { if(mParent) mParent->expand(exp); mFlags.set(Node::Expanded); } else if(!isRoot()) { if(isGroup()) for(U32 i = 0; i < mChildren.size(); i++) mChildren[i]->expand(exp); mFlags.clear(Selected); mFlags.clear(Expanded); } } //------------------------------------------------------------------------------ CreatorTree::Node * CreatorTree::Node::find(S32 id) { if(mId == id) return(this); if(!isGroup()) return(0); for(U32 i = 0; i < mChildren.size(); i++) { Node * node = mChildren[i]->find(id); if(node) return(node); } return(0); } //------------------------------------------------------------------------------ bool CreatorTree::Node::isFirst() { AssertFatal(!isRoot(), "CreatorTree::Node::isFirst - cannot call on root node"); return(this == mParent->mChildren[0]); } bool CreatorTree::Node::isLast() { AssertFatal(!isRoot(), "CreatorTree::Node::isLast - cannot call on root node"); return(this == mParent->mChildren[mParent->mChildren.size()-1]); } bool CreatorTree::Node::hasChildItem() { for(U32 i = 0; i < mChildren.size(); i++) { if(mChildren[i]->isGroup() && mChildren[i]->hasChildItem()) return(true); if(!mChildren[i]->isGroup()) return(true); } return(false); } S32 CreatorTree::Node::getSelected() { for(U32 i = 0; i < mChildren.size(); i++) { if(mChildren[i]->isSelected()) return(mChildren[i]->mId); else if(mChildren[i]->isGroup()) { S32 ret = mChildren[i]->getSelected(); if(ret != -1) return(ret); } } return(-1); } //------------------------------------------------------------------------------ // Class CreatorTree //------------------------------------------------------------------------------ CreatorTree::CreatorTree() : mCurId(0), mTxtOffset(5), mRoot(0) { VECTOR_SET_ASSOCIATION(mNodeList); clear(); } CreatorTree::~CreatorTree() { delete mRoot; } //------------------------------------------------------------------------------ CreatorTree::Node * CreatorTree::createNode(const char * name, const char * value, bool group, Node * parent) { Node * node = new Node(); node->mId = mCurId++; node->mName = name ? StringTable->insert(name) : 0; node->mValue = value ? StringTable->insert(value) : 0; node->mFlags.set(Node::Group, group); // add to the parent group if(parent) { node->mParent = parent; if(!addNode(parent, node)) { delete node; return(0); } } return(node); } //------------------------------------------------------------------------------ void CreatorTree::clear() { delete mRoot; mCurId = 0; mRoot = createNode(0, 0, true); mRoot->mFlags.set(Node::Root | Node::Expanded); mSize = Point2I(1,0); } //------------------------------------------------------------------------------ bool CreatorTree::addNode(Node * parent, Node * node) { if(!parent->isGroup()) return(false); // parent->mChildren.push_back(node); return(true); } //------------------------------------------------------------------------------ CreatorTree::Node * CreatorTree::findNode(S32 id) { return(mRoot->find(id)); } //------------------------------------------------------------------------------ void CreatorTree::sort() { // groups then items by alpha } //------------------------------------------------------------------------------ ConsoleMethod( CreatorTree, addGroup, S32, 4, 4, "(string group, string name, string value)") { CreatorTree::Node * grp = object->findNode(dAtoi(argv[2])); if(!grp || !grp->isGroup()) return(-1); // return same named group if found... for(U32 i = 0; i < grp->mChildren.size(); i++) if(!dStricmp(argv[3], grp->mChildren[i]->mName)) return(grp->mChildren[i]->mId); CreatorTree::Node * node = object->createNode(argv[3], 0, true, grp); object->build(); return(node ? node->getId() : -1); } ConsoleMethod( CreatorTree, addItem, S32, 5, 5, "(Node group, string name, string value)") { CreatorTree::Node * grp = object->findNode(dAtoi(argv[2])); if(!grp || !grp->isGroup()) return -1; CreatorTree::Node * node = object->createNode(argv[3], argv[4], false, grp); object->build(); return(node ? node->getId() : -1); } //------------------------------------------------------------------------------ ConsoleMethod( CreatorTree, fileNameMatch, bool, 5, 5, "(string world, string type, string filename)"){ // argv[2] - world short // argv[3] - type short // argv[4] - filename // interior filenames // 0 - world short ('b', 'x', ...) // 1-> - type short ('towr', 'bunk', ...) U32 typeLen = dStrlen(argv[3]); if(dStrlen(argv[4]) < (typeLen + 1)) return(false); // world if(dToupper(argv[4][0]) != dToupper(argv[2][0])) return(false); return(!dStrnicmp(argv[4]+1, argv[3], typeLen)); } ConsoleMethod( CreatorTree, getSelected, S32, 2, 2, "Return a handle to the currently selected item.") { return(object->getSelected()); } ConsoleMethod( CreatorTree, isGroup, bool, 3, 3, "(Group g)") { CreatorTree::Node * node = object->findNode(dAtoi(argv[2])); if(node && node->isGroup()) return(true); return(false); } ConsoleMethod( CreatorTree, getName, const char*, 3, 3, "(Node item)") { CreatorTree::Node * node = object->findNode(dAtoi(argv[2])); return(node ? node->mName : 0); } ConsoleMethod( CreatorTree, getValue, const char*, 3, 3, "(Node n)") { CreatorTree::Node * node = object->findNode(dAtoi(argv[2])); return(node ? node->mValue : 0); } ConsoleMethod( CreatorTree, clear, void, 2, 2, "Clear the tree.") { object->clear(); } ConsoleMethod( CreatorTree, getParent, S32, 3, 3, "(Node n)") { CreatorTree::Node * node = object->findNode(dAtoi(argv[2])); if(node && node->mParent) return(node->mParent->getId()); else return(-1); } //------------------------------------------------------------------------------ void CreatorTree::buildNode(Node * node, U32 tab) { if(node->isExpanded()) for(U32 i = 0; i < node->mChildren.size(); i++) { Node * child = node->mChildren[i]; child->mTab = tab; child->select(false); mNodeList.push_back(child); // grab width if(bool(mProfile->mFont) && child->mName) { S32 width = (tab + 1) * mTabSize + mProfile->mFont->getStrWidth(child->mName) + mTxtOffset; if(width > mMaxWidth) mMaxWidth = width; } if(node->mChildren[i]->isGroup()) buildNode(node->mChildren[i], tab+1); } } //------------------------------------------------------------------------------ void CreatorTree::build() { mMaxWidth = 0; mNodeList.clear(); buildNode(mRoot, 0); mCellSize.set( mMaxWidth + 1, 11 ); setSize(Point2I(1, mNodeList.size())); } //------------------------------------------------------------------------------ bool CreatorTree::onWake() { if(!Parent::onWake()) return(false); mTabSize = 11; // build(); mCellSize.set( mMaxWidth + 1, 11 ); setSize(Point2I(1, mNodeList.size())); return true; } //------------------------------------------------------------------------------ void CreatorTree::onMouseUp(const GuiEvent & event) { onAction(); } void CreatorTree::onMouseDown(const GuiEvent & event) { Point2I pos = globalToLocalCoord(event.mousePoint); bool dblClick = event.mouseClickCount > 1; // determine cell Point2I cell(pos.x < 0 ? -1 : pos.x / mCellSize.x, pos.y < 0 ? -1 : pos.y / mCellSize.y); if(cell.x >= 0 && cell.x < mSize.x && cell.y >= 0 && cell.y < mSize.y) { Node * node = mNodeList[cell.y]; S32 offset = mTabSize * node->mTab; if(node->isGroup() && node->mChildren.size() && pos.x >= offset && pos.x <= (offset + mTabSize)) { node->expand(!node->isExpanded()); build(); dblClick = false; } if(pos.x >= offset) { if(dblClick) node->expand(!node->isExpanded()); build(); node->select(true); } } } //------------------------------------------------------------------------------ void CreatorTree::onMouseDragged(const GuiEvent & event) { event; } //------------------------------------------------------------------------------ void CreatorTree::onRenderCell(Point2I offset, Point2I cell, bool, bool) { Point2I cellOffset = offset; Node *node = mNodeList[cell.y]; // Get our points Point2I boxStart( cellOffset.x + mTabSize * node->mTab, cellOffset.y ); boxStart.x += 2; boxStart.y += 1; Point2I boxEnd = Point2I( boxStart ); boxEnd.x += 8; boxEnd.y += 8; // Start drawing stuff if( node->isGroup() ) { // If we need a box... dglDrawRectFill( boxStart, boxEnd, mProfile->mFillColor ); // Box background dglDrawRect( boxStart, boxEnd, mProfile->mFontColor ); // Border // Cross line dglDrawLine( boxStart.x + 2, boxStart.y + 4, boxStart.x + 7, boxStart.y + 4, mProfile->mFontColor ); if( !node->isExpanded() ) // If it's a [+] draw down line dglDrawLine( boxStart.x + 4, boxStart.y + 2, boxStart.x + 4, boxStart.y + 7, mProfile->mFontColor ); } else { // Draw horizontal line dglDrawLine( boxStart.x + 4, boxStart.y + 4, boxStart.x + 9, boxStart.y + 4, mProfile->mFontColor ); if( !node->isLast() ) // If it's a continuing one, draw a long down line dglDrawLine( boxStart.x + 4, boxStart.y - 6, boxStart.x + 4, boxStart.y + 10, mProfile->mFontColor ); else // Otherwise, just a small one dglDrawLine( boxStart.x + 4, boxStart.y - 2, boxStart.x + 4, boxStart.y + 4, mProfile->mFontColor ); } //draw in all the required continuation lines Node *parent = node->mParent; while( !parent->isRoot() ) { if( !parent->isLast() ) { dglDrawLine( cellOffset.x + ( parent->mTab * mTabSize ) + 6, cellOffset.y - 2, cellOffset.x + ( parent->mTab * mTabSize ) + 6, cellOffset.y + 11, mProfile->mFontColor ); } parent = parent->mParent; } ColorI fontColor = mProfile->mFontColor; if( node->isSelected() ) fontColor = mProfile->mFontColorHL; else if( node->isGroup() && node->hasChildItem() ) fontColor.set( 128, 0, 0 ); else if( !node->isGroup() ) fontColor.set( 0, 0, 128 ); dglSetBitmapModulation(fontColor); //node->isSelected() ? mProfile->mFontColorHL : mProfile->mFontColor); dglDrawText( mProfile->mFont, Point2I( offset.x + mTxtOffset + mTabSize * ( node->mTab + 1 ), offset.y ), node->mName); }