//----------------------------------------------------------------------------- // V12 Engine // // Copyright (c) 2001 GarageGames.Com // Portions Copyright (c) 2001 by Sierra Online, Inc. //----------------------------------------------------------------------------- #include "dgl/dgl.h" #include "shell/shellFancyArray.h" #include "gui/guiCanvas.h" #include "console/consoleTypes.h" //------------------------------------------------------------------------------ // // VirtualScrollContentCtrl functions // //------------------------------------------------------------------------------ IMPLEMENT_CONOBJECT(VirtualScrollContentCtrl); //------------------------------------------------------------------------------ VirtualScrollContentCtrl::VirtualScrollContentCtrl() : GuiScrollContentCtrl() { mVirtualContent = NULL; } //------------------------------------------------------------------------------ bool VirtualScrollContentCtrl::onAdd() { if ( !Parent::onAdd() ) return( false ); GuiControl* control = new GuiControl(); if ( !control->registerObject() ) Con::errorf( ConsoleLogEntry::General, "Failed to add virtual control to VirtualScrollContentCtrl!" ); addObject( control ); return( true ); } //------------------------------------------------------------------------------ void VirtualScrollContentCtrl::addObject( SimObject* obj ) { // The virtual scroll control can accept only one content control. GuiControl* control = dynamic_cast( obj ); AssertFatal( control, "Object added to the VirtualScrollContentCtrl is not a GuiControl!" ); if ( mVirtualContent ) mVirtualContent->deleteObject(); Parent::addObject( control ); mVirtualContent = control; // Pass the virtual content back through the line: GuiControl* parent = getParent(); if ( parent ) { AssertFatal( dynamic_cast( parent ), "VirtualScrollContentCtrl's parent is not a VirtualScrollCtrl!" ); VirtualScrollCtrl* scrollCtrl = static_cast( parent ); scrollCtrl->setVirtualContent( control ); } } //------------------------------------------------------------------------------ void VirtualScrollContentCtrl::removeObject( SimObject* obj ) { // If we are deleting the virtual content control, we need to let the "family" know:. GuiControl* control = dynamic_cast( obj ); if ( mVirtualContent == control ) mVirtualContent = NULL; GuiControl* parent = getParent(); if ( parent ) { AssertFatal( dynamic_cast( parent ), "VirtualScrollContentCtrl's parent is not a VirtualScrollCtrl!" ); VirtualScrollCtrl* scrollCtrl = static_cast( parent ); scrollCtrl->setVirtualContent( NULL ); } Parent::removeObject( obj ); } //------------------------------------------------------------------------------ // // VirtualScrollCtrl functions // //------------------------------------------------------------------------------ IMPLEMENT_CONOBJECT(VirtualScrollCtrl); //------------------------------------------------------------------------------ VirtualScrollCtrl::VirtualScrollCtrl() : ShellScrollCtrl() { // The virtual scroll control should not have a field background: mFieldBase = StringTable->insert( "" ); } //------------------------------------------------------------------------------ void VirtualScrollCtrl::setVirtualContent( GuiControl* control ) { GuiControl* parent = getParent(); if ( parent ) { AssertFatal( dynamic_cast( parent ), "VirtualScrollCtrl's parent is not a ShellFancyArrayScrollCtrl!" ); ShellFancyArrayScrollCtrl* arrayCtrl = static_cast( parent ); arrayCtrl->setVirtualContent( control ); } } //------------------------------------------------------------------------------ GuiControl* VirtualScrollCtrl::getVirtualContent() { if ( mContentCtrl ) { VirtualScrollContentCtrl* scrollContentCtrl = static_cast( mContentCtrl ); return( scrollContentCtrl->mVirtualContent ); } return( NULL ); } //------------------------------------------------------------------------------ bool VirtualScrollCtrl::onAdd() { // We have to bypass the GuiScrollCtrl::onAdd here: if ( !GuiControl::onAdd() ) return( false ); VirtualScrollContentCtrl* content = new VirtualScrollContentCtrl(); if ( !content->registerObject() ) Con::errorf( ConsoleLogEntry::General, "Failed to add virtual content control to VirtualScrollCtrl!" ); addObject( content ); return( true ); } //------------------------------------------------------------------------------ void VirtualScrollCtrl::addObject( SimObject* obj ) { VirtualScrollContentCtrl* contentCtrl = dynamic_cast( obj ); if ( contentCtrl ) { if ( mContentCtrl ) mContentCtrl->deleteObject(); // We have to bypass GuiScrollCtrl::addObject here: GuiControl::addObject( obj ); mContentCtrl = (GuiScrollContentCtrl*) contentCtrl; return; } AssertFatal( mContentCtrl, "ERROR - VirtualScrollCtrl has no content control!" ); mContentCtrl->addObject( obj ); computeSizes(); } //------------------------------------------------------------------------------ // // ShellFancyArray functions // //------------------------------------------------------------------------------ IMPLEMENT_CONOBJECT(ShellFancyArray); //------------------------------------------------------------------------------ ShellFancyArray::ShellFancyArray() : GuiControl() { dMemset( mBmpBounds, 0, sizeof( mBmpBounds ) ); mHeaderBitmap = StringTable->insert( "gui/server_tabs" ); mSortArrowBitmap = StringTable->insert( "gui/shll_sortarrow" ); mBarBase = StringTable->insert( "gui/shll_bar" ); mTexHeader = NULL; mTexSortArrow = NULL; mTexCellSelected = NULL; mTexCellRollover = NULL; mFieldBase = StringTable->insert( "" ); mTexLeftTop = NULL; mTexCenterTop = NULL; mTexRightTop = NULL; mTexLeftCenter = NULL; mTexCenter = NULL; mTexRightCenter = NULL; mTexLeftBottom = NULL; mTexCenterBottom = NULL; mTexRightBottom = NULL; // Initialize object statics: mActive = true; mFixedHorizontal = false; mColumnInfoList.clear(); mNumColumns = 0; mRowHeight = 20; // Default--can change the field mHeaderHeight = 0; // This is sized automatically in onWake mGlowOffset = 4; // This should be set based on the header bitmap mMinColumnWidth = 2; // This gets set automatically in onWake mStartScrollRgn.set( 0, 0 ); mFont = NULL; mHeaderFont = NULL; mHeaderFontType = StringTable->insert( "" ); mHeaderFontSize = 0; mDefaultCursor = NULL; mResizeCursor = NULL; mRepositionCursor = NULL; // Default color values: mHeaderFontColor.set( 8, 19, 6 ); mHeaderFontColorHL.set( 25, 68, 56 ); mSeparatorColor.set( 192, 192, 192 ); mDrawCellSeparators = false; mHeaderSort = true; mAllowReposition = true; mNoSelect = false; mNumRows = 0; mSelectedRow = -1; mMouseOverRow = -2; mMouseOverColumn = -1; mScrollPanePos.set( 0, 0 ); mColumnState = None; mActiveColumn = 0; mDragAnchor.set( 0, 0 ); mSortColumnKey = -1; mSecondarySortColumnKey = -1; mSortInc = true; mSecondarySortInc = true; mAbsResizeLeftMargin = 0; mAbsResizeRightMargin = 0; mResizeFixedColumn = false; mResizeColumnOrigSize = 0; mRepositionColumnTo = 0; mRepositionCursorPos.set( 0, 0 ); } //------------------------------------------------------------------------------ bool ShellFancyArray::onAdd() { if ( !Parent::onAdd() ) return false; // Initialize cursors: SimObject* obj = Sim::findObject( "DefaultCursor" ); mDefaultCursor = dynamic_cast( obj ); obj = Sim::findObject( "LeftRightCursor" ); mResizeCursor = dynamic_cast( obj ); obj = Sim::findObject( "GrabCursor" ); mRepositionCursor = dynamic_cast( obj ); return true; } //------------------------------------------------------------------------------ void ShellFancyArray::onRemove() { mColumnInfoList.clear(); mNumColumns = 0; Parent::onRemove(); } //------------------------------------------------------------------------------ void ShellFancyArray::initPersistFields() { Parent::initPersistFields(); addField( "startScrollRegion", TypePoint2I, Offset( mStartScrollRgn, ShellFancyArray ) ); addField( "headerBitmap", TypeString, Offset( mHeaderBitmap, ShellFancyArray ) ); addField( "sortArrowBitmap", TypeString, Offset( mSortArrowBitmap, ShellFancyArray ) ); addField( "fieldBase", TypeString, Offset( mFieldBase, ShellFancyArray ) ); addField( "barBase", TypeString, Offset( mBarBase, ShellFancyArray ) ); addField( "glowOffset", TypeS32, Offset( mGlowOffset, ShellFancyArray ) ); addField( "rowHeight", TypeS32, Offset( mRowHeight, ShellFancyArray ) ); addField( "headerFontType", TypeString, Offset( mHeaderFontType, ShellFancyArray ) ); addField( "headerFontSize", TypeS32, Offset( mHeaderFontSize, ShellFancyArray ) ); addField( "headerFontColor", TypeColorI, Offset( mHeaderFontColor, ShellFancyArray ) ); addField( "headerFontColorHL", TypeColorI, Offset( mHeaderFontColorHL, ShellFancyArray ) ); addField( "separatorColor", TypeColorI, Offset( mSeparatorColor, ShellFancyArray ) ); addField( "drawSeparators", TypeBool, Offset( mDrawCellSeparators, ShellFancyArray ) ); addField( "headerSort", TypeBool, Offset( mHeaderSort, ShellFancyArray ) ); addField( "allowReposition", TypeBool, Offset( mAllowReposition, ShellFancyArray ) ); addField( "noSelect", TypeBool, Offset( mNoSelect, ShellFancyArray ) ); } //------------------------------------------------------------------------------ static void cShellFancyArrayClearColumns( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArrayClearColumns!" ); ShellFancyArray* array = static_cast( obj ); array->clearColumns(); } //------------------------------------------------------------------------------ static S32 cShellFancyArrayGetNumColumns( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArrayClearColumns!" ); ShellFancyArray* array = static_cast( obj ); return array->getNumColumns(); } //------------------------------------------------------------------------------ static void cShellFancyArrayAddColumn( SimObject* obj, S32 argc, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArrayAddColumn!" ); ShellFancyArray* array = static_cast( obj ); array->addColumn( dAtoi( argv[2] ), argv[3], dAtoi( argv[4] ), dAtoi( argv[5] ), dAtoi( argv[6] ), ( argc == 8 ) ? argv[7] : NULL ); } //------------------------------------------------------------------------------ static void cShellFancyArrayAddRow( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArrayAddRow!" ); ShellFancyArray* array = static_cast( obj ); array->setNumRows( array->getNumRows() + 1 ); } //------------------------------------------------------------------------------ static void cShellFancyArraySetNumRows( SimObject* obj, S32, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArraySetNumRows!" ); ShellFancyArray* array = static_cast( obj ); array->setNumRows( dAtoi( argv[2] ) ); } //------------------------------------------------------------------------------ static S32 cShellFancyArrayRowCount( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArrayRowCount!" ); ShellFancyArray* array = static_cast( obj ); return( array->getNumRows() ); } //------------------------------------------------------------------------------ static void cShellFancyArrayForceUpdate( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArrayForceUpdate!" ); ShellFancyArray* array = static_cast( obj ); array->updateList(); } //------------------------------------------------------------------------------ static void cShellFancyArrayClearList( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArrayClearList!" ); ShellFancyArray* array = static_cast( obj ); array->clearList(); } //------------------------------------------------------------------------------ static void cShellFancyArraySetSelectedRow( SimObject* obj, S32, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArraySetSelectedRow!" ); ShellFancyArray* array = static_cast( obj ); array->selectCell( dAtoi( argv[2] ), 0 ); } //------------------------------------------------------------------------------ static void cShellFancyArraySetSortColumn( SimObject* obj, S32, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArraySetSortColumn!" ); ShellFancyArray* array = static_cast( obj ); array->setSortColumnKey( dAtoi( argv[2] ) ); } //------------------------------------------------------------------------------ static void cShellFancyArraySetSortIncreasing( SimObject* obj, S32, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArraySetSortIncreasing!" ); ShellFancyArray* array = static_cast( obj ); array->setSortInc( dAtob( argv[2] ) ); } //------------------------------------------------------------------------------ static void cSFASetSecondarySortColumn( SimObject* obj, S32, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cSFASetSecondarySortColumn!" ); ShellFancyArray* array = static_cast( obj ); array->setSecondarySortColumnKey( dAtoi( argv[2] ) ); } //------------------------------------------------------------------------------ static void cSFASetSecondarySortIncreasing( SimObject* obj, S32, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cSFASetSecondarySortIncreasing!" ); ShellFancyArray* array = static_cast( obj ); array->setSecondarySortInc( dAtob( argv[2] ) ); } //------------------------------------------------------------------------------ static S32 cShellFancyArrayGetSelectedRow( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Non-ShellFancyArray passed to cShellFancyArrayGetSelectedRow!" ); ShellFancyArray* array = static_cast( obj ); return( array->getSelectedRow() ); } //------------------------------------------------------------------------------ static S32 cShellFancyArrayGetColumnKey( SimObject* obj, S32, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Object passed to cShellFancyArrayGetColumnKey is not a ShellFancyArray!" ); ShellFancyArray* array = static_cast( obj ); return ( array->getColumnKey( dAtoi( argv[2] ) ) ); } //------------------------------------------------------------------------------ static S32 cShellFancyArrayGetColumnWidth( SimObject* obj, S32, const char** argv ) { AssertFatal( dynamic_cast( obj ), "Object passed to cShellFancyArrayGetColumnWidth is not a ShellFancyArray!" ); ShellFancyArray* array = static_cast( obj ); return ( array->getColumnWidth( dAtoi( argv[2] ) ) ); } //------------------------------------------------------------------------------ static S32 cShellFancyArrayGetSortColumnKey( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Object passed to cShellFancyArrayGetSortColumnKey is not a ShellFancyArray!" ); ShellFancyArray* array = static_cast( obj ); return ( array->getSortColumnKey() ); } //------------------------------------------------------------------------------ static bool cShellFancyArrayGetSortIncreasing( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Object passed to cShellFancyArrayGetSortIncreasing is not a ShellFancyArray!" ); ShellFancyArray* array = static_cast( obj ); return ( array->getSortIncreasing() ); } //------------------------------------------------------------------------------ static S32 cSFAGetSecondarySortColumnKey( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Object passed to cSFAGetSecondarySortColumnKey is not a ShellFancyArray!" ); ShellFancyArray* array = static_cast( obj ); return ( array->getSecondarySortColumnKey() ); } //------------------------------------------------------------------------------ static bool cSFAGetSecondarySortIncreasing( SimObject* obj, S32, const char** ) { AssertFatal( dynamic_cast( obj ), "Object passed to cSFAGetSecondarySortIncreasing is not a ShellFancyArray!" ); ShellFancyArray* array = static_cast( obj ); return ( array->getSecondarySortIncreasing() ); } //------------------------------------------------------------------------------ void ShellFancyArray::consoleInit() { Con::addCommand( "ShellFancyArray", "clearColumns", cShellFancyArrayClearColumns, "array.clearColumns();", 2, 2 ); Con::addCommand( "ShellFancyArray", "getNumColumns", cShellFancyArrayGetNumColumns, "array.getNumColumns();", 2, 2 ); Con::addCommand( "ShellFancyArray", "addColumn", cShellFancyArrayAddColumn, "array.addColumn( key, name, defaultWidth, minWidth, maxWidth{, flags} );", 7, 8 ); Con::addCommand( "ShellFancyArray", "addRow", cShellFancyArrayAddRow, "array.addRow();", 2, 2 ); Con::addCommand( "ShellFancyArray", "setNumRows", cShellFancyArraySetNumRows, "array.setNumRows( numRows );", 3, 3 ); Con::addCommand( "ShellFancyArray", "rowCount", cShellFancyArrayRowCount, "array.rowCount();", 2, 2 ); Con::addCommand( "ShellFancyArray", "clearList", cShellFancyArrayClearList, "array.clearList();", 2, 2 ); Con::addCommand( "ShellFancyArray", "forceUpdate", cShellFancyArrayForceUpdate, "array.forceUpdate();", 2, 2 ); Con::addCommand( "ShellFancyArray", "setSelectedRow", cShellFancyArraySetSelectedRow, "array.setSelectedRow( row );", 3, 3 ); Con::addCommand( "ShellFancyArray", "setSortColumn", cShellFancyArraySetSortColumn, "array.setSortColumn( key );", 3, 3 ); Con::addCommand( "ShellFancyArray", "setSortIncreasing", cShellFancyArraySetSortIncreasing, "array.setSortIncreasing( );", 3, 3 ); Con::addCommand( "ShellFancyArray", "setSecondarySortColumn", cSFASetSecondarySortColumn, "array.setSecondarySortColumn( key );", 3, 3 ); Con::addCommand( "ShellFancyArray", "setSecondarySortIncreasing", cSFASetSecondarySortIncreasing, "array.setSecondarySortIncreasing( );", 3, 3 ); Con::addCommand( "ShellFancyArray", "getSelectedRow", cShellFancyArrayGetSelectedRow, "array.getSelectedRow();", 2, 2 ); Con::addCommand( "ShellFancyArray", "getColumnKey", cShellFancyArrayGetColumnKey, "array.getColumnKey( index );", 3, 3 ); Con::addCommand( "ShellFancyArray", "getColumnWidth", cShellFancyArrayGetColumnWidth, "array.getColumnWidth( index );", 3, 3 ); Con::addCommand( "ShellFancyArray", "getSortColumnKey", cShellFancyArrayGetSortColumnKey, "array.getSortColumnKey();", 2, 2 ); Con::addCommand( "ShellFancyArray", "getSortIncreasing", cShellFancyArrayGetSortIncreasing, "array.getSortIncreasing();", 2, 2 ); Con::addCommand( "ShellFancyArray", "getSecondarySortColumnKey", cSFAGetSecondarySortColumnKey, "array.getSecondarySortColumnKey();", 2, 2 ); Con::addCommand( "ShellFancyArray", "getSecondarySortIncreasing", cSFAGetSecondarySortIncreasing, "array.getSecondarySortIncreasing();", 2, 2 ); } //------------------------------------------------------------------------------ void ShellFancyArray::clearColumns() { mColumnInfoList.clear(); mNumColumns = 0; } //------------------------------------------------------------------------------ void ShellFancyArray::addColumn( S32 key, const char* name, S32 defaultWidth, S32 minWidth, S32 maxWidth, const char* flags ) { ColumnInfo newColumn; newColumn.name = StringTable->insert( name, true ); newColumn.key = key; newColumn.minWidth = getMax( mMinColumnWidth, minWidth ); newColumn.maxWidth = maxWidth; newColumn.width = getMin( maxWidth, getMax( newColumn.minWidth, defaultWidth ) ); newColumn.flags = 0; if ( flags ) { if ( dStrstr( flags, "numeric" ) ) newColumn.flags |= Column_Numeric; else if ( dStrstr( flags, "icon" ) ) newColumn.flags |= Column_Icon; if ( dStrstr( flags, "center" ) ) newColumn.flags |= Column_Center; else if ( dStrstr( flags, "right" ) ) newColumn.flags |= Column_Right; } mColumnInfoList.push_back( newColumn ); mNumColumns = mColumnInfoList.size(); if ( mFixedHorizontal ) mStartScrollRgn.x = mNumColumns; } //------------------------------------------------------------------------------ void ShellFancyArray::clearList() { mSelectedRow = -1; mMouseOverRow = -2; setNumRows( 0 ); } //------------------------------------------------------------------------------ void ShellFancyArray::updateList() { // This function should be called whenever the contents of the // array have changed and usually only has to call setNumRows // and setUpdate. } //------------------------------------------------------------------------------ bool ShellFancyArray::onWake() { if ( !Parent::onWake() ) return false; // Get the font: mFont = mProfile->mFont; // Get the header font: if ( mHeaderFontType[0] ) mHeaderFont = GFont::create( mHeaderFontType, mHeaderFontSize ); if ( !mHeaderFont ) mHeaderFont = mFont; // Get the cell textures: char buf[512]; bool result; if ( mHeaderBitmap[0] ) { dSprintf( buf, sizeof( buf ), "%s.png", mHeaderBitmap ); mTexHeader = TextureHandle( buf, BitmapKeepTexture ); result = createBitmapArray( mTexHeader.getBitmap(), mBmpBounds, StateCount, BmpCount ); AssertFatal( result, "Failed to create the header bitmap array for the ShellFancyArray!" ); mHeaderHeight = mBmpBounds[0].extent.y - ( 2 * mGlowOffset ); mMinColumnWidth = mBmpBounds[StateCount * BmpLeft].extent.x + mBmpBounds[StateCount * BmpRight].extent.x - ( 2 * mGlowOffset ); // Set the minimum extents: mMinExtent.y = mGlowOffset + mHeaderHeight; resize( mBounds.point, mBounds.extent ); } if ( mSortArrowBitmap[0] ) { dSprintf( buf, sizeof( buf ), "%s.png", mSortArrowBitmap ); mTexSortArrow = TextureHandle( buf, BitmapTexture ); } // Get the field: if ( mFieldBase[0] ) { dSprintf( buf, sizeof( buf ), "%s_TL.png", mFieldBase ); mTexLeftTop = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_TM.png", mFieldBase ); mTexCenterTop = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_TR.png", mFieldBase ); mTexRightTop = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_ML.png", mFieldBase ); mTexLeftCenter = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_MM.png", mFieldBase ); mTexCenter = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_MR.png", mFieldBase ); mTexRightCenter = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_BL.png", mFieldBase ); mTexLeftBottom = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_BM.png", mFieldBase ); mTexCenterBottom = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_BR.png", mFieldBase ); mTexRightBottom = TextureHandle( buf, BitmapTexture ); } if ( mBarBase[0] ) { dSprintf( buf, sizeof( buf ), "%s_rol.png", mBarBase ); mTexCellRollover = TextureHandle( buf, BitmapTexture ); dSprintf( buf, sizeof( buf ), "%s_act.png", mBarBase ); mTexCellSelected = TextureHandle( buf, BitmapTexture ); } return true; } //------------------------------------------------------------------------------ void ShellFancyArray::onSleep() { Parent::onSleep(); mFont = NULL; mHeaderFont = NULL; mTexHeader = NULL; mTexSortArrow = NULL; mTexLeftTop = NULL; mTexCenterTop = NULL; mTexRightTop = NULL; mTexLeftCenter = NULL; mTexCenter = NULL; mTexRightCenter = NULL; mTexLeftBottom = NULL; mTexCenterBottom = NULL; mTexRightBottom = NULL; mTexCellRollover = NULL; mTexCellSelected = NULL; } //------------------------------------------------------------------------------ bool ShellFancyArray::pointInColumn( bool inHeader, Point2I pt, S32 &column, bool &inResizeRgn, bool &resizeLeft ) { S32 columnLimit = ( mNumColumns > mStartScrollRgn.x ) ? mStartScrollRgn.x : mNumColumns; S32 startPos = 0, endPos = 0; inResizeRgn = false; if ( inHeader ) { if ( pt.x < 0 || pt.y < 0 || pt.x >= mBounds.extent.x || pt.y >= ( mHeaderHeight + mGlowOffset ) ) return false; } else { if ( pt.x < 0 || pt.y < mHeaderHeight || pt.x >= mBounds.extent.x || pt.y >= mBounds.extent.y ) return false; } for ( column = 0; column < columnLimit; column++ ) { endPos = startPos + mColumnInfoList[column].width; if ( pt.x < endPos ) { if ( ( ( pt.x - startPos ) < 4 && column ) || ( ( endPos - pt.x ) < 4 && ( !mFixedHorizontal || column != mNumColumns - 1 ) ) ) inResizeRgn = true; resizeLeft = pt.x < ( startPos + ( ( endPos - startPos ) / 2 ) ); return true; } startPos = endPos; } RectI columnScrollRect; if ( getColumnScrollViewRect( columnScrollRect ) ) { startPos = columnScrollRect.point.x + mScrollPanePos.x; for ( column = mStartScrollRgn.x; column < mColumnInfoList.size(); column++ ) { endPos = startPos + mColumnInfoList[column].width; if ( pt.x < endPos ) { if ( ( ( pt.x - startPos ) < 4 && column ) || ( ( endPos - pt.x ) < 4 && ( !mFixedHorizontal || column != mNumColumns - 1 ) ) ) inResizeRgn = true; resizeLeft = ( pt.x < ( startPos + ( ( endPos - startPos ) / 2 ) ) ); return true; } startPos = endPos; } } return false; } //------------------------------------------------------------------------------ bool ShellFancyArray::getScrollRect( RectI &rect ) { bool hasHorizontalScroll = getColumnScrollViewRect( rect ); rect.point.y = mHeaderHeight + mGlowOffset + ( mRowHeight * mStartScrollRgn.y ); rect.extent.y = mBounds.extent.y - rect.point.y; return hasHorizontalScroll; } //------------------------------------------------------------------------------ void ShellFancyArray::setSortColumnKey( S32 newKey ) { if ( mSortColumnKey == newKey ) mSortInc = !mSortInc; else mSortColumnKey = newKey; sort(); } //------------------------------------------------------------------------------ void ShellFancyArray::setSecondarySortColumnKey( S32 newKey ) { if ( mSecondarySortColumnKey == newKey ) mSecondarySortInc = !mSecondarySortInc; else mSecondarySortColumnKey = ( newKey == mSortColumnKey ) ? -1 : newKey; sort(); } //------------------------------------------------------------------------------ void ShellFancyArray::sort() { } //------------------------------------------------------------------------------ void ShellFancyArray::selectCell( S32 row, S32 column ) { if ( row < 0 || row >= mNumRows ) { mSelectedRow = -1; return; } mSelectedRow = row; onCellSelected( row, column ); scrollSelectedRowVisible(); setUpdate(); } //------------------------------------------------------------------------------ void ShellFancyArray::setNumRows( S32 numRows ) { if ( mNumRows > numRows ) { // Need to scroll off the dead rows: if ( numRows > mStartScrollRgn.y ) mScrollPanePos.y += ( mNumRows - numRows ) * mRowHeight; else mScrollPanePos.y = 0; if ( mScrollPanePos.y > 0 ) mScrollPanePos.y = 0; } if ( numRows != mNumRows ) { mNumRows = numRows; if ( mNumRows ) scrollSelectedRowVisible(); setUpdate(); } } //------------------------------------------------------------------------------ S32 ShellFancyArray::getNoScrollWidth() { S32 result = 0; U32 colCount = getMin( mStartScrollRgn.x, mColumnInfoList.size() ); for ( U32 i = 0; i < colCount; i++ ) result += mColumnInfoList[i].width; return( result ); } //------------------------------------------------------------------------------ S32 ShellFancyArray::getColumnKey( S32 index ) { if ( index < 0 || index >= mColumnInfoList.size() ) return( -1 ); return( mColumnInfoList[index].key ); } //------------------------------------------------------------------------------ S32 ShellFancyArray::getColumnWidth( S32 index ) { if ( index < 0 || index >= mColumnInfoList.size() ) return( -1 ); return( mColumnInfoList[index].width ); } //------------------------------------------------------------------------------ void ShellFancyArray::forceFillScrollRegion() { if ( mStartScrollRgn.x >= mColumnInfoList.size() ) return; RectI r; if ( !getColumnScrollViewRect( r ) ) return; S32 i, len = 0; for ( i = mStartScrollRgn.x; i < mColumnInfoList.size(); i++ ) len += mColumnInfoList[i].width; if ( len >= r.extent.x ) return; S32 delta = ( r.extent.x - len ) / ( mColumnInfoList.size() - mStartScrollRgn.x ); len = 0; for ( i = mStartScrollRgn.x; i < mColumnInfoList.size() - 1; i++ ) { mColumnInfoList[i].width += delta; if ( mColumnInfoList[i].maxWidth < mColumnInfoList[i].width ) mColumnInfoList[i].maxWidth = mColumnInfoList[i].width; len += mColumnInfoList[i].width; } mColumnInfoList[i].width = r.extent.x - len; if ( mColumnInfoList[i].maxWidth < mColumnInfoList[i].width ) mColumnInfoList[i].maxWidth = mColumnInfoList[i].width; if ( mColumnInfoList[i].minWidth > mColumnInfoList[i].width ) mColumnInfoList[i].minWidth = mColumnInfoList[i].width; } //------------------------------------------------------------------------------ void ShellFancyArray::onHeaderAction( S32 column ) { setSortColumnKey( mColumnInfoList[column].key ); Con::executef( this, 3, "onSetSortKey", Con::getIntArg( mSortColumnKey ), Con::getIntArg( mSortInc ) ); } //------------------------------------------------------------------------------ void ShellFancyArray::onSecondaryHeaderAction( S32 column ) { setSecondarySortColumnKey( mColumnInfoList[column].key ); Con::executef( this, 3, "onSetSecondarySortKey", Con::getIntArg( mSecondarySortColumnKey ), Con::getIntArg( mSecondarySortInc ) ); } //------------------------------------------------------------------------------ void ShellFancyArray::onCellSelected( S32 row, S32 column ) { Con::executef( this, 3, "onSelect", Con::getIntArg( row ), Con::getIntArg( column ) ); // Call the console function: if ( mConsoleCommand[0] ) Con::evaluate( mConsoleCommand, false ); } //------------------------------------------------------------------------------ void ShellFancyArray::scrollSelectedRowVisible() { RectI r; if ( !getRowScrollViewRect( r ) ) return; // First, make sure we are within the acceptable range: S32 totalLen = ( mNumRows - mStartScrollRgn.y ) * mRowHeight; if ( totalLen > r.extent.y && ( ( mScrollPanePos.y + totalLen ) < r.extent.y ) ) { mScrollPanePos.y = r.extent.y - totalLen; setUpdate(); } if ( mSelectedRow < 0 || mSelectedRow >= mNumRows || mSelectedRow < mStartScrollRgn.y ) return; S32 rowStartPos_y = r.point.y + mScrollPanePos.y + ( mRowHeight * ( mSelectedRow - mStartScrollRgn.y ) ); S32 rowEndPos_y = rowStartPos_y + mRowHeight; if ( rowStartPos_y >= r.point.y && rowEndPos_y <= ( r.point.y + r.extent.y ) ) return; // Position the scrollable row region such that the selected row is visible: if ( rowStartPos_y < r.point.y ) mScrollPanePos.y += ( r.point.y - rowStartPos_y ); if ( rowEndPos_y > ( r.point.y + r.extent.y ) ) mScrollPanePos.y += ( ( r.point.y + r.extent.y ) - rowEndPos_y ) + 1; } //------------------------------------------------------------------------------ void ShellFancyArray::computeFixedResizingVals() { if ( mActiveColumn < 0 || mActiveColumn >= mNumColumns || mActiveColumn >= mStartScrollRgn.x ) return; S32 i; mAbsResizeLeftMargin = 0; for ( i = 0; i < mActiveColumn; i++ ) mAbsResizeLeftMargin += mColumnInfoList[i].width; mAbsResizeLeftMargin += mColumnInfoList[mActiveColumn].minWidth; mAbsResizeRightMargin = mBounds.extent.x; S32 columnLimit = mNumColumns - 1; if ( mStartScrollRgn.x < mNumColumns ) { // Subtract 100 if we have a scroll region. // ( the minimum scroll region size ) mAbsResizeRightMargin -= 100; columnLimit = mStartScrollRgn.x - 1; } for ( i = columnLimit; i < mActiveColumn; i-- ) mAbsResizeRightMargin -= mColumnInfoList[i].minWidth; } //------------------------------------------------------------------------------ void ShellFancyArray::resizeFixedColumn( const GuiEvent &event ) { if ( mActiveColumn < 0 || mActiveColumn >= mNumColumns || mActiveColumn > mStartScrollRgn.x ) return; Point2I pt = globalToLocalCoord( event.mousePoint ); if ( pt.x < mAbsResizeLeftMargin ) pt.x = mAbsResizeLeftMargin; if ( pt.x > mAbsResizeRightMargin ) pt.x = mAbsResizeRightMargin; S32 widthDelta = mColumnInfoList[mActiveColumn].width; mColumnInfoList[mActiveColumn].width = pt.x - mAbsResizeLeftMargin + mColumnInfoList[mActiveColumn].minWidth; if ( mColumnInfoList[mActiveColumn].width > mColumnInfoList[mActiveColumn].maxWidth ) mColumnInfoList[mActiveColumn].width = mColumnInfoList[mActiveColumn].maxWidth; widthDelta = mColumnInfoList[mActiveColumn].width - widthDelta; ShellFancyArrayScrollCtrl* daddy = static_cast( getParent() ); if ( daddy ) daddy->positionChildren(); setUpdate(); } //------------------------------------------------------------------------------ void ShellFancyArray::resizeScrollColumn( const GuiEvent &event ) { if ( mActiveColumn < mStartScrollRgn.x || mActiveColumn >= mNumColumns ) return; S32 deltaX = event.mousePoint.x - mDragAnchor.x; mColumnInfoList[mActiveColumn].width += ( deltaX - mColumnInfoList[mActiveColumn].width + mResizeColumnOrigSize ); if ( mColumnInfoList[mActiveColumn].width < mColumnInfoList[mActiveColumn].minWidth ) mColumnInfoList[mActiveColumn].width = mColumnInfoList[mActiveColumn].minWidth; if ( mColumnInfoList[mActiveColumn].width > mColumnInfoList[mActiveColumn].maxWidth ) mColumnInfoList[mActiveColumn].width = mColumnInfoList[mActiveColumn].maxWidth; ShellFancyArrayScrollCtrl* daddy = static_cast( getParent() ); if ( daddy ) { if ( mActiveColumn == mNumColumns - 1 ) { RectI r; if ( getScrollRect( r ) ) mScrollPanePos.x = r.extent.x - getScrollExtent().x; } daddy->positionChildren(); } setUpdate(); } //------------------------------------------------------------------------------ Point2I ShellFancyArray::getScrollExtent() { Point2I extent( 0, 0 ); if ( mNumRows > mStartScrollRgn.y ) extent.y = ( mNumRows - mStartScrollRgn.y ) * mRowHeight; if ( mNumColumns > mStartScrollRgn.x ) { for ( S32 i = mStartScrollRgn.x; i < mNumColumns; i++ ) extent.x += mColumnInfoList[i].width; } return extent; } //------------------------------------------------------------------------------ void ShellFancyArray::onMouseDown( const GuiEvent& event ) { if ( !mActive || !mVisible || !mAwake ) return; if ( mProfile->mCanKeyFocus ) setFirstResponder(); // Lock the mouse to this control: mouseLock(); Point2I pt = globalToLocalCoord( event.mousePoint ); S32 prevSelected, column; bool inResizeRgn, resizeLeft; if ( pt.y > ( mHeaderHeight + mGlowOffset ) ) { if ( mNoSelect ) return; // In browser region, so select the row: if ( pointInColumn( false, pt, column, inResizeRgn, resizeLeft ) ) { S32 rowPos = ( pt.y - ( mHeaderHeight + mGlowOffset ) ) / mRowHeight; if ( rowPos < mStartScrollRgn.y ) { prevSelected = mSelectedRow; selectCell( rowPos, column ); // Test for double-click on same cell: if ( ( event.mouseClickCount > 1 ) && ( prevSelected == mSelectedRow ) && mAltConsoleCommand[0] ) Con::evaluate( mAltConsoleCommand ); return; } RectI rowScrollRect; if ( getRowScrollViewRect( rowScrollRect ) ) { rowPos = ( ( pt.y - rowScrollRect.point.y - mScrollPanePos.y ) / mRowHeight ) + mStartScrollRgn.y; if ( rowPos < mNumRows ) { prevSelected = mSelectedRow; selectCell( rowPos, column ); // Test for double-click on same cell: if ( ( event.mouseClickCount > 1 ) && ( prevSelected == mSelectedRow ) && mAltConsoleCommand[0] ) Con::evaluate( mAltConsoleCommand ); } } } return; } if ( pointInColumn( true, pt, column, inResizeRgn, resizeLeft ) ) { if ( inResizeRgn ) { // Start resizing: mColumnState = Resizing; mActiveColumn = column; mMouseOverColumn = -1; if ( resizeLeft ) mActiveColumn--; mResizeFixedColumn = mActiveColumn < mStartScrollRgn.x; if ( mResizeFixedColumn ) computeFixedResizingVals(); else { mDragAnchor = event.mousePoint; mResizeColumnOrigSize = mColumnInfoList[mActiveColumn].width; } } else if ( mHeaderSort ) { // Select the column header: mColumnState = Sorting; mActiveColumn = column; mDragAnchor = event.mousePoint; determineRepositionColumn( pt ); if ( mProfile->mSoundButtonDown ) { F32 pan = ( F32( event.mousePoint.x ) / F32( Canvas->mBounds.extent.x ) * 2.0f - 1.0f ) * 0.8f; AUDIOHANDLE handle = alxCreateSource( mProfile->mSoundButtonDown ); alxSourcef( handle, AL_PAN, pan ); alxPlay( handle ); } setUpdate(); } } } //------------------------------------------------------------------------------ void ShellFancyArray::onMouseUp( const GuiEvent& event ) { if ( !mActive || !mVisible || !mAwake ) return; Point2I pt = globalToLocalCoord( event.mousePoint ); switch ( mColumnState ) { case Sorting: { S32 column; bool inResizeRgn, resizeLeft; if ( pt.y <= ( mHeaderHeight + mGlowOffset ) && pointInColumn( true, pt, column, inResizeRgn, resizeLeft ) ) { if ( column == mActiveColumn ) onHeaderAction( column ); } break; } case Repositioning: { if ( mActiveColumn == mRepositionColumnTo || ( mActiveColumn + 1 ) == mRepositionColumnTo ) break; ColumnInfo temp = mColumnInfoList[mActiveColumn]; S32 insertPos = mRepositionColumnTo; if ( mActiveColumn < mRepositionColumnTo ) insertPos--; mColumnInfoList.erase( mActiveColumn ); mColumnInfoList.insert( insertPos ); mColumnInfoList[insertPos] = temp; // Call the console function: Con::executef( this, 3, "onColumnRepositioned", Con::getIntArg( mActiveColumn ), Con::getIntArg( mRepositionColumnTo ) ); break; } case Resizing: // Call the console function: Con::executef( this, 4, "onColumnResize", Con::getIntArg( mActiveColumn ), Con::getIntArg( mColumnInfoList[mActiveColumn].width ), Con::getIntArg( mColumnInfoList[mActiveColumn].key ) ); break; } // Unlock the mouse: setUpdate(); mouseUnlock(); Canvas->setCursor( mDefaultCursor ); mColumnState = None; } //------------------------------------------------------------------------------ void ShellFancyArray::onMouseMove( const GuiEvent& event ) { Point2I pt = globalToLocalCoord( event.mousePoint ); if ( pt.y < ( mGlowOffset + mHeaderHeight ) ) { GuiCursor* newCursor = mDefaultCursor; S32 column; bool inResizeRgn, unused2; if ( pointInColumn( true, pt, column, inResizeRgn, unused2 ) ) { if ( inResizeRgn && mResizeCursor ) { setMouseOverColumn( -1 ); newCursor = mResizeCursor; } else if ( mHeaderSort ) setMouseOverColumn( column, event.mousePoint.x ); } setMouseOverRow( -1 ); // -1 is the code for the header Canvas->setCursor( newCursor ); return; } else { Canvas->setCursor( mDefaultCursor ); setMouseOverColumn( -1 ); S32 row = ( pt.y - mGlowOffset - mHeaderHeight ) / mRowHeight; if ( row < mStartScrollRgn.y ) { setMouseOverRow( row ); return; } RectI rowScrollRect; if ( getRowScrollViewRect( rowScrollRect ) ) { row = ( ( pt.y - rowScrollRect.point.y - mScrollPanePos.y ) / mRowHeight ) + mStartScrollRgn.y; if ( row < mNumRows ) { setMouseOverRow( row ); return; } } } setMouseOverRow( -2 ); } //------------------------------------------------------------------------------ void ShellFancyArray::onMouseDragged( const GuiEvent& event ) { if ( mColumnState == None ) return; if ( mColumnState == Resizing ) { if ( mResizeFixedColumn ) resizeFixedColumn( event ); else resizeScrollColumn( event ); } else { if ( !mAllowReposition || mActiveColumn < mStartScrollRgn.x || ( mColumnInfoList.size() - mStartScrollRgn.x ) <= 1 ) mColumnState = Sorting; else mColumnState = ( mAbs( event.mousePoint.x - mDragAnchor.x ) > 4 ) ? Repositioning : Sorting; if ( mColumnState == Repositioning ) { if ( mRepositionCursor ) Canvas->setCursor( mRepositionCursor ); RectI columnRect; if ( getColumnScrollViewRect( columnRect ) ) { // Scroll the columns for positioning if necessary: columnRect.point = localToGlobalCoord( columnRect.point ); Point2I newMousePt = event.mousePoint; if ( event.mousePoint.x < columnRect.point.x ) { // Scroll right: newMousePt.set( columnRect.point.x, event.mousePoint.y ); mScrollPanePos.x += ( columnRect.point.x - event.mousePoint.x ); if ( mScrollPanePos.x > 0 ) { mScrollPanePos.x = 0; ShellFancyArrayScrollCtrl* daddy = static_cast( getParent() ); if ( daddy ) daddy->positionChildren(); } GuiCanvas* root = getRoot(); if ( root ) root->setCursorPos( newMousePt ); setUpdate(); } else if ( event.mousePoint.x > ( columnRect.point.x + columnRect.extent.x ) ) { // Scroll left: newMousePt.set( columnRect.point.x + columnRect.extent.x, event.mousePoint.y ); mScrollPanePos.x += ( columnRect.point.x + columnRect.extent.x - event.mousePoint.x ); if ( columnRect.extent.x > ( getScrollExtent().x + mScrollPanePos.x ) ) { mScrollPanePos.x = columnRect.extent.x - getScrollExtent().x; ShellFancyArrayScrollCtrl* daddy = static_cast( getParent() ); if ( daddy ) daddy->positionChildren(); } GuiCanvas* root = getRoot(); if ( root ) root->setCursorPos( newMousePt ); setUpdate(); } newMousePt = globalToLocalCoord( newMousePt ); determineRepositionColumn( newMousePt ); } } } } //------------------------------------------------------------------------------ void ShellFancyArray::onMouseEnter( const GuiEvent &/*event*/ ) { // Change the cursor to the appropriate one: if ( mColumnState == None ) return; if ( mColumnState == Resizing && mResizeCursor ) Canvas->setCursor( mResizeCursor ); if ( mColumnState == Repositioning && mRepositionCursor ) Canvas->setCursor( mRepositionCursor ); } //------------------------------------------------------------------------------ void ShellFancyArray::onMouseLeave( const GuiEvent &/*event*/ ) { // Restore the default cursor: Canvas->setCursor( mDefaultCursor ); setMouseOverRow( -2 ); setMouseOverColumn( -1 ); } //------------------------------------------------------------------------------ void ShellFancyArray::onRightMouseDown( const GuiEvent &event ) { if ( !mActive || !mAwake || !mVisible ) return; Parent::onRightMouseDown( event ); // Ignore if left mouse button is down: if ( mColumnState != None ) return; Point2I pt = globalToLocalCoord( event.mousePoint ); S32 column, row; bool inResizeRgn, resizeLeft; if ( pt.y <= ( mHeaderHeight + mGlowOffset ) ) { #if 0 // Not yet ready for prime time... if ( mHeaderSort ) { if ( pointInColumn( true, pt, column, inResizeRgn, resizeLeft ) ) { if ( !inResizeRgn ) { // Set the secondary sort column: mColumnState = SecondarySorting; mActiveColumn = column; if ( mProfile->mSoundButtonDown ) { F32 pan = ( F32( event.mousePoint.x ) / F32( Canvas->mBounds.extent.x ) * 2.0f - 1.0f ) * 0.8f; AUDIOHANDLE handle = alxCreateSource( mProfile->mSoundButtonDown ); alxSourcef( handle, AL_PAN, pan ); alxPlay( handle ); } } } } #endif return; } // In browser region, so find the hit row: if ( pointInColumn( false, pt, column, inResizeRgn, resizeLeft ) ) { row = ( pt.y - ( mHeaderHeight + mGlowOffset ) ) / mRowHeight; if ( row >= mStartScrollRgn.y ) { RectI rowScrollRect; if ( getRowScrollViewRect( rowScrollRect ) ) { row = ( ( pt.y - rowScrollRect.point.y - mScrollPanePos.y ) / mRowHeight ) + mStartScrollRgn.y; if ( row >= mNumRows ) return; // Didn't actually hit a row... } } } // Pass it to the console: char buf[32]; dSprintf( buf, sizeof( buf ), "%d %d", event.mousePoint.x, event.mousePoint.y ); Con::executef( this, 4, "onRightMouseDown", Con::getIntArg( column ), Con::getIntArg( row ), buf ); } //------------------------------------------------------------------------------ void ShellFancyArray::onRightMouseUp( const GuiEvent &event ) { if ( !mActive || !mAwake || !mVisible ) return; if ( mColumnState == SecondarySorting ) { Point2I pt = globalToLocalCoord( event.mousePoint ); S32 column; bool inResizeRgn, resizeLeft; if ( pt.y <= ( mHeaderHeight + mGlowOffset ) && pointInColumn( true, pt, column, inResizeRgn, resizeLeft ) ) { if ( column == mActiveColumn ) onSecondaryHeaderAction( column ); } } mColumnState = None; } //------------------------------------------------------------------------------ bool ShellFancyArray::onMouseWheelUp( const GuiEvent &event ) { if ( !mVisible || !mAwake ) return( false ); // scroll up a row... if ( mScrollPanePos.y + mRowHeight > 0 ) mScrollPanePos.y = 0; else mScrollPanePos.y += mRowHeight; // update the mouse over row... onMouseMove( event ); return( true ); } //------------------------------------------------------------------------------ bool ShellFancyArray::onMouseWheelDown( const GuiEvent &event ) { if ( !mVisible || !mAwake ) return( false ); RectI r; if ( !getRowScrollViewRect( r ) ) return( true ); S32 minPos = getMin( r.extent.y - ( ( mNumRows - mStartScrollRgn.y ) * mRowHeight ), 0 ); // scroll down a row... if ( mScrollPanePos.y - mRowHeight < minPos ) mScrollPanePos.y = minPos; else mScrollPanePos.y -= mRowHeight; // update the mouse over row... onMouseMove( event ); return( true ); } //------------------------------------------------------------------------------ bool ShellFancyArray::onKeyDown( const GuiEvent& event ) { if ( !mVisible || !mActive || !mAwake ) return true; if ( mSelectedRow < 0 ) return true; if ( event.modifier == 0 ) { switch ( event.keyCode ) { case KEY_RETURN: if ( mSelectedRow > 0 && mAltConsoleCommand[0] ) { Con::evaluate( mAltConsoleCommand, false ); return true; } break; case KEY_UP: if ( mSelectedRow > 0 ) selectCell( mSelectedRow - 1, 0 ); return true; case KEY_DOWN: if ( mSelectedRow < ( mNumRows - 1 ) ) selectCell( mSelectedRow + 1, 0 ); return true; } } // Not processed, so pass to parent: return Parent::onKeyDown( event ); } //------------------------------------------------------------------------------ void ShellFancyArray::resize( const Point2I &newPos, const Point2I &newExtent ) { Parent::resize( newPos, newExtent ); if ( mFixedHorizontal && mNumColumns > 0 ) { // Fit the columns to the new width: S32 i, width = 0; S32 realWidth = newExtent.x - mGlowOffset; for ( i = 0; i < mNumColumns; i++ ) width += mColumnInfoList[i].width; if ( width == realWidth ) return; S32 delta = realWidth - width; if ( delta > 0 ) { // Just add the leftovers to the last column: mColumnInfoList[mNumColumns - 1].width += delta; if ( mColumnInfoList[mNumColumns - 1].maxWidth < mColumnInfoList[mNumColumns - 1].width ) mColumnInfoList[mNumColumns - 1].maxWidth = mColumnInfoList[mNumColumns - 1].width; } else { // Subtract width to make the columns fit, but don't make // columns smaller than their minimum widths. i = mNumColumns - 1; while ( i >= 0 ) { mColumnInfoList[i].width += delta; if ( mColumnInfoList[i].width < mColumnInfoList[i].minWidth ) { delta = mColumnInfoList[i].width - mColumnInfoList[i].minWidth; mColumnInfoList[i].width = mColumnInfoList[i].minWidth; } else break; i--; } } } } //------------------------------------------------------------------------------ void ShellFancyArray::onRenderColumnHeader( Point2I offset, RectI clipRect, S32 column, bool mouseOver ) { ColumnInfo* ci = &mColumnInfoList[column]; bool realMouseOver = mHeaderSort ? mouseOver : false; // Draw the button-like background: dglClearBitmapModulation(); RectI drawRect; drawRect.point = offset - Point2I( mGlowOffset, mGlowOffset ); U32 state = realMouseOver ? ( ( ( mColumnState == Sorting || mColumnState == Repositioning ) && mActiveColumn == column ) ? StatePressed : StateRollover ) : StateNormal; // Draw the left edge: U32 bitmap = BmpLeft * StateCount + state; dglDrawBitmapSR( mTexHeader, drawRect.point, mBmpBounds[bitmap] ); // Draw the center section: drawRect.point.x += mBmpBounds[bitmap].extent.x; drawRect.extent.x = ci->width - mBmpBounds[bitmap].extent.x - mBmpBounds[BmpRight * StateCount].extent.x + ( 2 * mGlowOffset ) - 1; drawRect.extent.y = mHeaderHeight + ( 2 * mGlowOffset ); if ( drawRect.extent.x > 0 ) { bitmap = BmpCenter * StateCount + state; dglDrawBitmapStretchSR( mTexHeader, drawRect, mBmpBounds[bitmap] ); } // Draw the right edge: drawRect.point.x += drawRect.extent.x; bitmap = BmpRight * StateCount + state; dglDrawBitmapSR( mTexHeader, drawRect.point, mBmpBounds[bitmap] ); // If this is the sort column, draw the sort arrow: bool arrowDrawn = false; if ( mHeaderSort && mTexSortArrow && ( ci->key == mSortColumnKey || ci->key == mSecondarySortColumnKey ) ) { bool flip; if ( ci->key == mSortColumnKey ) flip = mSortInc; else { dglSetBitmapModulation( ColorF( 1.0f, 1.0f, 1.0f, 0.5f ) ); flip = mSecondarySortInc; } drawRect.point.x = offset.x + ci->width - mTexSortArrow.getWidth() - 6; drawRect.point.y = ( mHeaderHeight > mTexSortArrow.getHeight() ) ? offset.y + ( ( mHeaderHeight - mTexSortArrow.getHeight() ) / 2 ): offset.y; dglDrawBitmap( mTexSortArrow, drawRect.point, ( flip ? GFlip_Y : GFlip_None ) ); arrowDrawn = true; } // Draw the text: S32 textWidth = mHeaderFont->getStrWidth( ci->name ); drawRect.point.x = offset.x + getMax( 4, ( ci->width - textWidth ) / 2 ); drawRect.point.y = offset.y + ( ( mHeaderHeight - mHeaderFont->getHeight() ) / 2 ); drawRect.extent.x = ci->width + ( offset.x - drawRect.point.x ) - ( arrowDrawn ? mTexSortArrow.getWidth() + 6 : 4 ); drawRect.extent.y = mHeaderHeight; if ( drawRect.extent.x > 0 ) { RectI textRect( drawRect ); if ( textRect.intersect( clipRect ) ) { dglSetClipRect( textRect ); dglSetBitmapModulation( realMouseOver ? mHeaderFontColorHL : mHeaderFontColor ); dglDrawText( mHeaderFont, drawRect.point, ci->name ); } } } //------------------------------------------------------------------------------ // Most likely you will want to override this function since you probably // want something drawn in the cells...left this open so you can use text, bitmaps, etc. void ShellFancyArray::onRenderCell( Point2I offset, Point2I cell, bool selected, bool mouseOver ) { ColumnInfo* ci = &mColumnInfoList[cell.x]; // If mouse is over or row is selected, draw the background: dglClearBitmapModulation(); RectI drawRect( offset, Point2I( ci->width, mRowHeight ) ); if ( selected ) dglDrawBitmapStretch( mTexCellSelected, drawRect ); else if ( mouseOver ) dglDrawBitmapStretch( mTexCellRollover, drawRect ); // Draw cell contents... // Draw the cell limit: if ( mDrawCellSeparators && mSeparatorColor ) { Point2I leftPt( offset.x, offset.y + mRowHeight ); Point2I rightPt( offset.x + ci->width, leftPt.y ); dglDrawLine( leftPt, rightPt, mSeparatorColor ); } } //------------------------------------------------------------------------------ void ShellFancyArray::onRender( Point2I offset, const RectI &updateRect, GuiControl* /*firstResponder*/ ) { RectI clipRect( updateRect ); // Draw the background field: if ( mTexLeftTop && mTexCenterTop && mTexRightTop && mTexLeftCenter && mTexCenter && mTexRightCenter && mTexLeftBottom && mTexCenterBottom && mTexRightBottom ) { RectI drawRect; dglClearBitmapModulation(); U32 stretchWidth = mBounds.extent.x - mTexLeftTop.getWidth() - mTexRightTop.getWidth() - mGlowOffset; U32 topEdgeHeight = mBounds.extent.y - mHeaderHeight - mGlowOffset - mTexLeftBottom.getHeight(); if ( topEdgeHeight > mTexLeftTop.getHeight() ) topEdgeHeight = mTexLeftTop.getHeight(); // Draw upper left corner: drawRect.point = offset + Point2I( mGlowOffset, mHeaderHeight + mGlowOffset ); drawRect.extent.x = mTexLeftTop.getWidth(); drawRect.extent.y = topEdgeHeight; dglDrawBitmapStretch( mTexLeftTop, drawRect ); // Draw upper center edge: drawRect.point.x += drawRect.extent.x; drawRect.extent.x = stretchWidth; if ( drawRect.extent.x > 0 ) dglDrawBitmapStretch( mTexCenterTop, drawRect ); // Draw upper right corner: drawRect.point.x += drawRect.extent.x; drawRect.extent.x = mTexRightTop.getWidth(); dglDrawBitmapStretch( mTexRightTop, drawRect ); drawRect.point.x = offset.x + mGlowOffset; drawRect.point.y += drawRect.extent.y; drawRect.extent.y = mBounds.extent.y - mHeaderHeight - mGlowOffset - topEdgeHeight - mTexLeftBottom.getHeight(); if ( drawRect.extent.y > 0 ) { // Draw center left edge: drawRect.extent.x = mTexLeftCenter.getWidth(); dglDrawBitmapStretch( mTexLeftCenter, drawRect ); // Draw center: drawRect.point.x += drawRect.extent.x; drawRect.extent.x = stretchWidth; if ( drawRect.extent.x > 0 ) dglDrawBitmapStretch( mTexCenter, drawRect ); // Draw center right edge: drawRect.point.x += drawRect.extent.x; drawRect.extent.x = mTexRightCenter.getWidth(); dglDrawBitmapStretch( mTexRightCenter, drawRect ); } // Draw bottom left corner: drawRect.point.x = offset.x + mGlowOffset; drawRect.point.y += drawRect.extent.y; dglDrawBitmap( mTexLeftBottom, drawRect.point ); // Draw bottom center edge: drawRect.point.x += mTexLeftBottom.getWidth(); drawRect.extent.x = stretchWidth; drawRect.extent.y = mTexCenterBottom.getHeight(); if ( drawRect.extent.x > 0 ) dglDrawBitmapStretch( mTexCenterBottom, drawRect ); // Draw bottom right corner: drawRect.point.x += drawRect.extent.x; dglDrawBitmap( mTexRightBottom, drawRect.point ); } // First draw the non-scrollable columns: S32 columnLimit = ( mNumColumns > mStartScrollRgn.x ) ? mStartScrollRgn.x : mNumColumns; S32 colStartPos_x = offset.x + mGlowOffset; S32 colStartPos_y = offset.y + mGlowOffset; RectI rowScrollRect; bool hasRowScrollRect = getRowScrollViewRect( rowScrollRect ); rowScrollRect.point += offset; S32 column; for ( column = 0; column < columnLimit; column++ ) { dglSetClipRect( clipRect ); drawColumn( Point2I( colStartPos_x, colStartPos_y ), clipRect, hasRowScrollRect, rowScrollRect, column ); colStartPos_x += mColumnInfoList[column].width; } // Now draw the columns within the scroll range: RectI columnScrollRect; if ( getColumnScrollViewRect( columnScrollRect ) ) { columnScrollRect.point += offset; colStartPos_x = columnScrollRect.point.x + mScrollPanePos.x; if ( columnScrollRect.intersect( clipRect ) ) { for ( column = mStartScrollRgn.x; column < mColumnInfoList.size(); column++ ) { dglSetClipRect( columnScrollRect ); drawColumn( Point2I( colStartPos_x, colStartPos_y ), columnScrollRect, hasRowScrollRect, rowScrollRect, column ); colStartPos_x += mColumnInfoList[column].width; } } } } //------------------------------------------------------------------------------ S32 ShellFancyArray::findColumn( S32 key ) { for ( S32 col = 0; col < mNumColumns; col++ ) { if ( mColumnInfoList[col].key == key ) return( col ); } return( -1 ); } //------------------------------------------------------------------------------ void ShellFancyArray::drawColumn( Point2I offset, RectI clipRect, bool hasRowScrollRect, RectI rowScrollRect, S32 column ) { S32 colStartPos_x = offset.x; S32 colEndPos_x = offset.x + mColumnInfoList[column].width; // Draw the header first: RectI headerRect( colStartPos_x - mGlowOffset, offset.y - mGlowOffset, mColumnInfoList[column].width + ( 2 * mGlowOffset ) - 1, mHeaderHeight + ( 2 * mGlowOffset ) ); if ( headerRect.intersect( clipRect ) ) { dglSetClipRect( headerRect ); bool mouseOver = ( ( mMouseOverRow == -1 ) && ( mMouseOverColumn == column ) ); onRenderColumnHeader( Point2I( colStartPos_x, offset.y ), headerRect, column, mouseOver ); } // Now any non-scrollable rows: S32 rowLimit = ( mNumRows > mStartScrollRgn.y ) ? mStartScrollRgn.y : mNumRows; S32 rowStartPos_y = offset.y + mHeaderHeight + mGlowOffset; S32 rowEndPos_y = rowStartPos_y + mRowHeight; S32 row; for ( row = 0; row < rowLimit; row++ ) { RectI rowRect( colStartPos_x, rowStartPos_y, colEndPos_x - colStartPos_x - 1, rowEndPos_y - rowStartPos_y ); if ( rowRect.intersect( clipRect ) ) { dglSetClipRect( rowRect ); onRenderCell( Point2I( colStartPos_x, rowStartPos_y ), Point2I( column, row ), ( row == mSelectedRow ), ( row == mMouseOverRow ) ); } rowStartPos_y += mRowHeight; rowEndPos_y += mRowHeight; } // Finally, draw all of the rows in the scroll range: if ( hasRowScrollRect ) { RectI scrollRect = rowScrollRect; if ( scrollRect.intersect( clipRect ) ) { dglSetClipRect( scrollRect ); rowStartPos_y = mScrollPanePos.y + rowScrollRect.point.y; rowEndPos_y = rowStartPos_y + mRowHeight; for ( row = mStartScrollRgn.y; row < mNumRows; row++ ) { RectI cellRect( colStartPos_x, rowStartPos_y, colEndPos_x - colStartPos_x - 1, rowEndPos_y - rowStartPos_y ); if ( cellRect.intersect( scrollRect ) ) { dglSetClipRect( cellRect ); onRenderCell( Point2I( colStartPos_x, rowStartPos_y ), Point2I( column, row ), ( row == mSelectedRow ), ( row == mMouseOverRow ) ); } rowStartPos_y += mRowHeight; rowEndPos_y += mRowHeight; } } } // Draw the column separators: if ( mDrawCellSeparators && mSeparatorColor ) { dglSetClipRect( clipRect ); Point2I lineTop( offset.x + mColumnInfoList[column].width - 1, offset.y + mHeaderHeight ); Point2I lineBottom( lineTop.x, lineTop.y + mBounds.extent.y ); dglDrawLine( lineTop, lineBottom, mSeparatorColor ); } } //------------------------------------------------------------------------------ void ShellFancyArray::determineRepositionColumn( Point2I pt ) { RectI columnScrollRect; getColumnScrollViewRect( columnScrollRect ); S32 startPos = columnScrollRect.point.x + mScrollPanePos.x; S32 endPos; mRepositionColumnTo = mNumColumns; for ( S32 column = mStartScrollRgn.x; column < mNumColumns; column++ ) { endPos = startPos + mColumnInfoList[column].width; if ( pt.x < endPos ) { mRepositionColumnTo = column; if ( pt.x > ( startPos + ( ( endPos - startPos ) / 2 ) ) ) { mRepositionColumnTo++; startPos = endPos; } break; } startPos = endPos; } mRepositionCursorPos.set( startPos, mHeaderHeight ); if ( mRepositionCursorPos.x < columnScrollRect.point.x ) mRepositionCursorPos.x = columnScrollRect.point.x; if ( mRepositionCursorPos.x > ( columnScrollRect.point.x + columnScrollRect.extent.x ) ) mRepositionCursorPos.x = columnScrollRect.point.x + columnScrollRect.extent.x; } //------------------------------------------------------------------------------ bool ShellFancyArray::getColumnScrollViewRect( RectI &rect ) { // Get the view rect that the columns scroll // left and right in, in local coordinates. rect.point.set( mGlowOffset, 0 ); rect.extent.y = mBounds.extent.y; if ( mStartScrollRgn.x >= mNumColumns ) return false; for ( int i = 0; i < mStartScrollRgn.x; i++ ) rect.point.x += mColumnInfoList[i].width; rect.extent.x = mBounds.extent.x - rect.point.x; if ( rect.extent.x <= 0 ) return false; return true; } //------------------------------------------------------------------------------ bool ShellFancyArray::getRowScrollViewRect( RectI &rect ) { // Get the view rect that the rows scroll // up and down in, in local coordinates. if ( mStartScrollRgn.y >= mNumRows ) return false; rect.point.x = 0; rect.point.y = mHeaderHeight + mGlowOffset + ( mRowHeight * mStartScrollRgn.y ); rect.extent.x = mBounds.extent.x; rect.extent.y = mBounds.extent.y - rect.point.y; if ( rect.extent.y <= 0 ) return false; return true; } //------------------------------------------------------------------------------ void ShellFancyArray::setMouseOverRow( S32 row ) { if ( mMouseOverRow == row ) return; mMouseOverRow = row; setUpdate(); } //------------------------------------------------------------------------------ void ShellFancyArray::setMouseOverColumn( S32 column, S32 xPos ) { if ( mMouseOverColumn == column || mMouseOverRow != -1 ) return; mMouseOverColumn = column; // Play a mouse-over noise: if ( column != -1 && mProfile->mSoundButtonOver ) { F32 pan = ( F32( xPos ) / F32( Canvas->mBounds.extent.x ) * 2.0f - 1.0f ) * 0.8f; AUDIOHANDLE handle = alxCreateSource(mProfile->mSoundButtonOver); alxSourcef( handle, AL_PAN, pan ); alxPlay( handle ); } setUpdateRegion( mBounds.point, Point2I( mBounds.extent.x, mHeaderHeight ) ); } //------------------------------------------------------------------------------ // // ShellFancyArrayScrollCtrl functions // //------------------------------------------------------------------------------ IMPLEMENT_CONOBJECT(ShellFancyArrayScrollCtrl); //------------------------------------------------------------------------------ ShellFancyArrayScrollCtrl::ShellFancyArrayScrollCtrl() : GuiControl() { mArray = NULL; mScrollView = NULL; mVirtualContent = NULL; mVSpacerBitmap = StringTable->insert( "gui/shll_vertspacer" ); mHSpacerBitmap = StringTable->insert( "gui/shll_horzspacer" ); mTexVSpacer = NULL; mTexHSpacer = NULL; mPrevArrayPos.set( 0, 0 ); mPrevArrayExtent.set( 0, 0 ); mPrevContentPos.set( 0, 0 ); mFixedHorizontal = false; } //------------------------------------------------------------------------------ void ShellFancyArrayScrollCtrl::initPersistFields() { Parent::initPersistFields(); addField( "fixedHorizontal", TypeBool, Offset( mFixedHorizontal, ShellFancyArrayScrollCtrl ) ); addField( "vertSpacerBitmap", TypeString, Offset( mVSpacerBitmap, ShellFancyArrayScrollCtrl ) ); addField( "horzSpacerBitmap", TypeString, Offset( mHSpacerBitmap, ShellFancyArrayScrollCtrl ) ); } //------------------------------------------------------------------------------ bool ShellFancyArrayScrollCtrl::onAdd() { if ( !Parent::onAdd() ) return false; // Add the scroll control: VirtualScrollCtrl* scrollView = new VirtualScrollCtrl(); scrollView->mWillFirstRespond = false; scrollView->mForceHScrollBar = ( mFixedHorizontal ? GuiScrollCtrl::ScrollBarAlwaysOff : GuiScrollCtrl::ScrollBarAlwaysOn ); scrollView->mForceVScrollBar = GuiScrollCtrl::ScrollBarAlwaysOn; scrollView->mProfile = mProfile; // Share... if ( !scrollView->registerObject() ) Con::errorf( ConsoleLogEntry::General, "Failed to add scroll control to ShellFancyArrayScrollCtrl!" ); addObject( scrollView ); mVirtualContent = scrollView->getVirtualContent(); // Add the server browser: ShellFancyArray* textArray = new ShellFancyArray(); textArray->mProfile = mProfile; // Share... if ( !textArray->registerObject() ) Con::errorf( ConsoleLogEntry::General, "Failed to add browser to ShellFancyArrayScrollCtrl!" ); addObject( textArray ); positionChildren(); return true; } //------------------------------------------------------------------------------ void ShellFancyArrayScrollCtrl::addObject( SimObject* obj ) { // Only allow one array: ShellFancyArray* array = dynamic_cast( obj ); if ( array ) { if ( mArray ) mArray->deleteObject(); Parent::addObject( obj ); mArray = array; if ( mFixedHorizontal ) mArray->setFixedHorizontal(); positionChildren(); return; } // Only allow one scroll control: VirtualScrollCtrl* scrollCtrl = dynamic_cast( obj ); if ( scrollCtrl ) { if ( mScrollView ) { mScrollView->deleteObject(); // Deleting this also deletes the virtual content: mVirtualContent = NULL; } Parent::addObject( obj ); mScrollView = scrollCtrl; mScrollView->mForceHScrollBar = ( mFixedHorizontal ? GuiScrollCtrl::ScrollBarAlwaysOff : GuiScrollCtrl::ScrollBarAlwaysOn ); positionChildren(); return; } Parent::addObject( obj ); } //------------------------------------------------------------------------------ void ShellFancyArrayScrollCtrl::removeObject( SimObject* obj ) { // Keep track of the kids, so we don't crash: ShellFancyArray* array = dynamic_cast( obj ); if ( mArray == array ) mArray = NULL; VirtualScrollCtrl* scrollCtrl = dynamic_cast( obj ); if ( mScrollView == scrollCtrl ) mScrollView = NULL; Parent::removeObject( obj ); } //------------------------------------------------------------------------------ bool ShellFancyArrayScrollCtrl::onWake() { if ( !Parent::onWake() ) return( false ); char buf[256]; if ( mVSpacerBitmap[0] ) { dSprintf( buf, sizeof( buf ), "%s.png", mVSpacerBitmap ); mTexVSpacer = TextureHandle( buf, BitmapTexture ); } if ( mHSpacerBitmap[0] ) { dSprintf( buf, sizeof( buf ), "%s.png", mHSpacerBitmap ); mTexHSpacer = TextureHandle( buf, BitmapTexture ); } // Make sure all the kids are sized and aligned correctly: positionChildren(); return( true ); } //------------------------------------------------------------------------------ void ShellFancyArrayScrollCtrl::onSleep() { Parent::onSleep(); mTexVSpacer = NULL; mTexHSpacer = NULL; } //------------------------------------------------------------------------------ void ShellFancyArrayScrollCtrl::positionChildren() { // Don't position the children until all are present: if ( !mArray || !mScrollView || !mVirtualContent ) return; S32 glowOffset = mArray->getGlowOffset(); S32 scrollWidth = mScrollView->scrollBarThickness(); mArray->mBounds.point.set( 0, 0 ); mArray->mBounds.extent.set( mBounds.extent.x - scrollWidth - glowOffset, mBounds.extent.y - glowOffset ); if ( !mFixedHorizontal ) mArray->mBounds.extent.y -= ( scrollWidth + 1 ); mArray->resize( mArray->mBounds.point, mArray->mBounds.extent ); RectI scrollRect; mArray->getScrollRect( scrollRect ); mScrollView->mBounds.point = scrollRect.point - Point2I( glowOffset, glowOffset ); mScrollView->mBounds.extent.set( mBounds.extent.x - scrollRect.point.x + glowOffset, mBounds.extent.y - scrollRect.point.y + glowOffset ); mVirtualContent->mBounds.point = mArray->getScrollPos(); mVirtualContent->mBounds.extent = mArray->getScrollExtent() + Point2I( 2, 2 ); mScrollView->resize( mScrollView->mBounds.point, mScrollView->mBounds.extent ); mArray->forceFillScrollRegion(); } //------------------------------------------------------------------------------ void ShellFancyArrayScrollCtrl::resize( const Point2I &newPos, const Point2I &newExtent ) { Parent::resize( newPos, newExtent ); // Move the kids around: positionChildren(); } //------------------------------------------------------------------------------ void ShellFancyArrayScrollCtrl::onPreRender() { if ( mScrollView && mArray && mVirtualContent ) { // First see if the browser repositioned itself: Point2I newPos = mArray->getScrollPos(); Point2I newExtent = mArray->getScrollExtent(); if ( mPrevArrayExtent != newExtent ) { mVirtualContent->mBounds.extent = newExtent + Point2I( 2, 2 ); mPrevArrayExtent = newExtent; mScrollView->resize( mScrollView->mBounds.point, mScrollView->mBounds.extent ); } if ( mPrevArrayPos != newPos ) { mVirtualContent->mBounds.point = newPos; mPrevArrayPos = newPos; mPrevContentPos = newPos; mScrollView->resize( mScrollView->mBounds.point, mScrollView->mBounds.extent ); mArray->setUpdate(); } else { newPos = mVirtualContent->mBounds.point; if ( mPrevContentPos != newPos ) { mArray->setScrollPos( newPos ); mPrevArrayPos = newPos; mPrevContentPos = newPos; mArray->setUpdate(); } } } } //------------------------------------------------------------------------------ void ShellFancyArrayScrollCtrl::onRender( Point2I offset, const RectI &updateRect, GuiControl* firstResponder ) { if ( mArray ) { Point2I drawPos; RectI clipRect; S32 glowOffset = mArray->getGlowOffset(); dglClearBitmapModulation(); if ( mTexVSpacer ) { // Draw the upper right notch: S32 fillHeight = mArray->getNoScrollHeight() - 1; drawPos.x = offset.x + mArray->mBounds.extent.x; drawPos.y = offset.y + glowOffset; clipRect.set( drawPos, Point2I( mTexVSpacer.getWidth(), fillHeight ) ); if ( clipRect.intersect( updateRect ) ) { dglSetClipRect( clipRect ); while ( fillHeight > 0 ) { dglDrawBitmap( mTexVSpacer, drawPos ); drawPos.y += mTexVSpacer.getHeight(); fillHeight -= mTexVSpacer.getHeight(); } dglSetClipRect( updateRect ); } } if ( !mFixedHorizontal && mTexHSpacer ) { // Draw the lower left notch: S32 fillWidth = mArray->getNoScrollWidth() - 1; drawPos.x = offset.x + glowOffset; drawPos.y = offset.y + mArray->mBounds.extent.y; clipRect.set( drawPos, Point2I( fillWidth, mTexHSpacer.getHeight() ) ); if ( clipRect.intersect( updateRect ) ) { dglSetClipRect( clipRect ); while ( fillWidth > 0 ) { dglDrawBitmap( mTexHSpacer, drawPos ); drawPos.x += mTexHSpacer.getWidth(); fillWidth -= mTexHSpacer.getWidth(); } dglSetClipRect( updateRect ); } } } Parent::onRender( offset, updateRect, firstResponder ); }