tge/engine/gui/controls/guiTreeViewCtrl.cc
2017-04-17 06:17:10 -06:00

3825 lines
112 KiB
C++
Executable File

//-----------------------------------------------------------------------------
// Torque Game Engine
// Copyright (C) GarageGames.com, Inc.
//-----------------------------------------------------------------------------
#include "core/frameAllocator.h"
#include "gui/controls/guiTreeViewCtrl.h"
#include "gui/containers/guiScrollCtrl.h"
#include "console/consoleTypes.h"
#include "console/console.h"
#include "dgl/dgl.h"
#include "gui/core/guiTypes.h"
#include "platform/event.h"
IMPLEMENT_CONOBJECT(GuiTreeViewCtrl);
//-----------------------------------------------------------------------------
// TreeView Item
//-----------------------------------------------------------------------------
GuiTreeViewCtrl::Item::Item( GuiControlProfile *pProfile )
{
AssertFatal( pProfile != NULL , "Cannot create a tree item without a valid tree and control profile!");
mState = 0;
mId = -1;
mTabLevel = 0;
mIcon = 0;
mDataRenderWidth = 0;
mScriptInfo.mText = NULL;
mScriptInfo.mValue = NULL;
mParent = NULL;
mChild = NULL;
mNext = NULL;
mPrevious = NULL;
mProfile = pProfile;
}
GuiTreeViewCtrl::Item::~Item()
{
if( ! mState.test(InspectorData) )
{
if ( getText() )
{
delete [] getText();
setText(NULL);
}
if ( getValue() )
{
delete [] getValue();
setValue( NULL );
}
}
}
void GuiTreeViewCtrl::Item::setNormalImage(S8 id)
{
if(mState.test(InspectorData))
{
Con::errorf("Tried to set normal image %d for item %d, which is InspectorData!", id, mId);
return;
}
mScriptInfo.mNormalImage = id;
}
void GuiTreeViewCtrl::Item::setExpandedImage(S8 id)
{
if(mState.test(InspectorData))
{
Con::errorf("Tried to set expanded image %d for item %d, which is InspectorData!", id, mId);
return;
}
mScriptInfo.mExpandedImage = id;
}
void GuiTreeViewCtrl::Item::setText(char *txt)
{
if(mState.test(InspectorData))
{
Con::errorf("Tried to set text for item %d, which is InspectorData!", mId);
return;
}
mScriptInfo.mText = txt;
// Update Render Data
if( !mProfile.isNull() )
mDataRenderWidth = getDisplayTextWidth( mProfile->mFont );
}
void GuiTreeViewCtrl::Item::setValue(const char *val)
{
if(mState.test(InspectorData))
{
Con::errorf("Tried to set value for item %d, which is InspectorData!", mId);
return;
}
mScriptInfo.mValue = const_cast<char*>(val); // mValue really ought to be a StringTableEntry
// Update Render Data
if( !mProfile.isNull() )
mDataRenderWidth = getDisplayTextWidth( mProfile->mFont );
}
const S8 GuiTreeViewCtrl::Item::getNormalImage() const
{
if(mState.test(InspectorData))
{
Con::errorf("Tried to get the normal image for item %d, which is InspectorData!", mId);
return 0; // fail safe for width determinations
}
return mScriptInfo.mNormalImage;
}
const S8 GuiTreeViewCtrl::Item::getExpandedImage() const
{
if(mState.test(InspectorData))
{
Con::errorf("Tried to get the expanded image for item %d, which is InspectorData!", mId);
return 0; // fail safe for width determinations
}
return mScriptInfo.mExpandedImage;
}
char *GuiTreeViewCtrl::Item::getText()
{
if(mState.test(InspectorData))
{
Con::errorf("Tried to get the text for item %d, which is InspectorData!", mId);
return NULL;
}
return mScriptInfo.mText;
}
char *GuiTreeViewCtrl::Item::getValue()
{
if(mState.test(InspectorData))
{
Con::errorf("Tried to get the value for item %d, which is InspectorData!", mId);
return NULL;
}
return mScriptInfo.mValue;
}
void GuiTreeViewCtrl::Item::setObject(SimObject *obj)
{
if(!mState.test(InspectorData))
{
Con::errorf("Tried to set the object for item %d, which is not InspectorData!", mId);
return;
}
mInspectorInfo.mObject = obj;
// Update Render Data
if( !mProfile.isNull() )
mDataRenderWidth = getDisplayTextWidth( mProfile->mFont );
}
SimObject *GuiTreeViewCtrl::Item::getObject()
{
if(!mState.test(InspectorData))
{
Con::errorf("Tried to get the object for item %d, which is not InspectorData!", mId);
return NULL;
}
return mInspectorInfo.mObject;
}
const U32 GuiTreeViewCtrl::Item::getDisplayTextLength()
{
if(mState.test(Item::InspectorData))
{
SimObject *obj = getObject();
if(!obj)
return 0;
// For the results buffer, it's prefix along with a bunch of other stuff.
// So we'll be mostly accurate and add a bit of fudge.
return (16
+ (obj->getName() ? dStrlen(obj->getName()) : 0) + dStrlen(obj->getClassName())
+ dStrlen(obj->getIdString()) + 20
);
}
char *pText = getText();
if( pText == NULL )
return 0;
return dStrlen( pText ) + 1;
}
void GuiTreeViewCtrl::Item::getDisplayText(U32 bufLen, char *buf)
{
FrameAllocatorMarker txtAlloc;
char *displayText = NULL;
if(mState.test(Item::InspectorData))
{
// Inspector data!
SimObject *pObject = getObject();
if(pObject)
{
const char* pObjName = pObject->getName();
const char* pInternalName = pObject->getInternalName();
if( pObjName != NULL )
dSprintf(buf, bufLen, "%d: %s - %s", pObject->getId(), pObject->getClassName(), pObjName );
else if ( pInternalName != NULL )
dSprintf(buf, bufLen, "%d: %s [%s]", pObject->getId(), pObject->getClassName(), pInternalName);
else
dSprintf(buf, bufLen, "%d: %s", pObject->getId(), pObject->getClassName());
}
}
else
{
// Script data! (copy it in)
dStrncpy(buf, getText(), bufLen);
}
}
const S32 GuiTreeViewCtrl::Item::getDisplayTextWidth(GFont *font)
{
if( !font )
return 0;
FrameAllocatorMarker txtAlloc;
U32 bufLen = getDisplayTextLength();
if( bufLen == 0 )
return 0;
char *buf = (char*)txtAlloc.alloc(bufLen);
getDisplayText(bufLen, buf);
return font->getStrWidth(buf);
}
const bool GuiTreeViewCtrl::Item::isParent() const
{
if(mState.test(VirtualParent))
{
if( !isInspectorData() )
return true;
// Does our object have any children?
if(mInspectorInfo.mObject)
{
SimSet *pSimSet = dynamic_cast<SimSet*>( (SimObject*)mInspectorInfo.mObject);
if ( pSimSet != NULL && pSimSet->size() > 0)
return pSimSet->size();
}
}
// Otherwise, just return whether the child list is populated.
return mChild;
}
const bool GuiTreeViewCtrl::Item::isExpanded() const
{
if(mState.test(InspectorData))
return mInspectorInfo.mObject ? mInspectorInfo.mObject->isExpanded() : false;
else
return mState.test(Expanded);
}
void GuiTreeViewCtrl::Item::setExpanded(bool f)
{
if(mState.test(InspectorData) && !mInspectorInfo.mObject.isNull() )
mInspectorInfo.mObject->setExpanded(f);
else
mState.set(Expanded, f);
}
void GuiTreeViewCtrl::Item::setVirtualParent( bool value )
{
mState.set(VirtualParent, value);
}
GuiTreeViewCtrl::Item *GuiTreeViewCtrl::Item::findChildByValue(const SimObject *obj)
{
// Iterate over our children and try to find the given
// SimObject
Item *pResultObj = mChild;
while(pResultObj)
{
// Skip non-inspector data stuff.
if(pResultObj->mState.test(InspectorData))
{
if(pResultObj->getObject() == obj)
break; // Whoa.
}
pResultObj = pResultObj->mNext;
}
// If the loop terminated we are NULL, otherwise we have the result in res.
return pResultObj;
}
GuiTreeViewCtrl::Item *GuiTreeViewCtrl::Item::findChildByValue( StringTableEntry Value )
{
// Iterate over our children and try to find the given Value
// Note : This is a case-insensitive search
Item *pResultObj = mChild;
while(pResultObj)
{
// check the script value of the item against the specified value
if( pResultObj->mScriptInfo.mValue != NULL && dStricmp( pResultObj->mScriptInfo.mValue, Value ) == 0 )
return pResultObj;
pResultObj = pResultObj->mNext;
}
// If the loop terminated we didn't find an item with the specified script value
return NULL;
}
//------------------------------------------------------------------------------
GuiTreeViewCtrl::GuiTreeViewCtrl()
{
VECTOR_SET_ASSOCIATION(mItems);
VECTOR_SET_ASSOCIATION(mVisibleItems);
VECTOR_SET_ASSOCIATION(mSelectedItems);
VECTOR_SET_ASSOCIATION(mSelected);
mItemFreeList = NULL;
mRoot = NULL;
mInstantGroup = 0;
mItemCount = 0;
mSelectedItem = 0;
mStart = 0;
mTicksPassed = 0;
mTreeRefreshInterval = 30;
mDraggedToItem = 0;
mOldDragY = 0;
mCurrentDragCell = 0;
mPreviousDragCell = 0;
mDragMidPoint = NomDragMidPoint;
mMouseDragged = false;
mDebug = false;
// persist info..
mTabSize = 16;
mTextOffset = 2;
mFullRowSelect = false;
mItemHeight = 20;
//
setSize(Point2I(1, 0));
// Set up default state
mFlags.set(ShowTreeLines);
mFlags.set(IsEditable, false);
mDestroyOnSleep = true;
mSupportMouseDragging = true;
mMultipleSelections = true;
mDeleteObjectAllowed = true;
mDragToItemAllowed = true;
mBitmapBase = StringTable->insert("");
mTexRollover = NULL;
mTexSelected = NULL;
}
GuiTreeViewCtrl::~GuiTreeViewCtrl()
{
destroyTree();
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::initPersistFields()
{
Parent::initPersistFields();
addGroup("TreeView");
addField("tabSize", TypeS32, Offset(mTabSize, GuiTreeViewCtrl));
addField("textOffset", TypeS32, Offset(mTextOffset, GuiTreeViewCtrl));
addField("fullRowSelect", TypeBool, Offset(mFullRowSelect, GuiTreeViewCtrl));
addField("itemHeight", TypeS32, Offset(mItemHeight, GuiTreeViewCtrl));
addField("destroyTreeOnSleep", TypeBool, Offset(mDestroyOnSleep, GuiTreeViewCtrl));
addField("MouseDragging", TypeBool, Offset(mSupportMouseDragging, GuiTreeViewCtrl));
addField("MultipleSelections", TypeBool, Offset(mMultipleSelections, GuiTreeViewCtrl));
addField("DeleteObjectAllowed", TypeBool, Offset(mDeleteObjectAllowed, GuiTreeViewCtrl));
addField("DragToItemAllowed", TypeBool, Offset(mDragToItemAllowed, GuiTreeViewCtrl));
endGroup("TreeView");
}
//------------------------------------------------------------------------------
GuiTreeViewCtrl::Item * GuiTreeViewCtrl::getItem(S32 itemId)
{
if((itemId > 0) && (itemId <= mItems.size()))
return(mItems[itemId-1]);
return(0);
}
//------------------------------------------------------------------------------
GuiTreeViewCtrl::Item * GuiTreeViewCtrl::createItem(S32 icon)
{
Item * pNewItem = NULL;
// grab from the free list?
if( mItemFreeList )
{
pNewItem = mItemFreeList;
mItemFreeList = pNewItem->mNext;
// re-add to vector
mItems[ pNewItem->mId - 1 ] = pNewItem;
}
else
{
pNewItem = new Item( mProfile );
AssertFatal( pNewItem != NULL, "Fatal : unable to allocate tree item!");
mItems.push_back( pNewItem );
// set the id
pNewItem->mId = mItems.size();
}
// reset
if (icon)
pNewItem->mIcon = icon;
else
pNewItem->mIcon = Default; //default icon to stick next to an item
pNewItem->mState.clear();
pNewItem->mState = 0;
pNewItem->mTabLevel = 0;
// Null out item pointers
pNewItem->mNext = 0;
pNewItem->mPrevious = 0;
pNewItem->mChild = 0;
pNewItem->mParent = 0;
mItemCount++;
return pNewItem;
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::destroyChildren(Item * item, Item * parent)
{
if ( !item || item == parent )
return;
if ( item->isParent() && item->mChild )
destroyChildren(item->mChild, parent);
else if( item->mNext )
destroyChildren(item->mNext, parent);
else
{
// destroy the item and back up
Item * pPrevItem = item->mPrevious;
destroyItem( item );
destroyChildren( pPrevItem, parent );
}
}
void GuiTreeViewCtrl::destroyItem(Item * item)
{
if(!item)
return;
if(item->isInspectorData())
{
// make sure the SimObjectPtr is clean!
//Con::executef(this,2, "onDeleteObject",Con::getIntArg(item->mInspectorInfo.mObject->getIdString()));
SimObject *pObject = item->getObject();
if( pObject && pObject->isProperlyAdded() )
pObject->deleteObject();
item->setObject( NULL );
}
else
{
char *pCleanup = item->getText();
// Clean up the memory...
if ( pCleanup != NULL )
{
delete []pCleanup;
item->setText( NULL );
}
pCleanup = item->getValue();
if ( pCleanup != NULL )
{
delete []pCleanup;
item->setValue( NULL );
}
}
// unlink
if( item->mPrevious )
item->mPrevious->mNext = item->mNext;
if( item->mNext )
item->mNext->mPrevious = item->mPrevious;
if( item->mParent && ( item->mParent->mChild == item ) )
item->mParent->mChild = item->mNext;
// remove from vector
mItems[item->mId-1] = 0;
// set as root free item
item->mNext = mItemFreeList;
mItemFreeList = item;
mItemCount--;
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::deleteItem(Item *item)
{
removeItem(item->mId);
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::destroyTree()
{
// clear the item list
for(U32 i = 0; i < mItems.size(); i++)
{
Item *pFreeItem = mItems[ i ];
if( pFreeItem != NULL )
delete pFreeItem;
}
mItems.clear();
// clear the free list
while(mItemFreeList)
{
Item *next = mItemFreeList->mNext;
delete mItemFreeList;
mItemFreeList = next;
}
mVisibleItems.clear();
mSelectedItems.clear();
//
mRoot = NULL;
mItemFreeList = NULL;
mItemCount = 0;
mSelectedItem = 0;
mDraggedToItem = 0;
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::buildItem( Item* item, U32 tabLevel, bool bForceFullUpdate )
{
if (!item || !mActive || !isVisible() || !mProfile )
return;
// If it's inspector data, make sure we still have it, if not, kill it.
if(item->isInspectorData() && !item->getObject() )
{
removeItem(item->mId);
return;
}
// If it's a virtual parent, give a chance to update itself...
if(item->mState.test( Item::VirtualParent) )
{
// If it returns false the item has been removed.
if(!onVirtualParentBuild(item, bForceFullUpdate))
return;
}
item->mTabLevel = tabLevel;
mVisibleItems.push_back( item );
if ( mProfile != NULL && !mProfile->mFont.isNull() )
{
S32 width = ( tabLevel + 1 ) * mTabSize + item->getDisplayTextWidth(mProfile->mFont);
if ( mProfile->mBitmapArrayRects.size() > 0 )
width += mProfile->mBitmapArrayRects[0].extent.x;
width += (item->mTabLevel+1) * mItemHeight; // using mItemHeight for icon width, close enough
// this will only fail if somebody starts using super wide icons.
if ( width > mMaxWidth )
mMaxWidth = width;
}
// if expanded, then add all the children items as well
if ( item->isExpanded() || bForceFullUpdate)
{
Item * child = item->mChild;
while ( child )
{
// Bit of a hack so we can safely remove items as we
// traverse.
Item *pChildTemp = child;
child = child->mNext;
buildItem( pChildTemp, tabLevel + 1, bForceFullUpdate );
}
}
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::buildVisibleTree(bool bForceFullUpdate)
{
// Recursion Prevention.
if( mFlags.test( BuildingVisTree ) )
return;
mFlags.set( BuildingVisTree, true );
mMaxWidth = 0;
mVisibleItems.clear();
// Update the flags.
mFlags.clear(RebuildVisible);
// build the root items
Item *traverse = mRoot;
while(traverse)
{
buildItem(traverse, 0, bForceFullUpdate);
traverse = traverse->mNext;
}
// adjust the GuiArrayCtrl
mCellSize.set(mMaxWidth+1, mItemHeight);
setSize(Point2I(1, mVisibleItems.size()));
syncSelection();
// Done Recursing.
mFlags.clear( BuildingVisTree );
}
//------------------------------------------------------------------------------
bool GuiTreeViewCtrl::scrollVisible( S32 itemId )
{
Item* item = getItem(itemId);
if(item)
return scrollVisible(item);
return false;
}
bool GuiTreeViewCtrl::scrollVisible( Item *item )
{
// Now, make sure it's visible (ie, all parents expanded)
Item *parent = item->mParent;
if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
onVirtualParentExpand(item);
while(parent)
{
parent->setExpanded(true);
if( !parent->isInspectorData() && parent->mState.test(Item::VirtualParent) )
onVirtualParentExpand(parent);
parent = parent->mParent;
}
// Get our scroll-pappy, if any.
GuiScrollCtrl *pScrollParent = dynamic_cast<GuiScrollCtrl*>( getParent() );
if ( !pScrollParent )
{
Con::warnf("GuiTreeViewCtrl::scrollVisible - parent control is not a GuiScrollCtrl!");
return false;
}
// And now, build the visible tree so we know where we have to scroll.
buildVisibleTree();
// All done, let's figure out where we have to scroll...
for(S32 i=0; i<mVisibleItems.size(); i++)
{
if(mVisibleItems[i] == item)
{
pScrollParent->scrollRectVisible(RectI(0, i * mItemHeight, mMaxWidth, mItemHeight));
return true;
}
}
// If we got here, it's probably bad...
Con::errorf("GuiTreeViewCtrl::scrollVisible - was unable to find specified item in visible list!");
return false;
}
//------------------------------------------------------------------------------
S32 GuiTreeViewCtrl::insertItem(S32 parentId, const char * text, const char * value, const char * iconString, S16 normalImage, S16 expandedImage)
{
if( ( parentId < 0 ) || ( parentId > mItems.size() ) )
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::insertItem: invalid parent id!");
return 0;
}
if((parentId != 0) && (mItems[parentId-1] == 0))
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::insertItem: parent item invalid!");
return 0;
}
S32 icon = getIcon(iconString);
// create an item (assigns id)
Item * pNewItem = createItem(icon);
if( pNewItem == NULL )
return 0;
const char * pItemText = ( text != NULL ) ? text : "";
const char * pItemValue = ( value != NULL ) ? value : "";
char *tmp = new char[dStrlen( pItemText ) + 1];
dStrcpy( tmp, pItemText );
pNewItem->setText( tmp );
tmp = new char[dStrlen( pItemValue ) + 1];
dStrcpy( tmp, pItemValue );
pNewItem->setValue( tmp );
pNewItem->setNormalImage( normalImage );
pNewItem->setExpandedImage( expandedImage );
// root level?
if(parentId == 0)
{
// insert back
if( mRoot != NULL )
{
Item * pTreeTraverse = mRoot;
while( pTreeTraverse != NULL && pTreeTraverse->mNext != NULL )
pTreeTraverse = pTreeTraverse->mNext;
pTreeTraverse->mNext = pNewItem;
pNewItem->mPrevious = pTreeTraverse;
}
else
mRoot = pNewItem;
mFlags.set(RebuildVisible);
}
else if( mItems.size() >= ( parentId - 1 ) )
{
Item * pParentItem = mItems[parentId-1];
// insert back
if( pParentItem != NULL && pParentItem->mChild)
{
Item * pTreeTraverse = pParentItem->mChild;
while( pTreeTraverse != NULL && pTreeTraverse->mNext != NULL )
pTreeTraverse = pTreeTraverse->mNext;
pTreeTraverse->mNext = pNewItem;
pNewItem->mPrevious = pTreeTraverse;
}
else
pParentItem->mChild = pNewItem;
pNewItem->mParent = pParentItem;
if( pParentItem->isExpanded() )
mFlags.set(RebuildVisible);
}
//
if(mFlags.test(RebuildVisible))
buildVisibleTree();
return pNewItem->mId;
}
//------------------------------------------------------------------------------
bool GuiTreeViewCtrl::removeItem(S32 itemId)
{
// tree?
if(itemId == 0)
{
destroyTree();
return(true);
}
Item * item = getItem(itemId);
if(!item)
{
//Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::removeItem: invalid item id!");
return false;
}
// root?
if(item == mRoot)
mRoot = item->mNext;
// Dispose of any children...
if (item->mChild)
destroyChildren(item->mChild, item);
// Kill the item...
destroyItem(item);
// Update the rendered tree...
buildVisibleTree();
return true;
}
void GuiTreeViewCtrl::removeAllChildren(S32 itemId)
{
Item * item = getItem(itemId);
if(item)
{
destroyChildren(item->mChild, item);
}
}
//------------------------------------------------------------------------------
const S32 GuiTreeViewCtrl::getFirstRootItem() const
{
return (mRoot ? mRoot->mId : 0);
}
//------------------------------------------------------------------------------
S32 GuiTreeViewCtrl::getChildItem(S32 itemId)
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getChild: invalid item id!");
return(0);
}
return(item->mChild ? item->mChild->mId : 0);
}
S32 GuiTreeViewCtrl::getParentItem(S32 itemId)
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getParent: invalid item id!");
return(0);
}
return(item->mParent ? item->mParent->mId : 0);
}
S32 GuiTreeViewCtrl::getNextSiblingItem(S32 itemId)
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getNextSibling: invalid item id!");
return(0);
}
return(item->mNext ? item->mNext->mId : 0);
}
S32 GuiTreeViewCtrl::getPrevSiblingItem(S32 itemId)
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getPrevSibling: invalid item id!");
return(0);
}
return(item->mPrevious ? item->mPrevious->mId : 0);
}
//------------------------------------------------------------------------------
S32 GuiTreeViewCtrl::getItemCount()
{
return(mItemCount);
}
S32 GuiTreeViewCtrl::getSelectedItem()
{
return mSelectedItem;
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::moveItemUp( S32 itemId )
{
GuiTreeViewCtrl::Item* pItem = getItem( itemId );
if ( !pItem )
{
Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemUp: invalid item id!");
return;
}
Item * pParent = pItem->mParent;
Item * pPrevItem = pItem->mPrevious;
if ( pPrevItem == NULL || pParent == NULL )
{
Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemUp: Unable to move item up, bad data!");
return;
}
// Diddle the linked list!
if ( pPrevItem->mPrevious )
pPrevItem->mPrevious->mNext = pItem;
else if ( pItem->mParent )
pItem->mParent->mChild = pItem;
if ( pItem->mNext )
pItem->mNext->mPrevious = pPrevItem;
pItem->mPrevious = pPrevItem->mPrevious;
pPrevItem->mNext = pItem->mNext;
pItem->mNext = pPrevItem;
pPrevItem->mPrevious = pItem;
// Update SimObjects if Appropriate.
SimObject * pSimObject = NULL;
SimSet * pParentSet = NULL;
// Fetch Current Add Set
if( pParent->isInspectorData() )
pParentSet = dynamic_cast<SimSet*>( pParent->getObject() );
else
{
// parent is probably script data so we search up the tree for a
// set to put our object in
Item * pTraverse = pItem->mParent;
while ( pTraverse != NULL && !pTraverse->isInspectorData() )
pTraverse = pTraverse->mParent;
// found an ancestor who is an inspectorData?
pParentSet = pTraverse->isInspectorData() ? dynamic_cast<SimSet*>( pTraverse->getObject() ) : NULL;
}
// Reorder the item and make sure that the children of the item get updated
// correctly prev item may be script... so find a prevItem if there is.
// We only need to reorder if there you move it above an inspector item.
if ( pSimObject != NULL && pParentSet != NULL )
{
Item * pTraverse = pItem->mNext;
while(pTraverse)
{
if (pTraverse->isInspectorData())
break;
pTraverse = pTraverse->mNext;
}
if (pTraverse && pItem->getObject() && pTraverse->getObject())
pParentSet->reOrder(pItem->getObject(), pTraverse->getObject());
}
buildVisibleTree();
}
void GuiTreeViewCtrl::moveItemDown( S32 itemId )
{
GuiTreeViewCtrl::Item* item = getItem( itemId );
if ( !item )
{
Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemDown: invalid item id!");
return;
}
Item* nextItem = item->mNext;
if ( !nextItem )
{
Con::errorf( ConsoleLogEntry::General, "GuiTreeViewCtrl::moveItemDown: no next sibling?");
return;
}
// Diddle the linked list!
if ( nextItem->mNext )
nextItem->mNext->mPrevious = item;
if ( item->mPrevious )
item->mPrevious->mNext = nextItem;
else if ( item->mParent )
item->mParent->mChild = nextItem;
item->mNext = nextItem->mNext;
nextItem->mPrevious = item->mPrevious;
item->mPrevious = nextItem;
nextItem->mNext = item;
// And update the simobjects if apppropriate...
SimObject * simobj = NULL;
if (item->isInspectorData())
simobj = item->getObject();
SimSet *parentSet = NULL;
// grab the current parentSet if there is any...
if(item->mParent->isInspectorData())
parentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
else
{
// parent is probably script data so we search up the tree for a
// set to put our object in
Item * temp = item->mParent;
while (!temp->isInspectorData())
temp = temp->mParent;
// found an ancestor who is an inspectorData?
parentSet = temp->isInspectorData() ? dynamic_cast<SimSet*>(temp->getObject()) : NULL;
}
// Reorder the item and make sure that the children of the item get updated
// correctly prev item may be script... so find a prevItem if there is.
// We only need to reorder if there you move it above an inspector item.
if (simobj && parentSet)
{
Item * temp = item->mPrevious;
while(temp)
{
if (temp->isInspectorData())
break;
temp = temp->mPrevious;
}
if (temp && item->getObject() && temp->getObject())
parentSet->reOrder(temp->getObject(), item->getObject());
}
buildVisibleTree();
}
//------------------------------------------------------------------------------
bool GuiTreeViewCtrl::onWake()
{
if(!Parent::onWake() || !mProfile->constructBitmapArray())
return false;
// If destroy on sleep, then we have to give things a chance to rebuild.
if(mDestroyOnSleep)
{
destroyTree();
Con::executef(this, 1, "onWake");
// (Re)build our icon table.
const char * res = Con::executef(this, 1, "onDefineIcons");
// If no icons were defined in script then use defaults.
if(!(dAtob(res)))
{
buildIconTable(NULL);
}
}
// Update the row height, if appropriate.
if(mProfile->mAutoSizeHeight)
{
// make sure it's big enough for both bitmap AND font...
mItemHeight = getMax((S32)mFont->getHeight(), (S32)mProfile->mBitmapArrayRects[0].extent.y);
}
return true;
}
void GuiTreeViewCtrl::onSleep()
{
Parent::onSleep();
// If appropriate, blast the tree. (We probably rebuild it on wake.)
if( mDestroyOnSleep )
destroyTree();
}
bool GuiTreeViewCtrl::buildIconTable(const char * icons)
{
// Icons should be designated by the bitmap/png file names (minus the file extensions)
// and separated by colons (:). This list should be synchronized with the Icons enum.
// This is an abominal piece of code. -- BJG
if (!icons)
{
icons = "common/ui/default:"
"common/ui/simgroup:"
"common/ui/simgroup_closed:"
"common/ui/simgroup_selected:"
"common/ui/simgroup_selected_closed:"
"common/ui/audio:"
"common/ui/camera:"
"common/ui/fxfoliage:"
"common/ui/fxlight:"
"common/ui/fxshapereplicator:"
"common/ui/fxsunlight:"
"common/ui/hidden:"
"common/ui/interior:"
"common/ui/lightning:"
"common/ui/common/ui/shll_icon_passworded_hi:"
"common/ui/shll_icon_passworded:"
"common/ui/mission_area:"
"common/ui/particle:"
"common/ui/path:"
"common/ui/pathmarker:"
"common/ui/physical_area:"
"common/ui/precipitation:"
"common/ui/shape:"
"common/ui/sky:"
"common/ui/static_shape:"
"common/ui/sun:"
"common/ui/terrain:"
"common/ui/trigger:"
"common/ui/water:"
"common/ui/default";
}
// Figure the size of the buffer we need...
const char* temp = dStrchr( icons, '\t' );
U32 textLen = temp ? ( temp - icons ) : dStrlen( icons );
// Allocate temporary space.
FrameAllocatorMarker txtBuff;
char* drawText = (char*)txtBuff.alloc(sizeof(char) * (textLen + 4));
dStrncpy( drawText, icons, textLen );
drawText[textLen] = '\0';
U32 numIcons = 0;
char *buf = (char*)txtBuff.alloc(sizeof(char) * 256);
char* token = dStrtok( drawText, ":" );
// Count the number of icons and store them.
while (token && numIcons < MaxIcons)
{
dSprintf( buf, sizeof( char ) * 256, "%s", token );
mIconTable[numIcons] = TextureHandle( buf, BitmapKeepTexture );
token = dStrtok( NULL, ":" );
numIcons++;
}
return true;
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::onPreRender()
{
Parent::onPreRender();
S32 nRootItemId = getFirstRootItem();
if( nRootItemId == 0 )
return;
Item *pRootItem = getItem( nRootItemId );
if( pRootItem == NULL )
return;
mTicksPassed++;
if( mTicksPassed > mTreeRefreshInterval )
{
// Update every render in case new objects are added
buildVisibleTree();
mTicksPassed = 0;
}
}
//------------------------------------------------------------------------------
bool GuiTreeViewCtrl::hitTest(const Point2I & pnt, Item* & item, BitSet32 & flags)
{
// Initialize some things.
const Point2I pos = globalToLocalCoord(pnt);
flags.clear();
item = 0;
// get the hit cell
Point2I cell((pos.x < 0 ? -1 : pos.x / mCellSize.x),
(pos.y < 0 ? -1 : pos.y / mCellSize.y));
// valid?
if((cell.x < 0 || cell.x >= mSize.x) ||
(cell.y < 0 || cell.y >= mSize.y))
return false;
flags.set(OnRow);
// Grab the cell.
if (cell.y >= mVisibleItems.size())
return false; //Invalid cell, so don't do anything
item = mVisibleItems[cell.y];
S32 min = mTabSize * item->mTabLevel;
// left of icon/text?
if(pos.x < min)
{
flags.set(OnIndent);
return true;
}
// check image
S32 image = BmpChild;
if(item->isInspectorData())
image = item->isExpanded() ? BmpExp : BmpCon;
else
image = item->isExpanded() ? item->getExpandedImage() : item->getNormalImage();
if((image >= 0) && (image < mProfile->mBitmapArrayRects.size()))
min += mProfile->mBitmapArrayRects[image].extent.x;
// Is it on the image?
if(pos.x < min)
{
flags.set(OnImage);
return(true);
}
// Bump over to the start of the text.
min += mTextOffset;
// Check against the text.
FrameAllocatorMarker txtAlloc;
U32 bufLen = item->getDisplayTextLength();
char *buf = (char*)txtAlloc.alloc(bufLen);
item->getDisplayText(bufLen, buf);
min += mProfile->mFont->getStrWidth(buf);
if(pos.x < min)
flags.set(OnText);
return true;
}
void GuiTreeViewCtrl::setInstantGroup(SimObject * obj)
{
// make sure we're talking about a group.
SimGroup * grp = dynamic_cast<SimGroup*>(obj);
// Set the instant group variable.
if(grp)
{
Con::setVariable("instantGroup", grp->getIdString());
// Notify Script
Con::executef(this,2,"onInstantGroupSelected",Con::getIntArg(grp->getId()));
}
}
void GuiTreeViewCtrl::syncSelection()
{
// for each visible item check to see if it is on the mSelected list.
// if it is then make sure that it is on the mSelectedItems list as well.
for (S32 i = 0; i < mVisibleItems.size(); i++)
{
for (S32 j = 0; j < mSelected.size(); j++)
{
if (mVisibleItems[i]->mId == mSelected[j])
{
// check to see if it is on the visible items list.
bool addToSelectedItems = true;
for (S32 k = 0; k < mSelectedItems.size(); k++)
{
if (mSelected[j] == mSelectedItems[k]->mId)
{
// don't add it
addToSelectedItems = false;
}
}
if (addToSelectedItems)
{
mVisibleItems[i]->mState.set(Item::Selected, true);
mSelectedItems.push_front(mVisibleItems[i]);
break;
}
}
else if (mVisibleItems[i]->isInspectorData())
{
if (mVisibleItems[i]->getObject() && mVisibleItems[i]->getObject()->getId() == mSelected[j])
{
// check to see if it is on the visible items list.
bool addToSelectedItems = true;
for (S32 k = 0; k < mSelectedItems.size(); k++)
{
if (mSelectedItems[k]->isInspectorData())
{
if (mSelected[j] == mSelectedItems[k]->getObject()->getId())
{
// don't add it
addToSelectedItems = false;
}
}
else
{
if (mSelected[j] == mSelectedItems[k]->mId)
{
// don't add it
addToSelectedItems = false;
}
}
}
if (addToSelectedItems)
{
mVisibleItems[i]->mState.set(Item::Selected, true);
mSelectedItems.push_front(mVisibleItems[i]);
break;
}
}
}
}
}
}
void GuiTreeViewCtrl::removeSelection(S32 itemId)
{
if (mDebug)
Con::printf("removeSelection called");
Item * item = getItem(itemId);
// the item may have been selected at one point but was never created/visible in the tree
// so remove it.
for (S32 j = 0; j <mSelected.size(); j++)
{
if (item)
{
if (item->isInspectorData())
{
if (item->getObject() && item->getObject()->getId() == mSelected[j])
{
mSelected.erase(j);
break;
}
}
}
if (mSelected[j] == itemId)
{
mSelected.erase(j);
break;
}
}
if(!item)
{
// maybe what we were passed wasn't an item id but an object id.
for (S32 i = 0; i <mItems.size(); i++)
{
if (mItems[i] != 0)
{
if (mItems[i]->isInspectorData())
{
if (mItems[i]->getObject()->getId() == itemId)
{
item = mItems[i];
break;
}
}
}
}
if (!item)
{
//Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::removeSelection: invalid item id! Perhaps it isn't visible yet");
return;
}
}
item->mState.set(Item::Selected, false);
for (S32 i = 0; i < mSelectedItems.size(); i++)
{
if (mSelectedItems[i] == item)
{
mSelectedItems.erase(i);
break;
}
}
// Callback - onRemoveSelection( %itemID )
char buf[16];
dSprintf(buf, 16, "%d", item->mId);
Con::executef(this, 2, "onRemoveSelection", buf);
}
void GuiTreeViewCtrl::addSelection(S32 itemId)
{
if (mDebug)
Con::printf("addSelection called");
Item * item = getItem(itemId);
if(!item)
{
// maybe what we were passed wasn't an item id but an object id.
for (S32 i = 0; i <mItems.size(); i++)
{
if (mItems[i] != 0)
{
if (mItems[i]->isInspectorData())
{
if ( mItems[i]->getObject() && mItems[i]->getObject()->getId() == itemId)
{
item = mItems[i];
//looks like it is. check to see if it is on the list
bool alreadySelected = false;
Vector<Item *>::iterator i;
for(i = mSelectedItems.begin(); i != mSelectedItems.end(); i++)
{
if (*(i) == item)
{
//already a selected item which means this call should be ignored
alreadySelected = true;
return;
}
}
break;
}
}
}
}
if (!item)
{
// Do we want to allow more than one selected item?
if( !mMultipleSelections )
clearSelection();
//Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::addSelection: invalid item id! Perhaps it isn't visible yet.");
mSelected.push_front(itemId);
return;
}
}
else
{
// Do we want to allow more than one selected item?
if( !mMultipleSelections )
clearSelection();
// regardless of whether we found an item, we keep track of the Id that was passed
// as the item may simply not have been created/visible yet.
mSelected.push_front(itemId);
}
item->mState.set(Item::Selected, true);
// Also make it so we can see it if we didn't already.
scrollVisible(item);
// Do we want to allow more than one selected item?
//if( !mMultipleSelections )
// clearSelection();
mSelectedItems.push_front(item);
// Callback - onAddSelection( %itemID )
char buf[16];
dSprintf(buf, 16, "%d", item->mId);
Con::executef(this, 2, "onAddSelection", buf);
}
void GuiTreeViewCtrl::onItemSelected( Item *item )
{
char buf[16];
dSprintf(buf, 16, "%d", item->mId);
if (item->isInspectorData())
{
if(item->getObject())
Con::executef(this, 2, "onSelect", Con::getIntArg(item->getObject()->getId()));
if (!(item->isParent()) && item->getObject())
Con::executef(this, 2, "onInspect", Con::getIntArg(item->getObject()->getId()));
}
else
{
Con::executef(this, 2, "onSelect", buf);
if (!(item->isParent()))
Con::executef(this, 2, "onInspect", buf);
}
mSelectedItem = item->getID();
}
bool GuiTreeViewCtrl::setItemSelected(S32 itemId, bool select)
{
Item * item = getItem(itemId);
if (select)
{
if (mDebug) Con::printf("setItemSelected called true");
mSelected.push_front(itemId);
}
else
{
if (mDebug) Con::printf("setItemSelected called false");
// remove it from the mSelected list
for (S32 j = 0; j <mSelected.size(); j++)
{
if (item)
{
if (item->isInspectorData())
{
if (item->getObject())
{
if(item->getObject()->getId() == mSelected[j])
{
mSelected.erase(j);
break;
}
}
else
{
// Zombie, kill it!
mSelected.erase(j);
j--;
}
}
}
if (mSelected[j] == itemId)
{
mSelected.erase(j);
break;
}
}
}
if(!item)
{
// maybe what we were passed wasn't an item id but an object id.
for (S32 i = 0; i <mItems.size(); i++)
{
if (mItems[i] != 0)
{
if (mItems[i]->isInspectorData())
{
if (mItems[i]->getObject())
{
if(mItems[i]->getObject()->getId() == itemId)
{
item = mItems[i];
break;
}
}
else
{
// It's a zombie, blast it.
mItems.erase(i);
i--;
}
}
}
}
if (!item)
{
//Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemSelected: invalid item id! Perhaps it isn't visible yet.");
return(false);
}
}
if(select)
{
addSelection( item->mId );
onItemSelected( item );
}
else
{
// unselect the item, if it's present.
item->mState.set(Item::Selected, false);
if (item->isInspectorData() && item->getObject())
Con::executef(this, 2, "onUnSelect", Con::getIntArg(item->getObject()->getId()));
else
Con::executef(this, 2, "onUnSelect", Con::getIntArg(item->mId));
// remove it from the selected items list
for (S32 i = 0; i < mSelectedItems.size(); i++)
{
if (mSelectedItems[i] == item)
{
mSelectedItems.erase(i);
break;
}
}
}
setUpdate();
return(true);
}
// Given an item's index in the selection list, return its itemId
S32 GuiTreeViewCtrl::getSelectedItem(S32 index)
{
if(index >= 0 && index < getSelectedItemsCount())
{
return mSelectedItems[index]->mId;
}
return -1;
}
bool GuiTreeViewCtrl::setItemExpanded(S32 itemId, bool expand)
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemExpanded: invalid item id!");
return(false);
}
if(item->isExpanded() == expand)
return(true);
// expand parents
if(expand)
{
while(item)
{
if(item->mState.test(Item::VirtualParent))
onVirtualParentExpand(item);
item->setExpanded(true);
item = item->mParent;
}
}
else
{
if(item->mState.test(Item::VirtualParent))
onVirtualParentCollapse(item);
item->setExpanded(false);
}
return(true);
}
bool GuiTreeViewCtrl::setItemValue(S32 itemId, StringTableEntry Value)
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::setItemValue: invalid item id!");
return(false);
}
item->setValue( ( Value ) ? Value : "" );
return(true);
}
const char * GuiTreeViewCtrl::getItemText(S32 itemId)
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getItemText: invalid item id!");
return("");
}
return(item->getText() ? item->getText() : "");
}
const char * GuiTreeViewCtrl::getItemValue(S32 itemId)
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getItemValue: invalid item id!");
return("");
}
if(item->mState.test(Item::InspectorData))
{
// If it's InspectorData, we let people use this call to get an object reference.
return item->mInspectorInfo.mObject->getIdString();
}
else
{
// Just return the script value...
return(item->getValue() ? item->getValue() : "");
}
}
bool GuiTreeViewCtrl::editItem( S32 itemId, const char* newText, const char* newValue )
{
Item* item = getItem( itemId );
if ( !item )
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::editItem: invalid item id!");
return false;
}
if ( item->mState.test(Item::InspectorData) )
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::editItem: item %d is inspector data and may not be modified!", itemId);
return false;
}
delete [] item->getText();
item->setText (new char[dStrlen( newText ) + 1]);
dStrcpy( item->getText(), newText );
delete [] item->getValue();
item->setValue( new char[dStrlen( newValue ) + 1] );
dStrcpy( item->getValue(), newValue );
// Update the widths and such:
buildVisibleTree();
return true;
}
void GuiTreeViewCtrl::deleteSelection()
{
Con::executef(this, 1, "onDeleteSelection");
if (mSelectedItems.empty())
{
for (S32 i = 0; i < mSelected.size(); i++)
{
S32 objectId = mSelected[i];
// find the object
SimObject* obj = Sim::findObject(objectId);
obj->deleteObject();
}
}
else
{
Vector<Item*> delSelection;
delSelection = mSelectedItems;
mSelectedItems.clear();
while (!delSelection.empty())
{
Item * item = delSelection.front();
setItemSelected(item->mId,false);
if ( item->mParent )
deleteItem( item );
delSelection.pop_front();
}
}
mSelected.clear();
mSelectedItems.clear();
Con::executef( this, 1, "onObjectDeleteCompleted");
}
//------------------------------------------------------------------------------
// keyboard movement of items is restricted to just one item at a time
// if more than one item is selected then movement operations are not performed
bool GuiTreeViewCtrl::onKeyDown( const GuiEvent& event )
{
if ( !mVisible || !mActive || !mAwake )
return true;
// All the keyboard functionality requires a selected item, so if none exists...
// Deal with enter and delete
if ( event.modifier == 0 )
{
if ( event.keyCode == KEY_RETURN )
{
if ( mAltConsoleCommand[0] )
Con::evaluate( mAltConsoleCommand );
return true;
}
if ( event.keyCode == KEY_DELETE && mDeleteObjectAllowed )
{
// Don't delete the root!
if (mSelectedItems.empty())
return true;
//this may be fighting with the world editor delete
deleteSelection();
return true;
}
//call a generic bit of script that will let the subclass know that a key was pressed
Con::executef(this, 3, "onKeyDown", Con::getIntArg(event.modifier), Con::getIntArg(event.keyCode));
}
// only do operations if only one item is selected
if ( mSelectedItems.empty() || (mSelectedItems.size() > 1))
return true;
Item* item = mSelectedItems.first();
if ( !item )
return true;
// The Alt key lets you move items around!
if ( mFlags.test(IsEditable) && event.modifier & SI_ALT )
{
switch ( event.keyCode )
{
case KEY_UP:
// Move us up.
if ( item->mPrevious )
{
moveItemUp( item->mId );
scrollVisible(item);
}
return true;
case KEY_DOWN:
// Move the item under us up.
if ( item->mNext )
{
moveItemUp( item->mNext->mId );
scrollVisible(item);
}
return true;
case KEY_LEFT:
if ( item->mParent )
{
if ( item->mParent->mParent )
{
// Ok, we have both an immediate parent, and a grandparent.
// The goal of left-arrow alt is to become the child of our
// grandparent, ie, to become a sibling of our parent.
// First, unlink item from its siblings.
if ( item->mPrevious )
item->mPrevious->mNext = item->mNext;
else
item->mParent->mChild = item->mNext;
if ( item->mNext )
item->mNext->mPrevious = item->mPrevious;
// Now, relink as the next sibling of our parent.
item->mPrevious = item->mParent;
item->mNext = item->mParent->mNext;
// If there was already a next sibling, deal with that case.
if ( item->mNext )
item->mNext->mPrevious = item;
item->mParent->mNext = item;
// Snag the current parent set if any...
SimSet *parentSet = NULL;
if(item->mParent->isInspectorData())
parentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
else
{
// parent is probably script data so we search up the tree for a
// set to put our object in
Item * temp = item->mParent;
while (!temp->isInspectorData())
temp = temp->mParent;
// found a ancestor who is an inspectorData
if (temp->isInspectorData())
parentSet = dynamic_cast<SimSet*>(temp->getObject());
else parentSet = NULL;
}
// Get our active SimObject if any
SimObject *simObj = NULL;
if(item->isInspectorData())
simObj = item->getObject();
// Remove from the old parentset...
if(simObj && parentSet) {
if (parentSet->size()>0)
{
SimObject *lastObject = parentSet->last();
parentSet->removeObject(simObj);
parentSet->reOrder(lastObject);
} else
parentSet->removeObject(simObj);
}
// And finally, update our item
item->mParent = item->mParent->mParent;
// Snag the newparent set if any...
SimSet *newParentSet = NULL;
if(item->mParent->isInspectorData())
newParentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
else
{
// parent is probably script data so we search up the tree for a
// set to put our object in
Item * temp = item->mParent;
while (!temp->isInspectorData())
temp = temp->mParent;
// found a ancestor who is an inspectorData
if (temp->isInspectorData())
newParentSet = dynamic_cast<SimSet*>(temp->getObject());
else newParentSet = NULL;
}
if(simObj && newParentSet)
{
newParentSet->addObject(simObj);
Item * temp = item->mNext;
// item->mNext may be script, so find an inspector item to reorder with if any
if (temp) {
do {
if (temp->isInspectorData())
break;
temp = temp->mNext;
} while (temp);
if (temp && item->getObject() && temp->getObject()) //do we still have a item->mNext? If not then don't bother reordering
newParentSet->reOrder(item->getObject(), temp->getObject());
}
} else if (!simObj&&newParentSet) {
// our current item is script data. but it may have children who
// is inspector data who need an updated set
if (item->mChild)
inspectorSearch(item->mChild, item, parentSet, newParentSet);
}
// And update everything hurrah.
buildVisibleTree();
scrollVisible(item);
}
}
return true;
case KEY_RIGHT:
if ( item->mPrevious )
{
// Make the item the last child of its previous sibling.
// First, unlink from the current position in the list
item->mPrevious->mNext = item->mNext;
if ( item->mNext )
item->mNext->mPrevious = item->mPrevious;
// Get the object we're poking with.
SimObject *simObj = NULL;
SimSet *parentSet = NULL;
if(item->isInspectorData())
simObj = item->getObject();
if(item->mParent->isInspectorData())
parentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
else {
// parent is probably script data so we search up the tree for a
// set to put our object in
Item * temp = item->mParent;
while (!temp->isInspectorData())
temp = temp->mParent;
// found an ancestor who is an inspectorData
if (temp->isInspectorData())
parentSet = dynamic_cast<SimSet*>(temp->getObject());
}
// If appropriate, remove from the current SimSet.
if(parentSet && simObj) {
if (parentSet->size()>0)
{
SimObject *lastObject = parentSet->last();
parentSet->removeObject(simObj);
parentSet->reOrder(lastObject);
} else
parentSet->removeObject(simObj);
}
// Now, make our previous sibling our parent...
item->mParent = item->mPrevious;
item->mNext = NULL;
// And sink us down to the end of its siblings, if appropriate.
if ( item->mParent->mChild )
{
Item* temp = item->mParent->mChild;
while ( temp->mNext )
temp = temp->mNext;
temp->mNext = item;
item->mPrevious = temp;
}
else
{
// only child...<sniff>
item->mParent->mChild = item;
item->mPrevious = NULL;
}
// Make sure the new parent is expanded:
if ( !item->mParent->mState.test( Item::Expanded ) )
setItemExpanded( item->mParent->mId, true );
// Snag the new parent simset if any.
SimSet *newParentSet = NULL;
// new parent might be script. so figure out what set we need to add it to.
if(item->mParent->isInspectorData())
newParentSet = dynamic_cast<SimSet*>(item->mParent->getObject());
else
{
// parent is probably script data so we search up the tree for a
// set to put our object in
if (mDebug) Con::printf("oh nos my parent is script!");
Item * temp = item->mParent;
while (!temp->isInspectorData())
temp = temp->mParent;
// found a ancestor who is an inspectorData
if (temp->isInspectorData())
newParentSet = dynamic_cast<SimSet*>(temp->getObject());
else newParentSet = NULL;
}
// Add the item's SimObject to the new parent simset, at the end.
if(newParentSet && simObj)
newParentSet->addObject(simObj);
else if (!simObj&&newParentSet&&parentSet) {
// our current item is script data. but it may have children who
// is inspector data who need an updated set
if (item->mChild) {
inspectorSearch(item->mChild, item, parentSet, newParentSet);
}
}
scrollVisible(item);
}
return true;
}
}
// Explorer-esque navigation...
switch( event.keyCode )
{
case KEY_UP:
// Select previous visible item:
if ( item->mPrevious )
{
item = item->mPrevious;
while ( item->isParent() && item->isExpanded() )
{
item = item->mChild;
while ( item->mNext )
item = item->mNext;
}
setItemSelected(mSelectedItems.first()->mId,false);
setItemSelected( item->mId, true );
scrollVisible(item);
return true;
}
// or select parent:
if ( item->mParent )
{
setItemSelected(mSelectedItems.first()->mId,false);
setItemSelected( item->mParent->mId, true );
scrollVisible(item->mParent);
return true;
}
return false;
break;
case KEY_DOWN:
// Selected child if it is visible:
if ( item->isParent() && item->isExpanded() )
{
setItemSelected(mSelectedItems.first()->mId,false);
setItemSelected( item->mChild->mId, true );
scrollVisible(item->mChild);
return true;
}
// or select next sibling (recursively):
do
{
if ( item->mNext )
{
setItemSelected(mSelectedItems.first()->mId,false);
setItemSelected(item->mNext->mId, true );
scrollVisible(item->mNext);
return true;
}
item = item->mParent;
} while ( item );
return false;
break;
case KEY_LEFT:
// Contract current menu:
if ( item->isExpanded() )
{
setItemExpanded( item->mId, false );
scrollVisible(item);
return true;
}
// or select parent:
if ( item->mParent )
{
setItemSelected(mSelectedItems.first()->mId,false);
setItemSelected( item->mParent->mId, true );
scrollVisible(item->mParent);
return true;
}
return false;
break;
case KEY_RIGHT:
// Expand selected item:
if ( item->isParent() )
{
if ( !item->isExpanded() )
{
setItemExpanded( item->mId, true );
scrollVisible(item);
return true;
}
// or select child:
setItemSelected(mSelectedItems.first()->mId,false);
setItemSelected( item->mChild->mId, true );
scrollVisible(item->mChild);
return true;
}
return false;
break;
}
// Not processed, so pass the event on:
return Parent::onKeyDown( event );
}
//------------------------------------------------------------------------------
// on mouse up look at the current item and check to see if it is valid
// to move the selected item(s) to it.
void GuiTreeViewCtrl::onMouseUp(const GuiEvent &event)
{
if( !mActive || !mAwake || !mVisible )
return;
mouseUnlock();
if ( mSelectedItems.empty())
{
mDragMidPoint = NomDragMidPoint;
return;
}
if (!mMouseDragged)
return;
else
mMouseDragged = false;
Item* newItem = NULL;
Item* newItem2 = NULL;
if (mFlags.test(IsEditable))
{
Parent::onMouseMove( event );
if (mOldDragY != mMouseOverCell.y)
{
mOldDragY = mMouseOverCell.y;
BitSet32 hitFlags = 0;
if ( !hitTest( event.mousePoint, newItem2, hitFlags ) )
{
mDragMidPoint = NomDragMidPoint;
return;
}
newItem2->mState.clear(Item::MouseOverBmp | Item::MouseOverText );
// if the newItem isn't in the mSelectedItemList then continue.
Vector<Item *>::iterator k;
for(k = mSelectedItems.begin(); k != mSelectedItems.end(); k++)
{
newItem = newItem2;
if (*(k) == newItem)
{
mDragMidPoint = NomDragMidPoint;
return;
}
Item * temp = *(k);
Item * grandpaTemp = newItem->mParent;
// grandpa check, kick out if an item would be its own ancestor
while (grandpaTemp)
{
if (temp == grandpaTemp)
{
if (mDebug)
{
Con::printf("grandpa check");
if (temp->isInspectorData())
Con::printf("temp's name: %s",temp->getObject()->getName());
if (grandpaTemp->isInspectorData())
Con::printf("grandpa's name: %s",grandpaTemp->getObject()->getName());
}
mDragMidPoint = NomDragMidPoint;
return;
}
grandpaTemp = grandpaTemp->mParent;
}
}
for (S32 i = 0; i <mSelectedItems.size();i++)
{
newItem = newItem2;
Item * item = mSelectedItems[i];
char bufs[2][32];
if (mDebug) Con::printf("----------------------------");
// clear old highlighting of the item
item->mState.clear(Item::MouseOverBmp | Item::MouseOverText );
// move the selected item to the newItem
Item* oldParent = item->mParent;
// Snag the current parent set if any for future reference
SimSet *parentSet = NULL;
if(oldParent->isInspectorData())
parentSet = dynamic_cast<SimSet*>(oldParent->getObject());
else
{
// parent is probably script data so we search up the tree for a
// set to put our object in
Item * temp = oldParent;
while (temp)
{
if (temp->isInspectorData())
break;
temp = temp->mParent;
}
// found an ancestor who is an inspectorData
if (temp)
{
if (temp->isInspectorData())
parentSet = dynamic_cast<SimSet*>(temp->getObject());
}
}
// unlink from the current position in the list
unlinkItem(item);
// update the parent's children
// check if we an only child
if (item->mParent->mChild == item)
{
if (item->mNext)
item->mParent->mChild = item->mNext;
else
item->mParent->mChild = NULL;
}
if (mDragMidPoint != NomDragMidPoint)
{
//if it is below an expanded tree, place as last item in the tree
//if it is below a parent who isn't expanded put below it
// position the item above or below another item
if (mDragMidPoint == AbovemDragMidPoint)
{
// easier to treat everything as "Below the mDragMidPoint" so make some adjustments
if (mDebug) Con::printf("adding item above mDragMidPoint");
// above the mid point of an item, so grab either the parent
// or the previous sibling
// does the item have a previous sibling?
if (newItem->mPrevious)
{
newItem = newItem->mPrevious;
if (mDebug) Con::printf("treating as if below an item that isn't expanded");
// otherwise add below that item as a sibling
item->mParent = newItem->mParent;
item->mPrevious = newItem;
item->mNext = newItem->mNext;
if (newItem->mNext)
newItem->mNext->mPrevious = item;
newItem->mNext = item;
}
else
{
if (mDebug) Con::printf("treating as if adding below the parent of the item");
// instead we add as the first item below the newItem's parent
item->mParent = newItem->mParent;
item->mNext = newItem;
item->mPrevious = NULL;
newItem->mPrevious = item;
item->mParent->mChild = item;
}
}
else if (mDragMidPoint == BelowmDragMidPoint)
{
if ((newItem->isParent())&&(newItem->isExpanded()))
{
if (mDebug) Con::printf("adding item to an expanded parent below the mDragMidPoint");
item->mParent = newItem;
// then add the new item as a child
item->mNext = newItem->mChild;
if (newItem->mChild)
newItem->mChild->mPrevious = item;
item->mParent->mChild = item;
item->mPrevious = NULL;
}
else if ((!newItem->mNext)&&(newItem->mParent)&&(newItem->mParent->mParent))
{
// add below it's parent.
if (mDebug) Con::printf("adding below a tree");
item->mParent = newItem->mParent->mParent;
item->mNext = newItem->mParent->mNext;
item->mPrevious = newItem->mParent;
if (newItem->mParent->mNext)
newItem->mParent->mNext->mPrevious = item;
newItem->mParent->mNext = item;
}
else
{
// adding below item not as a child
if (mDebug) Con::printf("adding item below the mDragMidPoint of an item");
item->mParent = newItem->mParent;
// otherwise the item is a sibling
if (newItem->mNext)
newItem->mNext->mPrevious = item;
item->mNext = newItem->mNext;
item->mPrevious = newItem;
newItem->mNext = item;
}
}
}
// if we're not allowed to add to items, then try to add to the parent of the hit item.
// if we are, just add to the item we hit.
else
{
if (mDebug)
{
if (item->isInspectorData() && item->getObject())
Con::printf("Item: %i",item->getObject()->getId());
if (newItem->isInspectorData() && newItem->getObject())
Con::printf("Parent: %i",newItem->getObject()->getId());
Con::printf("dragged onto an item");
}
// if the hit item is not already a group, and we're not allowed to drag to items,
// then try to add to the parent.
if(!mDragToItemAllowed && !newItem->isParent())
{
// add to the item's parent.
if(!newItem->mParent || !newItem->mParent->isParent())
{
if(mDebug)
Con::printf("could not find the parent of that item. dragging to an item is not allowed, kicking out.");
mDragMidPoint = NomDragMidPoint;
return;
}
newItem = newItem->mParent;
}
// new parent is the item in the current cell
item->mParent = newItem;
// adjust children if any
if (newItem->mChild)
{
if (mDebug) Con::printf("not the first child");
// put it at the top of the list (easier to find if there are many children)
if (newItem->mChild)
newItem->mChild->mPrevious = item;
item->mNext = newItem->mChild;
newItem->mChild = item;
item->mPrevious = NULL;
}
else
{
if (mDebug) Con::printf("first child");
// only child
newItem->mChild = item;
item->mNext = NULL;
item->mPrevious = NULL;
}
}
// expand the item we added to, if it isn't expanded already
if ( !item->mParent->mState.test( Item::Expanded ) )
setItemExpanded( item->mParent->mId, true );
//----------------------------------------------------------------
// handle objects
// Get our active SimObject if any
SimObject *simObj = NULL;
if(item->isInspectorData())
{
simObj = item->getObject();
}
// Remove from the old parentset
if((simObj && parentSet)&&(oldParent != item->mParent))
{
if (mDebug) Con::printf("removing item from old parentset");
// hack to get around the way removeObject takes the last item of the set
// and moves it into the place of the object we removed
if (parentSet->size()>0)
{
SimObject *lastObject = parentSet->last();
parentSet->removeObject(simObj);
parentSet->reOrder(lastObject);
}
else
{
parentSet->removeObject(simObj);
}
}
// Snag the newparent set if any...
SimSet *newParentSet = NULL;
if(item->mParent->isInspectorData())
{
if (mDebug) Con::printf("getting a new parent set");
SimObject * tmpObj = item->mParent->getObject();
newParentSet = dynamic_cast<SimSet*>(tmpObj);
}
else
{
// parent is probably script data so we search up the tree for a
// set to put our object in
if (mDebug) Con::printf("oh nos my parent is script!");
Item * temp = item->mParent;
while (temp)
{
if (temp->isInspectorData())
break;
temp = temp->mParent;
}
// found a ancestor who is an inspectorData
if (temp)
{
if (temp->isInspectorData())
newParentSet = dynamic_cast<SimSet*>(temp->getObject());
}
else
{
newParentSet = NULL;
}
}
if(simObj && newParentSet)
{
if (mDebug) Con::printf("simobj and new ParentSet");
if (oldParent != item->mParent)
newParentSet->addObject(simObj);
//order the objects in the simset according to their
//order in the tree view control
if(!item->mNext)
{
if(!item->mPrevious) break;
//bring to the end of the set
SimObject *prevObject = item->mPrevious->getObject();
if (prevObject && item->getObject())
{
newParentSet->reOrder(item->getObject(), prevObject);
}
}
else
{
//reorder within the set
SimObject *nextObject = item->mNext->getObject();
if(nextObject && item->getObject())
{
newParentSet->reOrder(item->getObject(), nextObject);
}
}
}
else if (!simObj&&newParentSet)
{
// our current item is script data. but it may have children who
// is inspector data who need an updated set
if (mDebug) Con::printf("no simobj but new parentSet");
if (item->mChild)
inspectorSearch(item->mChild, item, parentSet, newParentSet);
}
else if (simObj&&!newParentSet)
{
if (mDebug) Con::printf("simobject and no new parent set");
}
else
if (mDebug) Con::printf("no simobject and no new parent set");
}
// And update everything.
scrollVisible(newItem);
}
}
mDragMidPoint = NomDragMidPoint;
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::onMouseDragged(const GuiEvent &event)
{
if(!mSupportMouseDragging) return;
if( !mActive || !mAwake || !mVisible )
return;
if (mSelectedItems.size() == 0)
return;
Point2I pt = globalToLocalCoord(event.mousePoint);
Parent::onMouseMove(event);
mouseLock();
mMouseDragged = true;
// whats our mDragMidPoint?
mCurrentDragCell = mMouseOverCell.y;
S32 midpCell = mCurrentDragCell * mItemHeight + (mItemHeight/2);
S32 currentY = pt.y;
S32 yDiff = currentY-midpCell;
S32 variance = (mItemHeight/5);
if (mPreviousDragCell >= 0)
mVisibleItems[mPreviousDragCell]->mState.clear( Item::MouseOverBmp | Item::MouseOverText );
if (mAbs(yDiff) > variance)
{
//above or below an item?
if (yDiff < 0)
mDragMidPoint = AbovemDragMidPoint;
else
mDragMidPoint = BelowmDragMidPoint;
} else
{
mDragMidPoint = NomDragMidPoint;
// highlight the current item
// hittest to detect whether we are on an item
// ganked from onMouseMouse
// used for tracking what our last cell was so we can clear it.
mPreviousDragCell = mCurrentDragCell;
if (mCurrentDragCell >= 0)
{
Item* item = NULL;
BitSet32 hitFlags = 0;
if ( !hitTest( event.mousePoint, item, hitFlags ) )
return;
if ( hitFlags.test( OnImage ) )
item->mState.set( Item::MouseOverBmp );
if ( hitFlags.test( OnText ))
item->mState.set( Item::MouseOverText );
// Always redraw the entire mouse over item, since we are distinguishing
// between the bitmap and the text:
setUpdateRegion( Point2I( mMouseOverCell.x * mCellSize.x, mMouseOverCell.y * mCellSize.y ), mCellSize );
}
}
}
void GuiTreeViewCtrl::onMiddleMouseDown(const GuiEvent & event)
{
//for debugging items
if (mDebug) {
Item* item;
BitSet32 hitFlags = 0;
hitTest( event.mousePoint, item, hitFlags );
Con::printf("debugging %d", item->mId);
Point2I pt = globalToLocalCoord(event.mousePoint);
if (item->isInspectorData() && item->getObject()) {
Con::printf("object data:");
Con::printf("name:%s",item->getObject()->getName());
Con::printf("className:%s",item->getObject()->getClassName());
}
Con::printf("contents of mSelectedItems:");
for(S32 i = 0; i < mSelectedItems.size(); i++) {
if (mSelectedItems[i]->isInspectorData()) {
Con::printf("%d",mSelectedItems[i]->getObject()->getId());
} else
Con::printf("wtf %d", mSelectedItems[i]);
}
Con::printf("contents of mSelected");
for (S32 j = 0; j < mSelected.size(); j++) {
Con::printf("%d", mSelected[j]);
}
S32 mCurrentDragCell = mMouseOverCell.y;
S32 midpCell = (mCurrentDragCell) * mItemHeight + (mItemHeight/2);
S32 currentY = pt.y;
S32 yDiff = currentY-midpCell;
S32 variance = (mItemHeight/5);
Con::printf("cell info: (%d,%d) mCurrentDragCell=%d est=(%d,%d,%d) ydiff=%d",pt.x,pt.y,mCurrentDragCell,mCurrentDragCell*mItemHeight, midpCell, (mCurrentDragCell+1)*mItemHeight,yDiff);
}
}
void GuiTreeViewCtrl::onMouseDown(const GuiEvent & event)
{
if( !mActive || !mAwake || !mVisible )
{
Parent::onMouseDown(event);
return;
}
if ( mProfile->mCanKeyFocus )
setFirstResponder();
Item * item = 0;
BitSet32 hitFlags;
mOldDragY = 0;
mDragMidPoint = NomDragMidPoint;
//
if(!hitTest(event.mousePoint, item, hitFlags))
return;
//
if(event.modifier & SI_CTRL)
{
bool selectFlag = item->mState.test(Item::Selected);
if (selectFlag == true)
{
// already selected, so unselect it and remove it
removeSelection(item->mId);
if (item->isInspectorData() && item->getObject())
Con::executef(this,2,"onRemoveSelection",Con::getIntArg(item->getObject()->getId()));
} else
{
// otherwise select it and add it to the list
// check if it is already on the list.
/*bool newSelection = true;
for (S32 i = 0; i < mSelectedItems.size(); i++) {
if (mSelectedItems[i] == item) {
newSelection = false;
}
}*/
//if (newSelection) {
addSelection(item->mId);
if (item->isInspectorData() && item->getObject())
Con::executef(this,2,"onAddSelection",Con::getIntArg(item->getObject()->getId()));
//}
}
}
else if (event.modifier & SI_SHIFT)
{
// is something already selected?
S32 firstSelectedIndex = 0;
Item * firstItem = NULL;
if (!mSelectedItems.empty())
{
firstItem = mSelectedItems.front();
for (S32 i = 0; i < mVisibleItems.size();i++)
{
if (mVisibleItems[i] == mSelectedItems.front())
{
firstSelectedIndex = i;
break;
}
}
S32 mCurrentDragCell = mMouseOverCell.y;
if (mVisibleItems[firstSelectedIndex] != firstItem )
{
/*
Con::printf("something isn't right...");
if (mVisibleItems[firstSelectedIndex]->isInspectorData())
Con::printf("visibleItem %s",mVisibleItems[firstSelectedIndex]->getObject()->getName());
if (firstItem->isInspectorData())
Con::printf("firstItem %s",firstItem->getObject()->getName());
*/
}
else
{
// select the cells
if ((mCurrentDragCell) < firstSelectedIndex)
{
//select up
for (S32 j = (mCurrentDragCell); j < firstSelectedIndex; j++) {
//if the item isn't already selected, then select it
bool newSelection = true;
Vector<Item *>::iterator k;
for(k = mSelectedItems.begin(); k != mSelectedItems.end(); k++)
{
if (mVisibleItems[j] == *(k)){
newSelection = false;
break;
}
}
if (newSelection)
{
addSelection(mVisibleItems[j]->mId);
if (mVisibleItems[j]->isInspectorData())
Con::executef(this,2,"onAddSelection",Con::getIntArg(mVisibleItems[j]->getObject()->getId()));
}
}
}
else
{
// select down
for (S32 j = firstSelectedIndex+1; j < (mCurrentDragCell+1); j++) {
bool newSelection = true;
Vector<Item *>::iterator k;
for(k = mSelectedItems.begin(); k != mSelectedItems.end(); k++) {
if (mVisibleItems[j] == *(k)){
newSelection = false;
break;
}
}
if (newSelection)
{
addSelection(mVisibleItems[j]->mId);
if (mVisibleItems[j]->isInspectorData())
Con::executef(this,2,"onAddSelection",Con::getIntArg(mVisibleItems[j]->getObject()->getId()));
}
}
}
}
}
}
else if (event.modifier & SI_ALT)
{
if (item->isInspectorData() && item->getObject())
{
setInstantGroup(item->getObject());
mInstantGroup = item->mId;
}
} else if (!hitFlags.test(OnImage))
{
// first check to see if the item is already selected
bool newSelection = true;
Vector<Item *>::iterator k;
for(k = mSelectedItems.begin(); k != mSelectedItems.end(); k++) {
if(*(k) == item)
{
newSelection = false;
break;
}
}
// if the item is not already selected then we have a
//newly selected item, so clear our list of selected items
if (newSelection)
{
clearSelection();
//Con::executef(this, 1, "onClearSelection");
//mSelectedItems.clear();
setItemSelected(item->mId,true);
}
}
if ( hitFlags.test( OnText ) && ( event.mouseClickCount > 1 ) && mAltConsoleCommand[0] )
Con::evaluate( mAltConsoleCommand );
if(!item->isParent())
return;
//
if ( mFullRowSelect || hitFlags.test( OnImage ) )
{
item->setExpanded(!item->isExpanded());
if( !item->isInspectorData() && item->mState.test(Item::VirtualParent) )
onVirtualParentExpand(item);
scrollVisible(item);
}
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::onMouseMove( const GuiEvent &event )
{
if ( mMouseOverCell.y >= 0 )
mVisibleItems[mMouseOverCell.y]->mState.clear( Item::MouseOverBmp | Item::MouseOverText );
Parent::onMouseMove( event );
if ( mMouseOverCell.y >= 0 )
{
Item* item = NULL;
BitSet32 hitFlags = 0;
if ( !hitTest( event.mousePoint, item, hitFlags ) )
return;
if ( hitFlags.test( OnImage ) )
item->mState.set( Item::MouseOverBmp );
if ( hitFlags.test( OnText ))
item->mState.set( Item::MouseOverText );
// Always redraw the entire mouse over item, since we are distinguishing
// between the bitmap and the text:
setUpdateRegion( Point2I( mMouseOverCell.x * mCellSize.x, mMouseOverCell.y * mCellSize.y ), mCellSize );
}
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::onMouseEnter( const GuiEvent &event )
{
Parent::onMouseEnter( event );
onMouseMove( event );
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::onMouseLeave( const GuiEvent &event )
{
if ( mMouseOverCell.y >= 0 )
mVisibleItems[mMouseOverCell.y]->mState.clear( Item::MouseOverBmp | Item::MouseOverText );
Parent::onMouseLeave( event );
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::onRightMouseDown(const GuiEvent & event)
{
if(!mActive)
{
Parent::onRightMouseDown(event);
return;
}
Item * item = NULL;
BitSet32 hitFlags;
//
if(!hitTest(event.mousePoint, item, hitFlags))
return;
//
char bufs[2][32];
dSprintf(bufs[0], 32, "%d", item->mId);
dSprintf(bufs[1], 32, "%d %d", event.mousePoint.x, event.mousePoint.y);
if (item->isInspectorData() && item->getObject())
Con::executef(this,4, "onRightMouseDown", bufs[0],bufs[1],Con::getIntArg(item->getObject()->getId()));
else
Con::executef(this, 3, "onRightMouseDown", bufs[0], bufs[1]);
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::onRender(Point2I offset, const RectI &updateRect)
{
// Get all our contents drawn!
Parent::onRender(offset,updateRect);
// Deal with drawing the drag & drop line, if any...
dglSetClipRect(updateRect);
// only do it if we have a mDragMidPoint
if (mDragMidPoint == NomDragMidPoint || !mSupportMouseDragging )
return;
ColorF greyLine(0.5,0.5,0.5,1);
Point2F squarePt;
glLineWidth(2.f);
// draw mDragMidPoint lines with a diamond
if (mDragMidPoint == AbovemDragMidPoint)
{
S32 tempY = mItemHeight*mCurrentDragCell+offset.y ;
squarePt.y = tempY;
squarePt.x = 125+offset.x;
dglDrawLine(0+offset.x, tempY, 250+offset.x, tempY,greyLine);
dglDraw2DSquare(squarePt, 6, 90 );
}
if (mDragMidPoint == BelowmDragMidPoint)
{
S32 tempY2 = mItemHeight*(mCurrentDragCell+1) +offset.y;
squarePt.y = tempY2;
squarePt.x = 125+offset.x;
dglDrawLine(0+offset.x, tempY2, 250+offset.x, tempY2,greyLine);
dglDraw2DSquare(squarePt,6, 90 );
}
glLineWidth(1.f);
}
void GuiTreeViewCtrl::onRenderCell(Point2I offset, Point2I cell, bool, bool )
{
if( !mVisibleItems.size() )
return;
// Do some sanity checking and data retrieval.
AssertFatal(cell.y < mVisibleItems.size(), "GuiTreeViewCtrl::onRenderCell: invalid cell");
Item * item = mVisibleItems[cell.y];
// If there's no object, deal with it.
if(item->isInspectorData())
if(!item->getObject())
return;
RectI drawRect( offset, mCellSize );
dglClearBitmapModulation();
FrameAllocatorMarker txtBuff;
// Ok, we have the item. There are a few possibilities at this point:
// - We need to draw inheritance lines and a treeview-chosen icon
// OR
// - We have to draw an item-dependent icon
// - If we're mouseover, we have to highlight it.
//
// - We have to draw the text for the item
// - Taking into account various mouseover states
// - Taking into account the value (set or not)
// - If it's an inspector data, we have to do some custom rendering
// Ok, first draw the tab and icon.
// Do we draw the tree lines?
if(mFlags.test(ShowTreeLines))
{
drawRect.point.x += ( mTabSize * item->mTabLevel );
Item* parent = item->mParent;
for ( S32 i = item->mTabLevel; ( parent && i > 0 ); i-- )
{
drawRect.point.x -= mTabSize;
if ( parent->mNext )
dglDrawBitmapSR( mProfile->mTextureHandle, drawRect.point, mProfile->mBitmapArrayRects[BmpLine] );
parent = parent->mParent;
}
}
// Now, the icon...
drawRect.point.x = offset.x + mTabSize * item->mTabLevel;
// First, draw the rollover glow, if it's an inner node.
if ( item->isParent() && item->mState.test( Item::MouseOverBmp ) )
dglDrawBitmapSR( mProfile->mTextureHandle, drawRect.point, mProfile->mBitmapArrayRects[BmpGlow] );
// Now, do we draw a treeview-selected item or an item dependent one?
S32 newOffset = 0; // This is stored so we can render glow, then update render pos.
if(item->isInspectorData())
{
S32 bitmap = 0;
// Ok, draw the treeview lines as appropriate.
if ( !item->isParent() )
{
bitmap = item->mNext ? BmpChild : BmpLastChild;
}
else
{
bitmap = item->isExpanded() ? BmpExp : BmpCon;
if ( item->mParent || item->mPrevious )
bitmap += ( item->mNext ? 3 : 2 );
else
bitmap += ( item->mNext ? 1 : 0 );
}
if ( ( bitmap >= 0 ) && ( bitmap < mProfile->mBitmapArrayRects.size() ) )
{
dglDrawBitmapSR( mProfile->mTextureHandle, drawRect.point, mProfile->mBitmapArrayRects[bitmap] );
newOffset = mProfile->mBitmapArrayRects[bitmap].extent.x;
}
// draw lock icon if need be
S32 icon = Lock1;
S32 icon2 = Hidden;
if (item->getObject() && item->getObject()->isLocked())
{
if (mIconTable[icon])
{
//drawRect.point.x = offset.x + mTabSize * item->mTabLevel + mIconTable[icon].getWidth();
drawRect.point.x += mIconTable[icon].getWidth();
dglDrawBitmap( mIconTable[icon], drawRect.point, 0 );
}
}
if (item->getObject() && item->getObject()->isHidden())
{
if (mIconTable[icon2])
{
//drawRect.point.x = offset.x + mTabSize * item->mTabLevel + mIconTable[icon].getWidth();
drawRect.point.x += mIconTable[icon2].getWidth();
dglDrawBitmap( mIconTable[icon2], drawRect.point, 0 );
}
}
SimObject * pObject = item->getObject();
SimGroup * pGroup = ( pObject == NULL ) ? NULL : dynamic_cast<SimGroup*>( pObject );
// draw the icon associated with the item
if (item->isParent())
{
if ( pGroup != NULL)
{
if (item->isExpanded())
item->mIcon = SimGroup1;
else
item->mIcon = SimGroup2;
}
else
item->mIcon = SimGroup2;
}
if (mInstantGroup == item->mId)
{
if (item->isInspectorData())
{
if ( pGroup != NULL )
{
if (item->isExpanded())
item->mIcon = SimGroup3;
else
item->mIcon = SimGroup4;
}
}
}
if (item->mIcon)
{
if (mIconTable[item->mIcon])
{
S32 iconHeight = (mItemHeight - mIconTable[item->mIcon].getHeight()) / 2;
S32 oldHeight = drawRect.point.y;
if(iconHeight > 0)
drawRect.point.y += iconHeight;
drawRect.point.x += mIconTable[item->mIcon].getWidth();
dglDrawBitmap( mIconTable[item->mIcon], drawRect.point, 0 );
drawRect.point.y = oldHeight;
}
}
}
else
{
S32 bitmap = 0;
// Ok, draw the treeview lines as appropriate.
if ( !item->isParent() )
bitmap = item->mNext ? BmpChild : BmpLastChild;
else
{
bitmap = item->isExpanded() ? BmpExp : BmpCon;
if ( item->mParent || item->mPrevious )
bitmap += ( item->mNext ? 3 : 2 );
else
bitmap += ( item->mNext ? 1 : 0 );
}
if ( ( bitmap >= 0 ) && ( bitmap < mProfile->mBitmapArrayRects.size() ) )
{
dglDrawBitmapSR( mProfile->mTextureHandle, drawRect.point, mProfile->mBitmapArrayRects[bitmap] );
newOffset = mProfile->mBitmapArrayRects[bitmap].extent.x;
}
S32 icon = item->isExpanded() ? item->mScriptInfo.mExpandedImage : item->mScriptInfo.mNormalImage;
if ( icon )
{
if (mIconTable[icon])
{
S32 iconHeight = (mItemHeight - mIconTable[icon].getHeight()) / 2;
S32 oldHeight = drawRect.point.y;
if(iconHeight > 0)
drawRect.point.y += iconHeight;
drawRect.point.x += mIconTable[icon].getWidth();
dglDrawBitmap( mIconTable[icon], drawRect.point, 0 );
drawRect.point.y = oldHeight;
}
}
}
// Ok, update offset so we can render some text!
drawRect.point.x += newOffset;
// Ok, now we're off to rendering the actual data for the treeview item.
U32 bufLen = item->mDataRenderWidth + 1;
char *displayText = (char *)txtBuff.alloc(bufLen);
displayText[bufLen-1] = 0;
item->getDisplayText(bufLen, displayText);
// Draw the rollover/selected bitmap, if one was specified.
drawRect.extent.x = mProfile->mFont->getStrWidth( displayText ) + ( 2 * mTextOffset );
if ( item->mState.test( Item::Selected ) && mTexSelected )
dglDrawBitmapStretch( mTexSelected, drawRect );
else if ( item->mState.test( Item::MouseOverText ) && mTexRollover )
dglDrawBitmapStretch( mTexRollover, drawRect );
// Offset a bit so as to space text properly.
drawRect.point.x += mTextOffset;
// Determine what color the font should be.
ColorI fontColor;
fontColor = item->mState.test( Item::Selected ) ? mProfile->mFontColorSEL :
( item->mState.test( Item::MouseOverText ) ? mProfile->mFontColorHL : mProfile->mFontColor );
if (item->mState.test(Item::Selected))
{
dglDrawRectFill(drawRect, mProfile->mFillColorHL);
}
else if (item->mState.test(Item::MouseOverText))
{
dglDrawRectFill(drawRect, mProfile->mFontColorNA);
}
if( mInstantGroup == item->mId)
{
fontColor = mProfile->mFontColorHL;
}
dglSetBitmapModulation( fontColor );
// Center the text horizontally.
S32 height = (mItemHeight - mProfile->mFont->getHeight()) / 2;
if(height > 0)
drawRect.point.y += height;
// JDD - offset by two pixels or so to keep the text from rendering RIGHT ONTOP of the outline
drawRect.point.x += 2;
dglDrawText( mProfile->mFont, drawRect.point, displayText, mProfile->mFontColors );
}
//------------------------------------------------------------------------------
void GuiTreeViewCtrl::clearSelection()
{
while (!mSelectedItems.empty())
{
if(!setItemSelected(mSelectedItems.last()->mId, false))
mSelectedItems.pop_back();
}
mSelectedItems.clear();
mSelected.clear();
Con::executef(this, 1, "onClearSelection");
}
void GuiTreeViewCtrl::lockSelection(bool lock)
{
for(U32 i = 0; i < mSelectedItems.size(); i++)
{
if(mSelectedItems[i]->isInspectorData())
mSelectedItems[i]->getObject()->setLocked(lock);
}
}
void GuiTreeViewCtrl::hideSelection(bool hide)
{
for(U32 i = 0; i < mSelectedItems.size(); i++)
{
if(mSelectedItems[i]->isInspectorData())
mSelectedItems[i]->getObject()->setHidden(hide);
}
}
//------------------------------------------------------------------------------
// handles icon assignments
S32 GuiTreeViewCtrl::getIcon(const char * iconString)
{
if( iconString == NULL )
return Default;
S32 icon = Default;
if (!dStrcmp(iconString, "Sky"))
icon = Sky;
else if (!dStrcmp(iconString, "Sun"))
icon = Sun;
else if (!dStrcmp(iconString, "Lightning"))
icon = Lightning;
else if (!dStrcmp(iconString, "Water"))
icon = Water;
else if (!dStrcmp(iconString, "WaterBlock"))
icon = Water;
else if (!dStrcmp(iconString, "Terrain"))
icon = Terrain;
else if (!dStrcmp(iconString, "TerrainBlock"))
icon = Terrain;
else if (!dStrcmp(iconString, "AudioEmitter"))
icon = Audio;
else if (!dStrcmp(iconString, "Precipitation"))
icon = Precipitation;
else if (!dStrcmp(iconString, "ParticleEmitter"))
icon = Particle;
else if (!dStrcmp(iconString, "ParticleEmitterNode"))
icon = Particle;
else if (!dStrcmp(iconString, "fxSunLight"))
icon = fxSunLight;
else if (!dStrcmp(iconString, "fxLight"))
icon = fxLight;
else if (!dStrcmp(iconString, "fxShapeReplicator"))
icon = fxShapeReplicator;
else if (!dStrcmp(iconString, "fxFoliageReplicator"))
icon = fxFoliageReplicator;
else if (!dStrcmp(iconString, "MissionArea"))
icon = MissionArea;
else if (!dStrcmp(iconString, "Path"))
icon = Path;
else if (!dStrcmp(iconString, "PathMarker"))
icon = Pathmarker;
else if (!dStrcmp(iconString, "Marker"))
icon = Pathmarker;
else if (!dStrcmp(iconString, "Trigger"))
icon = Trigger;
else if (!dStrcmp(iconString, "PhysicalZone"))
icon = PhysicalArea;
else if (!dStrcmp(iconString, "Camera"))
icon = Camera;
else if (!dStrcmp(iconString, "SimGroup"))
icon = SimGroup1;
else if (!dStrcmp(iconString, "Interior"))
icon = Interior;
else if (!dStrcmp(iconString, "InteriorInstance"))
icon = Interior;
else if (!dStrcmp(iconString, "TSStatic"))
icon = StaticShape;
else if (!dStrcmp(iconString, "Item"))
icon = Shape;
return icon;
}
void GuiTreeViewCtrl::addInspectorDataItem(Item *parent, SimObject *obj)
{
S32 icon = getIcon(obj->getClassName());
Item *item = createItem(icon);
item->mState.set(Item::InspectorData);
// Deal with child objects...
if(dynamic_cast<SimSet*>(obj))
item->mState.set(Item::VirtualParent);
// Actually store the data!
item->setObject(obj);
// Now add us to the data structure...
if(parent)
{
// Add as child of parent.
if(parent->mChild)
{
Item * traverse = parent->mChild;
while(traverse->mNext)
traverse = traverse->mNext;
traverse->mNext = item;
item->mPrevious = traverse;
}
else
parent->mChild = item;
item->mParent = parent;
}
else
{
// If no parent, add to root.
item->mNext = mRoot;
mRoot = item;
item->mParent = NULL;
}
if(!parent || parent->isExpanded())
mFlags.set(RebuildVisible);
buildVisibleTree();
}
void GuiTreeViewCtrl::unlinkItem(Item * item)
{
if (item->mPrevious)
item->mPrevious->mNext = item->mNext;
if (item->mNext)
item->mNext->mPrevious = item->mPrevious;
}
bool GuiTreeViewCtrl::childSearch(Item * item, SimObject *obj, bool yourBaby)
{
Item * temp = item->mChild;
while (temp)
{
//do you have my baby?
if (temp->isInspectorData())
{
if (temp->getObject() == obj)
yourBaby = false; //probably a child of an inner script
}
yourBaby = childSearch(temp,obj, yourBaby);
temp = temp->mNext;
}
return yourBaby;
}
void GuiTreeViewCtrl::inspectorSearch(Item * item, Item * parent, SimSet * parentSet, SimSet * newParentSet)
{
if (!parentSet||!newParentSet)
return;
if (item == parent->mNext)
return;
if (item)
{
if (item->isInspectorData())
{
// remove the object from the parentSet and add it to the newParentSet
SimObject* simObj = item->getObject();
if (parentSet->size())
{
SimObject *lastObject = parentSet->last();
parentSet->removeObject(simObj);
parentSet->reOrder(lastObject);
}
else
parentSet->removeObject(simObj);
newParentSet->addObject(simObj);
if (item->mNext)
{
inspectorSearch(item->mNext, parent, parentSet, newParentSet);
return;
}
else
{
// end of children so backing up
if (item->mParent == parent)
return;
else
{
inspectorSearch(item->mParent->mNext, parent, parentSet, newParentSet);
return;
}
}
}
if (item->mChild)
{
inspectorSearch(item->mChild, parent, parentSet, newParentSet);
return;
}
if (item->mNext)
{
inspectorSearch(item->mNext, parent, parentSet, newParentSet);
return;
}
}
}
bool GuiTreeViewCtrl::onVirtualParentBuild(Item *item, bool bForceFullUpdate)
{
if(!item->mState.test(Item::InspectorData))
return true;
// Blast an item if it doesn't have a corresponding SimObject...
if(item->mInspectorInfo.mObject == NULL)
{
removeItem(item->mId);
return false;
}
// Skip the next stuff unless we're expanded...
if(!item->isExpanded() && !bForceFullUpdate)
return true;
// Verify that we have all the kids we should in here...
SimSet *srcObj = dynamic_cast<SimSet*>(&(*item->mInspectorInfo.mObject));
// If it's not a SimSet... WTF are we doing here?
if(!srcObj)
return true;
SimSet::iterator i;
// This is slow but probably ok.
for(i = srcObj->begin(); i != srcObj->end(); i++)
{
SimObject *obj = *i;
// If we can't find it, add it.
// unless it has a parent that is a child that is a script
Item *res = item->findChildByValue(obj);
bool foundChild = true;
// search the children. if any of them are the parent of the object then don't add it.
foundChild = childSearch(item,obj,foundChild);
if(!res && foundChild)
{
if (mDebug) Con::printf("adding something");
addInspectorDataItem(item, obj);
}
}
return true;
}
bool GuiTreeViewCtrl::onVirtualParentExpand(Item *item)
{
// Do nothing...
return true;
}
bool GuiTreeViewCtrl::onVirtualParentCollapse(Item *item)
{
// Do nothing...
return true;
}
void GuiTreeViewCtrl::inspectObject(SimObject *obj, bool okToEdit)
{
destroyTree();
mFlags.set(IsEditable, okToEdit);
//build our icon table
const char * res = Con::executef(this, 1, "onDefineIcons");
if(!(dAtob(res)))
{
// if no icons were defined in script then use defaults.
buildIconTable(NULL);
}
addInspectorDataItem(NULL, obj);
}
S32 GuiTreeViewCtrl::findItemByName(const char *name)
{
for (S32 i = 0; i < mItems.size(); i++)
if (dStrcmp(mItems[i]->getText(),name) == 0)
return mItems[i]->mId;
return 0;
}
StringTableEntry GuiTreeViewCtrl::getTextToRoot( S32 itemId, const char * delimiter )
{
Item * item = getItem(itemId);
if(!item)
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getTextToRoot: invalid start item id!");
return StringTable->insert("");
}
if(item->isInspectorData())
{
Con::errorf(ConsoleLogEntry::General, "GuiTreeViewCtrl::getTextToRoot: cannot get text to root of inspector data items");
return StringTable->insert("");
}
char bufferOne[1024];
char bufferTwo[1024];
char bufferNodeText[128];
dMemset( bufferOne, 0, sizeof(bufferOne) );
dMemset( bufferTwo, 0, sizeof(bufferTwo) );
dStrcpy( bufferOne, item->getText() );
Item *prevNode = item->mParent;
while ( prevNode )
{
dMemset( bufferNodeText, 0, sizeof(bufferNodeText) );
dStrcpy( bufferNodeText, prevNode->getText() );
dSprintf( bufferTwo, 1024, "%s%s%s",bufferNodeText, delimiter, bufferOne );
dStrcpy( bufferOne, bufferTwo );
dMemset( bufferTwo, 0, sizeof(bufferTwo) );
prevNode = prevNode->mParent;
}
// Return the result, StringTable-ized.
return StringTable->insert( bufferOne );
}
//------------------------------------------------------------------------------
ConsoleMethod(GuiTreeViewCtrl, findItemByName, S32, 3, 3, "(find item by name and returns the mId)")
{
return(object->findItemByName(argv[2]));
}
ConsoleMethod(GuiTreeViewCtrl, insertItem, S32, 4, 8, "(TreeItemId parent, name, value, icon, normalImage=0, expandedImage=0)")
{
S32 norm=0, expand=0;
if (argc > 6)
{
norm = dAtoi(argv[6]);
if(argc > 7)
expand = dAtoi(argv[7]);
}
return(object->insertItem(dAtoi(argv[2]), argv[3], argv[4], argv[5], norm, expand));
}
ConsoleMethod(GuiTreeViewCtrl, lockSelection, void, 2, 3, "(locks selections)")
{
bool lock = true;
if(argc == 3)
lock = dAtob(argv[2]);
object->lockSelection(lock);
}
ConsoleMethod(GuiTreeViewCtrl, clearSelection, void, 2, 2, "(clears selection)")
{
object->clearSelection();
}
ConsoleMethod(GuiTreeViewCtrl, deleteSelection, void, 2, 2, "(deletes the selected items)")
{
object->deleteSelection();
}
ConsoleMethod(GuiTreeViewCtrl, addSelection, void, 3, 3, "(selects an item)")
{
S32 id = dAtoi(argv[2]);
object->addSelection(id);
}
ConsoleMethod(GuiTreeViewCtrl, addChildSelectionByValue, void, 4, 4, "addChildSelectionByValue(TreeItemId parent, value)")
{
S32 id = dAtoi(argv[2]);
GuiTreeViewCtrl::Item* parentItem = object->getItem(id);
GuiTreeViewCtrl::Item* child = parentItem->findChildByValue(argv[3]);
object->addSelection(child->getID());
}
ConsoleMethod(GuiTreeViewCtrl, removeSelection, void, 3, 3, "(deselects an item)")
{
S32 id = dAtoi(argv[2]);
object->removeSelection(id);
}
ConsoleMethod(GuiTreeViewCtrl, removeChildSelectionByValue, void, 4, 4, "removeChildSelectionByValue(TreeItemId parent, value)")
{
S32 id = dAtoi(argv[2]);
GuiTreeViewCtrl::Item* parentItem = object->getItem(id);
if(parentItem)
{
GuiTreeViewCtrl::Item* child = parentItem->findChildByValue(argv[3]);
if(child)
{
object->removeSelection(child->getID());
}
}
}
ConsoleMethod(GuiTreeViewCtrl, selectItem, bool, 3, 4, "(TreeItemId item, bool select=true)")
{
S32 id = dAtoi(argv[2]);
bool select = true;
if(argc == 4)
select = dAtob(argv[3]);
return(object->setItemSelected(id, select));
}
ConsoleMethod(GuiTreeViewCtrl, expandItem, bool, 3, 4, "(TreeItemId item, bool expand=true)")
{
S32 id = dAtoi(argv[2]);
bool expand = true;
if(argc == 4)
expand = dAtob(argv[3]);
return(object->setItemExpanded(id, expand));
}
// Make the given item visible.
ConsoleMethod(GuiTreeViewCtrl, scrollVisible, void, 3, 3, "(TreeItemId item)")
{
object->scrollVisible(dAtoi(argv[2]));
}
ConsoleMethod(GuiTreeViewCtrl, buildIconTable, bool, 3,3, "(builds an icon table)")
{
const char * icons = argv[2];
return object->buildIconTable(icons);
}
ConsoleMethod( GuiTreeViewCtrl, open, void, 3, 4, "(SimSet obj, bool okToEdit=true) Set the root of the tree view to the specified object, or to the root set.")
{
SimSet *treeRoot = NULL;
SimObject* target = Sim::findObject(argv[2]);
bool okToEdit = true;
if (argc == 4)
okToEdit = dAtob(argv[3]);
if (target)
treeRoot = dynamic_cast<SimSet*>(target);
if (! treeRoot)
Sim::findObject(RootGroupId, treeRoot);
object->inspectObject(treeRoot,okToEdit);
}
ConsoleMethod(GuiTreeViewCtrl, getItemText, const char *, 3, 3, "(TreeItemId item)")
{
return(object->getItemText(dAtoi(argv[2])));
}
ConsoleMethod(GuiTreeViewCtrl, getItemValue, const char *, 3, 3, "(TreeItemId item)")
{
return(object->getItemValue(dAtoi(argv[2])));
}
ConsoleMethod(GuiTreeViewCtrl, editItem, bool, 5, 5, "(TreeItemId item, string newText, string newValue)")
{
return(object->editItem(dAtoi(argv[2]), argv[3], argv[4]));
}
ConsoleMethod(GuiTreeViewCtrl, removeItem, bool, 3, 3, "(TreeItemId item)")
{
return(object->removeItem(dAtoi(argv[2])));
}
ConsoleMethod(GuiTreeViewCtrl, removeAllChildren, void, 3, 3, "removeAllChildren(TreeItemId parent)")
{
object->removeAllChildren(dAtoi(argv[2]));
}
ConsoleMethod(GuiTreeViewCtrl, clear, void, 2, 2, "() - empty tree")
{
object->removeItem(0);
}
ConsoleMethod(GuiTreeViewCtrl, getFirstRootItem, S32, 2, 2, "Get id for root item.")
{
return(object->getFirstRootItem());
}
ConsoleMethod(GuiTreeViewCtrl, getChild, S32, 3, 3, "(TreeItemId item)")
{
return(object->getChildItem(dAtoi(argv[2])));
}
ConsoleMethod(GuiTreeViewCtrl, buildVisibleTree, void, 3, 3, "Build the visible tree")
{
object->buildVisibleTree(dAtob(argv[2]));
}
ConsoleMethod(GuiTreeViewCtrl, getParent, S32, 3, 3, "(TreeItemId item)")
{
return(object->getParentItem(dAtoi(argv[2])));
}
ConsoleMethod(GuiTreeViewCtrl, getNextSibling, S32, 3, 3, "(TreeItemId item)")
{
return(object->getNextSiblingItem(dAtoi(argv[2])));
}
ConsoleMethod(GuiTreeViewCtrl, getPrevSibling, S32, 3, 3, "(TreeItemId item)")
{
return(object->getPrevSiblingItem(dAtoi(argv[2])));
}
ConsoleMethod(GuiTreeViewCtrl, getItemCount, S32, 2, 2, "")
{
return(object->getItemCount());
}
ConsoleMethod(GuiTreeViewCtrl, getSelectedItem, S32, 2, 2, "")
{
return ( object->getSelectedItem() );
}
ConsoleMethod(GuiTreeViewCtrl, getSelectedObject, S32, 2, 2, "returns the currently selected simObject in inspector mode or -1")
{
GuiTreeViewCtrl::Item *item = object->getItem( object->getSelectedItem() );
if( item != NULL && item->isInspectorData() )
{
SimObject *obj = item->getObject();
if( obj != NULL )
return object->getId();
}
return -1;
}
ConsoleMethod(GuiTreeViewCtrl, moveItemUp, void, 3, 3, "(TreeItemId item)")
{
object->moveItemUp( dAtoi( argv[2] ) );
}
ConsoleMethod(GuiTreeViewCtrl, getSelectedItemsCount, S32, 2, 2, "")
{
return ( object->getSelectedItemsCount() );
}
ConsoleMethod(GuiTreeViewCtrl, moveItemDown, void, 3, 3, "(TreeItemId item)")
{
object->moveItemDown( dAtoi( argv[2] ) );
}
//-----------------------------------------------------------------------------
ConsoleMethod(GuiTreeViewCtrl, getTextToRoot, const char*,4,4,"(TreeItemId item,Delimiter=none) gets the text from the current node to the root, concatenating at each branch upward, with a specified delimiter optionally")
{
if ( argc < 4 )
{
Con::warnf("GuiTreeViewCtrl::getTextToRoot - Invalid number of arguments!");
return ("");
}
S32 itemId = dAtoi( argv[2] );
StringTableEntry delimiter = argv[3];
return object->getTextToRoot( itemId, delimiter );
}
ConsoleMethod(GuiTreeViewCtrl,getSelectedItemList,const char*, 2,2,"returns a space seperated list of mulitple item ids")
{
char* buff = Con::getReturnBuffer(1024);
dSprintf(buff,1024,"");
for(int i = 0; i < object->mSelected.size(); i++)
{
S32 id = object->mSelected[i];
//get the current length of the buffer
U32 len = dStrlen(buff);
//the start of the buffer where we want to write
char* buffPart = buff+len;
//the size of the remaining buffer (-1 cause dStrlen doesn't count the \0)
S32 size = 1024-len-1;
//write it:
if(size < 1)
{
Con::errorf("GuiTreeViewCtrl::getSelectedItemList - Not enough room to return our object list");
return buff;
}
dSprintf(buffPart,size,"%d ", id);
}
//mSelected
return buff;
}
S32 GuiTreeViewCtrl::findItemByObjectId(S32 iObjId)
{
for (S32 i = 0; i < mItems.size(); i++)
{
SimObject* pObj = mItems[i]->getObject();
if(pObj && pObj->getId() == iObjId)
return mItems[i]->mId;
}
return -1;
}
//------------------------------------------------------------------------------
ConsoleMethod(GuiTreeViewCtrl, findItemByObjectId, S32, 3, 3, "(find item by object id and returns the mId)")
{
return(object->findItemByObjectId(dAtoi(argv[2])));
}