/*===============================
Copyright © 1998-2002 Brian Hall

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*===============================*/
#include "BlugsPrivate.h"

#pragma mark Row Data
//===============================
// One of these structs for each row in the list.
// 8 + 2*4 + 4*2 = 24 bytes per entry
typedef struct
{
	BLUID			uid;
	UInt32			identifier;
	BLPixelOffset	dyFromTop;			// Derived from height.
	UInt16			height;				// Primitive.
	UInt16			visibleHeight;		// 0 if not disclosed. Derived from height and flags
	UInt16			disclosureLevel;	// Top level is level 0. Primitive.
	UInt16			flags;				// See Blugs.h and below.
} BLRowDataRec, *BLRowDataPtr;

typedef BLRowDataRec		BLRowDataArray[1];
typedef BLRowDataArray*		BLRowDataArrayPtr;
typedef BLRowDataArray**	BLRowDataArrayHdl;


#pragma mark Column Data
//===============================
// One of these structs for each column in the list.
// 8 + 2*4 + 2*2 + 4 + 24 = 48 bytes per entry
typedef struct
{
	BLUID				uid;
	UInt32				identifier;
	BLPixelOffset		dxFromLeft;
	UInt16				width;
	UInt16				contentType;	// Tables only
	UInt32				flags;			// Blugs.h
	ControlFontStyleRec	fontStyle;
} BLColDataRec, *BLColDataPtr;

typedef BLColDataRec		BLColDataArray[1];
typedef BLColDataArray*		BLColDataArrayPtr;
typedef BLColDataArray**	BLColDataArrayHdl;


#pragma mark Macros
#define bl_RowData(list)			((BLRowDataArrayHdl)((*list)->rowData))
#define blp_RowData(list)			((BLRowDataArrayHdl)(list->rowData))
#define bl_ColData(list)			((BLColDataArrayHdl)((*list)->colData))
#define blp_ColData(list)			((BLColDataArrayHdl)(list->colData))

#define bl_NthRowDY(n,arrayH)			(((**arrayH)[n-1]).dyFromTop)
#define blp_NthRowDY(n,arrayP)			(((*arrayP)[n-1]).dyFromTop)
#define bl_NthRowHt(n,arrayH)			(((**arrayH)[n-1]).height)
#define blp_NthRowHt(n,arrayP)			(((*arrayP)[n-1]).height)
#define bl_NthRowVisHt(n,arrayH)		(((**arrayH)[n-1]).visibleHeight)
#define blp_NthRowVisHt(n,arrayP)		(((*arrayP)[n-1]).visibleHeight)
#define bl_NthRowDiscLevel(n,arrayH)	(((**arrayH)[n-1]).disclosureLevel)
#define blp_NthRowDiscLevel(n,arrayP)	(((*arrayP)[n-1]).disclosureLevel)
#define bl_NthColDX(n,arrayH)			(((**arrayH)[n-1]).dxFromLeft)
#define blp_NthColDX(n,arrayP)			(((*arrayP)[n-1]).dxFromLeft)
#define bl_NthColWd(n,arrayH)			(((**arrayH)[n-1]).width)
#define blp_NthColWd(n,arrayP)			(((*arrayP)[n-1]).width)
#define bl_NthColContentType(n,arrayH)	(((**arrayH)[n-1]).contentType)
#define blp_NthColContentType(n,arrayP)	(((*arrayP)[n-1]).contentType)
#define bl_NthRowDataAddr(n,arrayH)		(&((**arrayH)[n-1]))
#define blp_NthRowDataAddr(n,arrayP)	(&((*arrayP)[n-1]))
#define bl_NthColDataAddr(n,arrayH)		(&((**arrayH)[n-1]))
#define blp_NthColDataAddr(n,arrayP)	(&((*arrayP)[n-1]))
#define bl_NthRowFlags(n,arrayH)		(((**arrayH)[n-1]).flags)
#define blp_NthRowFlags(n,arrayP)		(((*arrayP)[n-1]).flags)
#define bl_NthRowCollapsed(n,arrayH)	bl_Test(((**arrayH)[n-1]).flags,blCollapsed)
#define blp_NthRowCollapsed(n,arrayP)	bl_Test(((*arrayP)[n-1]).flags,blCollapsed)
#define bl_NthRowUndisclosed(n,arrayH)	bl_Test(((**arrayH)[n-1]).flags,blUndisclosed)
#define blp_NthRowUndisclosed(n,arrayP)	bl_Test(((*arrayP)[n-1]).flags,blUndisclosed)
#define bl_NthColFlags(n,arrayH)		(((**arrayH)[n-1]).flags)
#define blp_NthColFlags(n,arrayP)		(((*arrayP)[n-1]).flags)
#define bl_NthColProxy(n,arrayH)		(((**arrayH)[n-1]).proxy)
#define blp_NthColProxy(n,arrayP)		(((*arrayP)[n-1]).proxy)
#define bl_NthColHasProxy(n,arrayH)		bl_Test(((**arrayH)[n-1]).flags,blColumnHasProxy)
#define blp_NthColHasProxy(n,arrayP)	bl_Test(((*arrayP)[n-1]).flags,blColumnHasProxy)

static OSErr		local_BLAddColumnsToIndex( UInt16 inCount, UInt16 inColumn, BlugsRef inList );
static void			local_BLDeleteRowsFromIndex( UInt16 inCount, UInt16 inRow, BlugsRef inList );
static void			local_BLDeleteColumnsFromIndex( UInt16 inCount, UInt16 inColumn, BlugsRef inList );
static OSErr		local_BLMoveColumnsInIndex( UInt16 inColumn, UInt16 inAfterThis,
												UInt16 inCount, BlugsRef inList );
static UInt16		local_BLIncludeDescendants( UInt16 inStartRow, UInt16 inCount, BlugsRef inList );

#pragma mark -
//===============================
BL_PROC OSErr BLGetCellFromUID( BLCellUID* inID, BLCell* outCell, BlugsRef inList )
{
	OSErr	err;
	
	outCell->row = BLGetRowFromUID( &inID->rowID, inList );
	outCell->col = BLGetColumnFromUID( &inID->colID, inList );
	if (!outCell->row || !outCell->col) err = userDataItemNotFound;
	else err = noErr;
	return err;
}

//===============================
BL_PROC UInt16 BLGetRowFromUID( BLUID* inID, BlugsRef inList )
{
	UInt16				row;
	Boolean				gotIt = false;
	BLRowDataPtr		dataP;
	UInt16				nRows;
	
	nRows = bl_NRows(inList);
	dataP = bl_NthRowDataAddr(1,bl_RowData(inList));
	for (row = 1; row <= nRows; row++, dataP++)
	{
		if (_BLEqualUID( &dataP->uid, inID ))
		{
			gotIt = true;
			break;
		}
	}
	if (!gotIt) row = 0;
	return row;
}

//===============================
BL_PROC UInt16 BLGetColumnFromUID( BLUID* inID, BlugsRef inList )
{
	UInt16				col;
	Boolean				gotIt = false;
	BLColDataPtr		dataP;
	UInt16				nCols;
	
	nCols = bl_NCols(inList);
	dataP = bl_NthColDataAddr(1,bl_ColData(inList));
	for (col = 1; col <= nCols; col++, dataP++)
	{
		if (_BLEqualUID( &dataP->uid, inID ))
		{
			gotIt = true;
			break;
		}
	}
	if (!gotIt) col = 0;
	return col;
}

//===============================
BL_PROC OSErr BLGetCellUID( BLCell inCell, BLCellUID* outID, BlugsRef inList )
{
	OSErr	err = noErr;
	
	BLGetRowUID( inCell.row, &outID->rowID, inList );
	BLGetColumnUID( inCell.col, &outID->colID, inList );
	if ((outID->colID.hi == 0 && outID->colID.lo == 0) ||
		(outID->rowID.hi == 0 && outID->rowID.lo == 0)) err = inputOutOfBounds;
	return err;
}

//===============================
BL_PROC void BLGetRowUID( UInt16 inRow, BLUID* outID, BlugsRef inList )
{
	BLUID*	id = nil;
	
	if (inRow && inRow <= bl_NRows(inList))
		id = &(bl_NthRowDataAddr(inRow,(bl_RowData(inList)))->uid);
	if (id)
	{
		outID->lo = id->lo;
		outID->hi = id->hi;
	}
	else
	{
		outID->lo = 0L;
		outID->hi = 0L;
	}
}

//===============================
BL_PROC void BLGetColumnUID( UInt16 inColumn, BLUID* outID, BlugsRef inList )
{
	BLUID*	id = nil;
	
	if (inColumn && inColumn <= bl_NCols(inList))
		id = &(bl_NthColDataAddr(inColumn,(bl_ColData(inList)))->uid);
	if (id)
	{
		outID->lo = id->lo;
		outID->hi = id->hi;
	}
	else
	{
		outID->lo = 0L;
		outID->hi = 0L;
	}
}

#pragma mark -
//===============================
BL_PROC OSErr BLSetDefaultRowFlags( UInt16 inWhichFlags, UInt16 inFlags, BlugsRef inList )
{
	OSErr		err;
	
	err = _BLValidateList( inList );
	if (!err)
		(*inList)->defaultRowFlags =
			_BLProcessFlags( inWhichFlags & blAllPublicRowFlags,
							inFlags & blAllPublicRowFlags,
							(*inList)->defaultRowFlags );
	return err;
}

//===============================
BL_PROC OSErr BLSetRowIdentifier( UInt16 inRow, UInt32 inIdentifier, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) err = _BLValidateRow( inRow, inList );
	if (!err)
	{
		BLRowDataPtr	dataP;
		
		dataP = bl_NthRowDataAddr(inRow,bl_RowData(inList));
		dataP->identifier = inIdentifier;
	}
	return err;
}

//===============================
BL_PROC OSErr BLGetRowIdentifier( UInt16 inRow, UInt32* outIdentifier, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) err = _BLValidateRow( inRow, inList );
	if (!err)
	{
		BLRowDataPtr	dataP;
		
		dataP = bl_NthRowDataAddr(inRow,bl_RowData(inList));
		*outIdentifier = dataP->identifier;
	}
	return err;
}

//===============================
BLPixelOffset _BLGetRowOffsetFromTop( UInt16 inRow, BlugsRef inList )
{
	BLRowDataPtr	dataP;
		
	dataP = bl_NthRowDataAddr(inRow,bl_RowData(inList));
	return (dataP) ? dataP->dyFromTop : 0;
}

//===============================
void _BLSetRowOffsetFromTop( UInt16 inRow, BLPixelOffset inOffset, BlugsRef inList )
{
	BLRowDataPtr	dataP;
		
	dataP = bl_NthRowDataAddr(inRow,bl_RowData(inList));
	if (dataP) dataP->dyFromTop = inOffset;
}

//===============================
UInt16 _BLGetRowDisclosureLevel( UInt16 inRow, BlugsRef inList )
{
	BLRowDataPtr	dataP;
		
	dataP = bl_NthRowDataAddr(inRow,bl_RowData(inList));
	return (dataP) ? dataP->disclosureLevel : 0;
}

//===============================
void _BLSetRowDisclosureLevel( UInt16 inRow, UInt16 inLevel, BlugsRef inList )
{
	BLRowDataPtr	dataP;
		
	dataP = bl_NthRowDataAddr(inRow,bl_RowData(inList));
	if (dataP) dataP->disclosureLevel = inLevel;
}

//===============================
UInt16 _BLGetRowHeight( UInt16 inRow, BlugsRef inList )
{
	OSErr	err;
	UInt16	height;
	
	err = _BLValidateRow( inRow, inList );
	if (err) height = 0;
	else height = bl_NthRowDataAddr(inRow,bl_RowData(inList))->height;
	return height;
}

//===============================
void _BLSetRowHeight( UInt16 inRow, UInt16 inHeight, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateRow( inRow, inList );
	if (!err) bl_NthRowDataAddr(inRow,bl_RowData(inList))->height = inHeight;
}

//===============================
UInt16 _BLGetRowVisibleHeight( UInt16 inRow, BlugsRef inList )
{
	OSErr	err;
	UInt16	height;
	
	err = _BLValidateRow( inRow, inList );
	if (err) height = 0;
	else height = bl_NthRowDataAddr(inRow,bl_RowData(inList))->visibleHeight;
	return height;
}

//===============================
void _BLSetRowVisibleHeight( UInt16 inRow, UInt16 inHeight, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateRow( inRow, inList );
	if (!err) bl_NthRowDataAddr(inRow,bl_RowData(inList))->visibleHeight = inHeight;
}

//===============================
BL_PROC UInt16 BLGetRowFlags( UInt16 inRow, BlugsRef inList )
{
	OSErr		err;
	UInt16		flags = 0;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		err = _BLValidateRow( inRow, inList );
		if (!err) flags = ((bl_NthRowDataAddr(inRow,bl_RowData(inList)))->flags) & blAllPublicRowFlags;
	}
	return flags;
}

//===============================
BL_PROC OSErr BLSetRowFlags( UInt16 inRow, UInt16 inWhichFlags, UInt16 inFlags, BlugsRef inList )
{
	OSErr		err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		err = _BLValidateRow( inRow, inList );
		if (!err)
		{
			BLRowDataPtr	data;
			Boolean			needUpdate = false;
			
			data = bl_NthRowDataAddr(inRow,bl_RowData(inList));
			// See if altering the blRowHasChildren flag.
			if (bl_Test(inWhichFlags,blRowHasChildren) && bl_Disclosure(inList))
			{
				if (bl_Test(inFlags,blRowHasChildren))
				{
					if (!bl_Test(data->flags,blRowHasChildren))
					{
						bl_Set(data->flags,blRowHasChildren);
						needUpdate = true;
					}
				}
				else // Set to no children
				{
					// Make sure there aren't any actual children
					if (!_BLCountDescendants( inRow, inList ))
					{
						if (bl_Test(data->flags,blRowHasChildren))
						{
							bl_Clear(data->flags,blRowHasChildren);
							needUpdate = true;
						}
					}
				}
			}
			// See if altering the blRowIsExpanded flag.
			if (bl_Test(inWhichFlags,blRowIsExpanded) && bl_Disclosure(inList))
			{
				if (bl_Test(inFlags,blRowIsExpanded)) BLExpandRow( false, inRow, inList );
				else BLCollapseRow( false, inRow, inList );
				// Recalc row data pointer, since expanding or collapsing may have moved memory.
				data = bl_NthRowDataAddr(inRow,bl_RowData(inList));
			}
			// See if altering the blRowIsTitleRow flag.
			if (bl_Test(inWhichFlags,blRowIsTitleRow))
			{
				if (bl_Test(inFlags,blRowIsTitleRow)) bl_Set(data->flags,blRowIsTitleRow);
				else bl_Clear(data->flags,blRowIsTitleRow);
				needUpdate = true;
			}
			// See if altering the blRowMarkedForMovement flag.
			if (bl_Test(inWhichFlags,blRowMarkedForMovement))
			{
				if (bl_Test(inFlags,blRowMarkedForMovement)) bl_Set(data->flags,blRowMarkedForMovement);
				else bl_Clear(data->flags,blRowMarkedForMovement);
			}
			// See if altering the blRowSelectAll flag.
			if (bl_Test(inWhichFlags,blRowSelectAll))
			{
				if (bl_Test(inFlags,blRowSelectAll)) bl_Set(data->flags,blRowSelectAll);
				else bl_Clear(data->flags,blRowSelectAll);
			}
			if (needUpdate)
			{
				BLCell		cell;
				
				cell.col = 1;
				cell.row = inRow;
				_BLUpdateCell( blUpdateBlit | blUpdateTriangle, cell, inList );
			}
		}
	} // if valid list
	return err;
}

//===============================
UInt16 _BLGetRowFlags( UInt16 inRow, BlugsRef inList )
{
	BLRowDataPtr	dataP;
		
	dataP = bl_NthRowDataAddr(inRow,bl_RowData(inList));
	return (dataP) ? dataP->flags : 0;
}

//===============================
void _BLSetRowFlags( UInt16 inRow, UInt16 inWhichFlags, UInt16 inFlags, BlugsRef inList )
{
	BLRowDataPtr	data;
	
	data = bl_NthRowDataAddr(inRow,bl_RowData(inList));
	data->flags = _BLProcessFlags( inWhichFlags, inFlags, data->flags );
}

#pragma mark -
//===============================
BL_PROC OSErr BLSetDefaultColumnFlags( UInt16 inWhichFlags, UInt16 inFlags, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) (*inList)->defaultColumnFlags =
				_BLProcessFlags( inWhichFlags & blAllPublicColumnFlags,
									inFlags & blAllPublicColumnFlags,
									(*inList)->defaultColumnFlags );
	return err;
}

//===============================
BL_PROC OSErr BLSetColumnIdentifier( UInt16 inColumn, UInt32 inIdentifier, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) err = _BLValidateColumn( inColumn, inList );
	if (!err)
	{
		BLColDataPtr	dataP;
		
		dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
		dataP->identifier = inIdentifier;
	}
	return err;
}

//===============================
BL_PROC OSErr BLGetColumnIdentifier( UInt16 inColumn, UInt32* outIdentifier, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err) err = _BLValidateColumn( inColumn, inList );
	if (!err)
	{
		BLColDataPtr	dataP;
		
		dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
		*outIdentifier = dataP->identifier;
	}
	return err;
}

//===============================
BLPixelOffset _BLGetColumnOffsetFromLeft( UInt16 inColumn, BlugsRef inList )
{
	BLColDataPtr	dataP;
		
	dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	return (dataP) ? dataP->dxFromLeft : 0;
}

//===============================
void _BLSetColumnOffsetFromLeft( UInt16 inColumn, BLPixelOffset inOffset, BlugsRef inList )
{
	BLColDataPtr	dataP;
		
	dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	if (dataP) dataP->dxFromLeft = inOffset;
}

//===============================
BLContentType _BLGetColumnContentType( UInt16 inColumn, BlugsRef inList )
{
	BLColDataPtr	dataP;
		
	dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	return (dataP) ? dataP->contentType : 0;
}

//===============================
void _BLSetColumnContentType( UInt16 inColumn, BLContentType inType, BlugsRef inList )
{
	BLColDataPtr	dataP;
		
	dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	if (dataP) dataP->contentType = inType;
}

//===============================
void _BLGetColumnFontStyle( UInt16 inColumn, ControlFontStyleRec* outFontStyle, BlugsRef inList )
{
	BLColDataPtr	dataP;
		
	dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	if (dataP) BlockMoveData( &dataP->fontStyle, outFontStyle, sizeof(ControlFontStyleRec) );
}

//===============================
void _BLSetColumnFontStyle( UInt16 inColumn, const ControlFontStyleRec* inFontStyle, BlugsRef inList )
{
	BLColDataPtr	dataP;
		
	dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	if (dataP) BlockMoveData( inFontStyle, &dataP->fontStyle, sizeof(ControlFontStyleRec) );
}

//===============================
BL_PROC UInt16 BLGetColumnFlags( UInt16 inColumn, BlugsRef inList )
{
	OSErr		err;
	UInt16		flags = 0;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		err = _BLValidateColumn( inColumn, inList );
		if (!err) flags = ((bl_NthColDataAddr(inColumn,bl_ColData(inList)))->flags) & blAllPublicColumnFlags;
	}
	return flags;
}

//===============================
BL_PROC OSErr BLSetColumnFlags( UInt16 inColumn, UInt16 inWhichFlags, UInt16 inFlags, BlugsRef inList )
{
	OSErr		err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		err = _BLValidateColumn( inColumn, inList );
		if (!err)
		{
			BLColDataPtr	data;
			
			data = bl_NthColDataAddr(inColumn,bl_ColData(inList));
			if (bl_Test(inWhichFlags,blColumnCantSelect))
			{
				if (bl_Test(inFlags,blColumnCantSelect))
				{
					if (!bl_Test(data->flags,blColumnCantSelect))
					{
						bl_Set(data->flags,blColumnCantSelect);
						_BLDeselectColumn( true, inColumn, inList );
					}
				}
				else bl_Clear(data->flags,blColumnCantSelect);
			}
		}
	} // if valid list
	return err;
}

//===============================
UInt32 _BLGetColumnFlags( UInt16 inColumn, BlugsRef inList )
{
	BLColDataPtr	dataP;
		
	dataP = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	return (dataP) ? dataP->flags : 0;
}

//===============================
void _BLSetColumnFlags( UInt16 inColumn, UInt32 inWhichFlags, UInt32 inFlags, BlugsRef inList )
{
	BLColDataPtr	data;
	
	data = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	data->flags = _BLProcessFlags( inWhichFlags, inFlags, data->flags );
}

//===============================
UInt16 _BLGetColumnWidth( UInt16 inColumn, BlugsRef inList )
{
	OSErr	err;
	UInt16	width;
	
	err = _BLValidateColumn( inColumn, inList );
	if (err) width = 0;
	else width = bl_NthColDataAddr(inColumn,bl_ColData(inList))->width;
	return width;
}

//===============================
void _BLSetColumnWidth( UInt16 inColumn, UInt16 inWidth, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateColumn( inColumn, inList );
	if (!err && inWidth) bl_NthColDataAddr(inColumn,bl_ColData(inList))->width = inWidth;
}


#pragma mark -
//===============================
OSErr _BLAllocateRowData( UInt16 inRows, BlugsRef inList )
{
	BLRowDataArrayHdl	rowData;
	OSErr				err = noErr;
	
	rowData = (BLRowDataArrayHdl)_VANew( inRows, sizeof(BLRowDataRec), true );
	if (!rowData) err = memFullErr;
	else
	{
		bl_RowData(inList) = rowData;
		_BLInitRowData( 1, inRows, inList );
	}
	return err;
}

//===============================
OSErr _BLAllocateColumnData( UInt16 inColumns, BlugsRef inList )
{
	BLColDataArrayHdl	colData;
	OSErr				err = noErr;
	
	colData = (BLColDataArrayHdl)_VANew( inColumns, sizeof(BLColDataRec), true );
	if (!colData) err = memFullErr;
	else
	{
		bl_ColData(inList) = colData;
		_BLInitColumnData( 1, inColumns, inList );		
	}
	return err;
}

//===============================
// Initialize array of row data elements
void _BLInitRowData( UInt16 inRow, UInt16 inCount, BlugsRef inList )
{
	BLRowDataPtr		rowDataPtr;
	UInt16				height, i;
	
	height = (*inList)->defaultCellSize.v;
	rowDataPtr = bl_NthRowDataAddr(inRow,bl_RowData(inList));
	for (i = 1; i <= inCount; i++, rowDataPtr++)
	{
		rowDataPtr->uid = (*inList)->nextRowID;
		_BLIncrUID( &(*inList)->nextRowID );
		rowDataPtr->height = height;
		rowDataPtr->flags = (*inList)->defaultRowFlags;
		//if (bl_DrawRowBorders(inList)) bl_Set(rowDataPtr->flags,blRowDrawBorder);
	}
}

//===============================
// Initialize array of column data elements
void _BLInitColumnData( UInt16 inColumn, UInt16 inCount, BlugsRef inList )
{
	BLColDataPtr		colDataPtr;
	UInt16				width, i;
	
	width = (*inList)->defaultCellSize.h;
	colDataPtr = bl_NthColDataAddr(inColumn,bl_ColData(inList));
	for (i = 1; i <= inCount; i++, colDataPtr++)
	{
		colDataPtr->uid = (*inList)->nextColID;
		_BLIncrUID( &(*inList)->nextColID );
		colDataPtr->width = width;
		colDataPtr->fontStyle.flags = kControlUseFontMask | kControlUseSizeMask;
		colDataPtr->fontStyle.font = 1;
		colDataPtr->fontStyle.size = 9;
		colDataPtr->flags = (*inList)->defaultColumnFlags;
		//if (bl_DrawColBorders(inList)) bl_Set(colDataPtr->flags,blColumnDrawBorder);
	}
}


#pragma mark -
//===============================
BL_PROC UInt16 BLCountRows( BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	return (err) ? 0 : bl_NRows(inList);
}

//===============================
BL_PROC UInt16 BLCountColumns( BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	return (err) ? 0 : bl_NCols(inList);
}

//===============================
// The option param is used only in disclosure lists.
BL_PROC OSErr BLAddRows( BLDisclosureOption inOption, UInt16 inCount, UInt16 inRow, BlugsRef inList )
{
	OSErr		err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		if (!inRow)
		{
			err = inputOutOfBounds;
			_AssertStr(false,"Error: tried to add rows at row #0.");
		}
		else
		{
			if (inCount)
			{
				UInt16				nCols, nRows, totalAddedRows;
				BLRowDataPtr		dataP;
				BLRowDataArrayHdl	arrayH;
				BLTitleBarHdl		theBar;
				Handle				index;
				Boolean				table, disclosure;
				
				nCols = bl_NCols(inList);
				nRows = bl_NRows(inList);
				arrayH = bl_RowData(inList);
				index = bl_Index(inList);
				theBar = bl_VTitle(inList);
				table = bl_Table(inList);
				disclosure = bl_Disclosure(inList);
				if (inRow - 1 > nRows)
				{
					totalAddedRows = inRow - nRows + inCount - 1;
					inRow = nRows + 1;
				}
				else totalAddedRows = inCount;
				if (0xFFFF - totalAddedRows < nRows) err = inputOutOfBounds;
				else
				{
					// Add rows to the index and zero them
					err = _VAAdd( inCount, inRow, nRows, nCols * sizeof(BLIndexRec), index, true );
					// Add rows to the row data array
					if (!err)
					{
						err = _VAAdd( inCount, inRow, nRows, sizeof(BLRowDataRec), (Handle)arrayH, true );
						if (err)
						{
							// Remove the added index entries
							_VARemove( totalAddedRows, inRow, nRows + totalAddedRows, nCols * sizeof(BLIndexRec),
										index );
						}
						else
						{
							// If there is a vertical title bar, add titles to it
							if (theBar)
							{
								err = _BLAddTitles( totalAddedRows, inRow, theBar, inList );
								if (err)
								{
									// Remove the added index entries
									_VARemove( totalAddedRows, inRow, nRows + totalAddedRows, nCols * sizeof(BLIndexRec),
											index );
									// Remove the added row data entries
									_VARemove( totalAddedRows, inRow, nRows + totalAddedRows, sizeof(BLRowDataRec),
											(Handle)arrayH );
								}
							}
							if (!err)
							{
								_BLInitRowData( inRow, totalAddedRows, inList );
								bl_NRows(inList) += totalAddedRows;
								// If it's a table list, the handler is set already, so we need to allow it to initialize
								//	the added cells.
								if (table)
								{
									BLCell		cell;
									
									for (cell.row = inRow; cell.row < inRow + totalAddedRows; cell.row++)
									{
										for (cell.col = 1; cell.col <= nCols; cell.col++)
										{
											BLHandlerParamBlock	pb;
											
											_BLCallHandlerForCell( blCellInitMsg, cell, &pb, inList );
										}
									}
								}
								// Adjust disclosure information
								if (disclosure)
								{
									// Disclosure option is irrelevant for row 1
									if (inRow > 1)
									{
										UInt16	newLevel = 0, i;
										
										dataP = bl_NthRowDataAddr(inRow - 1,arrayH);
										if (inOption == blDisclosureOptionSame) newLevel = dataP->disclosureLevel;
										else if (inOption == blDisclosureOptionChild)
										{
											newLevel = dataP->disclosureLevel + 1;
											// If the previous row wasn't a parent, it is now
											bl_Set(dataP->flags,blRowHasChildren);
											bl_Set(dataP->flags,blRowIsExpanded);
										}
										if (newLevel)
										{
											for (i = 0; i < totalAddedRows; i++)
											{
												dataP++;
												dataP->disclosureLevel = newLevel;
											}
										}
									}
								}
								// Update the visual side of the list
								_BLRecalc( blRecalcRowOffsets | blRecalcVisCells | blRecalcVScroll | blRecalcThumb, inRow, inList );
								_BLUpdateWorld( blUpdateBlit, nil, inList );
							}
						}
					}
				}
			} // Count > 0
		}
	}
	return err;
}

//===============================
BL_PROC OSErr BLAddColumns( UInt16 inCount, UInt16 inColumn, BlugsRef inList )
{
	OSErr		err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		_AssertStr( inColumn, "Error: tried to add columns at column #0." );
		if (!inColumn) err = inputOutOfBounds;
		else
		{
			if (inCount)
			{
				UInt16				nCols, nRows, totalAddedCols/*, pixels*/;
				BLColDataArrayHdl	arrayH;
				BLTitleBarHdl		theBar;
				Boolean				table;
				
				nCols = bl_NCols(inList);
				nRows = bl_NRows(inList);
				arrayH = bl_ColData(inList);
				table = bl_Table(inList);
				theBar = bl_HTitle(inList);
				if (inColumn - 1 > nCols)
				{
					totalAddedCols = inColumn - nCols + inCount - 1;
					inColumn = nCols + 1;
				}
				else totalAddedCols = inCount;
				if (0xFFFF - totalAddedCols < nCols) err = inputOutOfBounds;
				else
				{
					// Need only move stuff if there are index records there already
					if (nRows) err = local_BLAddColumnsToIndex( inCount, inColumn, inList );
					if (!err)
					{
						// Add entries to col data array
						err = _VAAdd( inCount, inColumn, nCols, sizeof(BLColDataRec), (Handle)arrayH, true );
						if (err) local_BLDeleteColumnsFromIndex( totalAddedCols, inColumn, inList );
						else
						{
							// If there is a horizontal title bar, add titles to it
							if (theBar) err = _BLAddTitles( totalAddedCols, inColumn, theBar, inList );
							if (err)
							{
								local_BLDeleteColumnsFromIndex( totalAddedCols, inColumn, inList );
								_VARemove( totalAddedCols, inColumn, nCols + totalAddedCols, sizeof(BLColDataRec), (Handle)arrayH );
							}
							else
							{
								_BLInitColumnData( inColumn, inCount, inList );
								// Update list to reflect addition
								bl_NCols(inList) += totalAddedCols;
								// If it's a table list, the handler is set already, so we need to allow it to initialize
								//	the added cells.
								if (table)
								{
									BLCell	cell;
									
									for (cell.row = 1; cell.row <= nRows; cell.row++)
									{
										for (cell.col = inColumn; cell.col < inColumn + totalAddedCols; cell.col++)
										{
											BLHandlerParamBlock	pb;
											
											_BLCallHandlerForCell( blCellInitMsg, cell, &pb, inList );
										}
									}
								}
								_BLRecalc( blRecalcColOffsets | blRecalcVisCells | blRecalcHScroll | blRecalcThumb | blRecalcRightPin, inColumn, inList );
								_BLUpdateWorld( blUpdateBlit, nil, inList );
							}
						}
					}
				}
			}
		}
	}
	return err;
}

//===============================
// The way in which stuff is copied in this routine probably makes it more efficient
//	than doing it with repeated _VAAdd calls.
static OSErr local_BLAddColumnsToIndex( UInt16 inCount, UInt16 inColumn, BlugsRef inList )
{
	Handle		indexH;
	UInt16		nRows, nCols;
	UInt32		neededSize;
	OSErr		err;
	
	nRows = bl_NRows(inList);
	nCols = bl_NCols(inList);
	indexH = bl_Index(inList);
	// Make room for new entries
	neededSize = (nCols + inCount) * nRows * sizeof(BLIndexRec);
	SetHandleSize( indexH, neededSize );
	err = MemError();
	if (!err)
	{
		// No need to mess with nonexistent memory
		if (neededSize)
		{
			UInt16		row, entriesBelow;
			BLIndexPtr	indexBase, source, dest;
			
			entriesBelow = inColumn - 1;
			indexBase = (BLIndexPtr)*indexH;
			if (nCols)
			{
				UInt32		copySize;
				UInt16		entriesAbove;
				
				entriesAbove = nCols - inColumn + 1;
				// Move to beginning of last row
				source = indexBase + ((nRows - 1) * nCols) + entriesBelow;
				dest = source + nRows * inCount;
				// Copy entries above for last row
				if (entriesAbove) BlockMoveData( (Ptr)source, (Ptr)dest, entriesAbove * sizeof(BLIndexRec) );
				source -= nCols;
				dest -= nCols + inCount;
				// Copy intermediate rows
				copySize = nCols * sizeof(BLIndexRec);
				for (row = nRows - 1; row > 0; row--, source -= nCols, dest -= nCols + inCount)
					BlockMoveData( (Ptr)source, (Ptr)dest, copySize );
			}
			// Zero the added records
			source = indexBase + entriesBelow;
			for (row = 1; row <= nRows; row++, source += nCols + inCount)
			{
				UInt16	i;
				
				dest = source;	// First added record in row
				for (i = 1; i <= inCount; i++, dest++)
				{
					// CAREFUL! If we add to the the index rec structure, this has to reflect that!
					dest->data = nil;
					dest->content = 0;
					dest->flags = 0;
				}
			}
		}
	}
	return err;
}

//===============================
// If count is 0, delete all.
// In a disclosure list, we delete rows and their descendants until the number of deleted
//	rows is equal to or greater than the count parameter.
BL_PROC OSErr BLDeleteRows( UInt16 inCount, UInt16 inRow, BlugsRef inList )
{
	OSErr		err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		UInt16		nRows;
		
		nRows = bl_NRows(inList);
		// No need to do anything if there are no rows.
		if (nRows)
		{
			err = _BLValidateRow( inRow, inList );
			if (!err)
			{
				UInt16				nCols, entriesBelow, i;
				UInt32				deletedHt = 0;
				BLRowDataArrayHdl	arrayH;
				BLRowDataPtr		dataP;
				BLTitleBarHdl		vTitle;
				
				nCols = bl_NCols(inList);
				vTitle = bl_VTitle(inList);
				// If we are deleting everything, make sure nthRow and count reflect this.
				if (!inCount)
				{
					inCount = nRows;
					inRow = 1;
				}
				else if (bl_Disclosure(inList))
					inCount = local_BLIncludeDescendants( inRow, inCount, inList );
				// If trying to delete more than the rest after nthColumn, delete rest.
				if (inRow + inCount - 1 > nRows) inCount = nRows - inRow + 1;
				// If the inline edit cell is being deleted, end the inline edit.
				// Also, update inline cell if necessary.
				if (bl_InlineNow(inList))
				{
					BLCell	inlineCell;
					
					err = BLGetInlineEditCell( &inlineCell, inList );
					if (!err && inlineCell.row >= inRow)
					{
						if (inlineCell.row <= inRow + inCount - 1)
						{
							_BLEndInlineEdit( false, inList );
							(void)BLSetFocusedPart( kControlListBoxPart, inList );
						}
					}
					err = noErr;
				}
				local_BLDeleteRowsFromIndex( inCount, inRow, inList );
				entriesBelow = inRow - 1;
				arrayH = bl_RowData(inList);
				dataP = (BLRowDataPtr)bl_NthRowDataAddr(inRow,arrayH);
				// Add up the deleted height
				for (i = inRow; i < inRow + inCount; i++)
				{
					deletedHt += dataP->height;
					dataP++;
				}
				_VARemove( inCount, inRow, nRows, sizeof(BLRowDataRec), (Handle)arrayH );
				// Delete titles if the list has a vertical title bar
				if (vTitle) _BLDisposeTitles( inCount, inRow, vTitle, inList );
				bl_NRows(inList) -= inCount;
				// Adjust scroll location
				// If the top row is physically below the rows left intact at the beginning of the list...
				if (bl_TVisRow(inList) > entriesBelow)
				{
					// If all the deleted stuff is 'above' the view rectangle then the scroll location
					//	is just decremented by that amount
					if (bl_TVisRow(inList) > entriesBelow + inCount) bl_VScrollLoc(inList) -= deletedHt;
					// Otherwise our view is just below the 'undisturbed' portion of the list.
					else bl_VScrollLoc(inList) = _BLRowBottom( entriesBelow, inList );
				}
				// Otherwise, deleted material was below the top vis cell and the scroll loc stays the same.
				_BLRecalc( blRecalcRowOffsets | blRecalcVisCells | blRecalcVScroll |
							blRecalcThumb | blRecalcRightPin, inRow, inList );
				_BLUpdateWorld( blUpdateBlit | blUpdateCorners, nil, inList );
			} // if valid row
		} // if nRows > 0
	} // if valid list
	return err;
}

//===============================
BL_PROC OSErr BLDeleteColumns( UInt16 inCount, UInt16 inColumn, BlugsRef inList )
{
	OSErr		err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		UInt16	nCols;
		
		nCols = bl_NCols(inList);
		// No need to do anything if there are no columns.
		if (nCols)
		{
			err = _BLValidateColumn( inColumn, inList );
			if (!err)
			{
				UInt16				entriesBelow, i;
				UInt32				deletedWd = 0;
				BLColDataPtr		dataP;
				BLColDataArrayHdl	arrayH;
				BLTitleBarHdl		theBar;
				
				if (!inCount)
				{
					inCount = nCols;
					inColumn = 1;
				}
				// If trying to delete more than the rest after nthColumn, delete rest.
				if (inColumn + inCount - 1 > nCols) inCount = nCols - inColumn + 1;
				// If the inline edit cell is being deleted, end the inline edit.
				if (bl_InlineNow(inList))
				{
					BLCell	inlineCell;
					
					err = BLGetInlineEditCell( &inlineCell, inList );
					if (!err && inlineCell.col >= inColumn)
					{
						if (inlineCell.col <= inColumn + inCount - 1)
						{
							_BLEndInlineEdit( true, inList );
							(void)BLSetFocusedPart( kControlListBoxPart, inList );
						}
					}
					err = noErr;
				}
				local_BLDeleteColumnsFromIndex( inCount, inColumn, inList );
				entriesBelow = inColumn - 1;
				arrayH = bl_ColData(inList);
				dataP = (BLColDataPtr)bl_NthColDataAddr(inColumn,arrayH);
				// Add up the deleted width
				for (i = inColumn; i < inColumn + inCount; i++)
				{
					deletedWd += dataP->width;
					dataP++;
				}
				_VARemove( inCount, inColumn, nCols, sizeof(BLColDataRec), (Handle)arrayH );
				theBar = bl_HTitle(inList);
				if (theBar) _BLDisposeTitles( inCount, inColumn, theBar, inList );
				bl_NCols(inList) -= inCount;
				// Adjust scroll location
				if (bl_LVisCol(inList) > entriesBelow)
				{
					if (bl_LVisCol(inList) > entriesBelow + inCount) bl_HScrollLoc(inList) -= deletedWd;
					else bl_HScrollLoc(inList) = _BLColumnRight( entriesBelow, inList );
				}
				// Reset the disclosure colum if needed.
				if ((*inList)->disclosureColumn > bl_NCols(inList))
					(*inList)->disclosureColumn = 1;
				_BLRecalc( blRecalcColOffsets | blRecalcVisCells | blRecalcHScroll | blRecalcThumb, inColumn, inList );
				_BLUpdateWorld( blUpdateBlit | blUpdateCorners, nil, inList );
			} // if valid column
		} // if nCols > 0
	} // if valid list
	return err;
}

//===============================
static void local_BLDeleteRowsFromIndex( UInt16 inCount, UInt16 inRow, BlugsRef inList )
{
	UInt16		entriesBelow, entriesDeleted, nRows, nCols, i;
	UInt32		itemSize;
	
	nRows = bl_NRows(inList);
	nCols = bl_NCols(inList);
	// The size of the array items
	itemSize = nCols * sizeof(BLIndexRec);
	entriesBelow = (inRow - 1) * nCols; // These remain in place
	entriesDeleted = inCount * nCols; // These are gone
	// Clear all deleted cells
	for (i = entriesBelow + 1; i <= entriesBelow + entriesDeleted; i++)
	{
		BLCell	cell;
		BLHandlerParamBlock	pb;
		
		cell = _BLNthCellToCell( i, inList );
		_BLCallHandlerForCell( blCellDeinitMsg, cell, &pb, inList );
	}
	_VARemove( inCount, inRow, nRows, itemSize, bl_Index(inList) );
}

//===============================
static void local_BLDeleteColumnsFromIndex( UInt16 count, UInt16 nthColumn, BlugsRef inList )
{
	UInt16			nRows, nCols;
	UInt32			neededSize, nRecords;
	Handle			indexH;
	
	nCols = bl_NCols(inList);
	nRows = bl_NRows(inList);
	indexH = bl_Index(inList);
	// If we're deleting everything, no need to move or copy anything
	if (nCols > count)
	{
		UInt16			row, col, entriesAbove, entriesBelow;
		UInt32			copySize, nthCell;
		BLIndexPtr		source, dest, indexBase;
		SInt8			state;
		
		state = HGetState( indexH );
		HLock( indexH );
		indexBase = (BLIndexPtr)*indexH;
		entriesBelow = nthColumn - 1;
		entriesAbove = nCols - count - entriesBelow;
		copySize = sizeof(BLIndexRec) * (entriesAbove + entriesBelow);
		dest = indexBase + entriesBelow;
		source = dest + count;
		for (row = 1; row < nRows; row++, dest += nCols - count, source += nCols)
		{
			nthCell = ((row - 1) * nCols) + nthColumn;
			for (col = nthColumn; col < nthColumn + count; col++, nthCell++)
			{
				BLCell	cell;
				BLHandlerParamBlock	pb;
				
				cell = _BLNthCellToCell( nthCell, inList );
				_BLCallHandlerForCell( blCellDeinitMsg, cell, &pb, inList );
			}
			BlockMoveData( (Ptr)source, (Ptr)dest, copySize );
		}
		// Copy the final row; just the entries above; exclude entries below of the next row
		if (entriesAbove)
			BlockMoveData( (Ptr)source, (Ptr)dest, entriesAbove * sizeof(BLIndexRec) );
		HSetState( indexH, state );
	}
	// The new number of records
	nRecords = nRows * (nCols - count);
	neededSize = sizeof(BLIndexRec) * nRecords;
	SetHandleSize( indexH, neededSize );
}

//===============================
// Yes, I know the param order doesn't agree with _BLMoveRows. Put inCount first to agree
//	with the List Mgr-like ordering of BLAddRows and such.
BL_PROC OSErr BLMoveRows( UInt16 inCount, UInt16 inRow, UInt16 inAfterThisRow,
						UInt16 inDisclosureLevel, BlugsRef inList )
{
	OSErr	err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		err = _BLValidateRow( inRow, inList );
		if (!err)
		{
			// Allow inAfterThisRow to be 0.
			if (inAfterThisRow) err = _BLValidateRow( inAfterThisRow, inList );
			if (!err)
			{
				UInt16	min, max;
				
				if (_BLValidDisclosureTarget( true, inAfterThisRow, inDisclosureLevel, &min, &max, inList ))
					err = _BLMoveRows( inRow, inAfterThisRow, inCount, inDisclosureLevel, inList );
				else err = paramErr;
			}
		}
	}
	return err;
}

//===============================
BL_PROC OSErr BLMoveColumns( UInt16 inCount, UInt16 inColumn, UInt16 inAfterThisColumn, BlugsRef inList )
{
	OSErr	err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		err = _BLValidateColumn( inColumn, inList );
		if (!err)
		{
			// Allow inAfterThisColumn to be 0.
			if (inAfterThisColumn) err = _BLValidateColumn( inAfterThisColumn, inList );
			if (!err) err = _BLMoveColumns( inColumn, inAfterThisColumn, inCount, inList );
		}
	}
	return err;
}

//===============================
// Moves a number of rows and their descendants after another row.
// The inAfterThisRow parameter can be 0, in that case the rows are moved to the top.
// Calculates a total count so as to include descendants, but once the total count
//	is reached, stops including rows. Works exactly like BLDeleteRows.
//	This means if the first two rows are at the same disclosure level, and each one has 1000
//	descendants, and you call _BLMoveRows( 1, 10, 2, 0, list), only the first row and
//	its descendants get moved. To include the second row you have to call _BLCountDescendants
//	on the first one. That is, you call _BLMoveRows( 1, 10, 1002, 0, list ) so the first
//	1001 rows are the first one and its descendants, and the last one (#1002) is the second
//	row you wanted to move. In this case a total of 2002 rows move, since the second parent
//	row also has 1000 descendants and they have to move with it.
OSErr _BLMoveRows( UInt16 inMoveRow, UInt16 inAfterThisRow, UInt16 inCount,
					UInt16 inDisclosureLevel, BlugsRef inList )
{
	OSErr		err = noErr;
	
	_AssertStr( inMoveRow != 0, "Error: tried to move row #0." );
	if (!inMoveRow) err = inputOutOfBounds;
	else
	{
		UInt16		nRows;
		Boolean		moving = true;	// true if the row is not being moved directly after itself
									//	or the row before it.
		
		nRows = bl_NRows(inList);
		_AssertStr( inMoveRow <= nRows, "Error: tried to move nonexistent row." );
		_AssertStr( inAfterThisRow <= nRows, "Internal error: tried to move after nonexistent row." );
		if (inMoveRow == inAfterThisRow || inMoveRow - 1 == inAfterThisRow) moving = false;
		if (bl_Disclosure(inList))
		{
			if (_BLValidDisclosureTarget( moving, inAfterThisRow, inDisclosureLevel, nil, nil, inList ))
			{
				// Have to do this first, since if we decrease disclosure level the moved
				//	row gets some temp fake descendants.
				inCount = local_BLIncludeDescendants( inMoveRow, inCount, inList );
				_BLSetDisclosureLevel( inMoveRow, inDisclosureLevel, inList );
			}
			else err = inputOutOfBounds;
		}
		if (!err)
		{
			if (moving)
			{
				err = _VAMoveAfter( inCount, inMoveRow, inAfterThisRow,
									bl_NCols(inList) * sizeof(BLIndexRec),
									nRows, bl_Index(inList) );
				if (!err)
				{
					err = _VAMoveAfter( inCount, inMoveRow, inAfterThisRow, sizeof(BLRowDataRec),
										nRows, (Handle)bl_RowData(inList) );
					if (!err)
					{
						BLTitleBarHdl	theBar;
						
						theBar = bl_VTitle(inList);
						if (theBar) _BLMoveTitles( inMoveRow, inAfterThisRow, inCount, nRows, theBar );
					}
				}
			} // end if moving.
			if (!err)
			{
				if (bl_Disclosure(inList))
				{
					_BLNewNumberOnMovement( &inAfterThisRow, inCount, inMoveRow, inAfterThisRow );
					//_BLRecalcDisclosure( true, inAfterThisRow, inList );
					// The call to _BLSetDisclosureLevel above can seriously screw up
					//	disclosure info, temporarily, since it can make a row's siblings into
					//	its descendants until it is moved.
					_BLRecalcDisclosure( false, 1, inList );
				}
				_BLRecalc( blRecalcRowOffsets | blRecalcVisCells, 1, inList );
				_BLUpdateWorld( blUpdateBlit, nil, inList );
			}
		}
	}
	return err;
}

//===============================
OSErr _BLMoveColumns( UInt16 inMoveColumn, UInt16 inAfterThisColumn, UInt16 inCount, BlugsRef inList )
{
	OSErr		err = noErr;
	
	_AssertStr(inMoveColumn != inAfterThisColumn,"Internal error: tried to move column after itself.");
	_AssertStr(inMoveColumn != 0,"Internal error: tried to move column #0.");
	_AssertStr(inMoveColumn - 1 != inAfterThisColumn,
		"Internal notice: moving columns after the one immediately  before them has no effect.");
	if (!inMoveColumn) err = inputOutOfBounds;
	else
	{
		// If the movement would not net us anything, do nothing.
		if (inMoveColumn != inAfterThisColumn && inMoveColumn - 1 != inAfterThisColumn)
		{
			UInt16				nCols;
			BLColDataArrayHdl	colData;
			
			nCols = bl_NCols(inList);
			_AssertStr( inMoveColumn <= nCols, "Internal error: tried to move nonexistent column." );
			_AssertStr( inAfterThisColumn <= nCols, "Internal error: tried to move after nonexistent column." );
			colData = bl_ColData(inList);
			err = _VAMoveAfter( inCount, inMoveColumn, inAfterThisColumn, sizeof(BLColDataRec), nCols, (Handle)colData );
			if (!err)
			{
				BLTitleBarHdl	hTitle;
				
				// If there is a horizontal title bar, move the title
				hTitle = bl_HTitle(inList);
				if (hTitle) err = _BLMoveTitles( inMoveColumn, inAfterThisColumn, 1, nCols, hTitle );
				if (!err)
				{
					err = local_BLMoveColumnsInIndex( inMoveColumn, inAfterThisColumn, inCount, inList );
					if (!err)
					{
						// Update list to reflect the change
						_BLRecalc( blRecalcColOffsets | blRecalcVisCells | blRecalcRightPin, 1, inList );
						_BLUpdateWorld( blUpdateBlit, nil, inList );
					}
				}
			}
		}
	}
	return err;
}

//===============================
static OSErr local_BLMoveColumnsInIndex( UInt16 inColumn, UInt16 inAfterThis,
										UInt16 inCount, BlugsRef inList )
{
	Ptr			storage;
	UInt32		storageSize;
	BLIndexRec	storageForOne;
	OSErr		err = noErr;
	
	if (inCount == 1)
	{
		storageSize = sizeof(BLIndexRec);
		storage = (Ptr)&storageForOne;
	}
	else
	{
		storageSize = inCount * sizeof(BLIndexRec);
		storage = NewPtr( storageSize );
	}
	if (!storage) err = memFullErr;
	else
	{
		BLIndexPtr	rowStartPtr, movedDataLoc, restoreDest, source, dest;
		UInt16		row, nRows, nCols, nEntriesShifted;
		UInt32		bytes;
		
		nRows = bl_NRows(inList);
		nCols = bl_NCols(inList);
		rowStartPtr = (BLIndexPtr)*bl_Index(inList); // Beginning of row.
		movedDataLoc = rowStartPtr + inColumn - 1; // The beginning of the entries to be moved.
		if (inAfterThis > inColumn)
		{
			nEntriesShifted = inAfterThis + 1 - inColumn - inCount;
			restoreDest = movedDataLoc + nEntriesShifted;
			source = movedDataLoc + inCount;
			dest = movedDataLoc;
		}
		else
		{
			nEntriesShifted = inColumn - inAfterThis - 1;
			restoreDest = rowStartPtr + inAfterThis;
			source = restoreDest;
			dest = restoreDest + inCount;
		}
		bytes = nEntriesShifted * sizeof(BLIndexRec);
		for (row = 1; row <= nRows; row++)
		{
			// Copy the moved records into storage
			BlockMoveData( movedDataLoc, storage, storageSize );
			// Shift the records in that row to make room
			BlockMoveData( (Ptr)source, (Ptr)dest, bytes );
			// Copy the moved record into the correct slot
			BlockMoveData( storage, (Ptr)restoreDest, storageSize );
			// Update all the pointers.
			movedDataLoc += nCols;
			restoreDest += nCols;
			source += nCols;
			dest += nCols;
		}
		if (inCount != 1) DisposePtr( storage );
	}
	return err;
}

//===============================
// Calculates the actual number of rows to be deleted or moved, so that all descendants
//	are included.
static UInt16 local_BLIncludeDescendants( UInt16 inStartRow, UInt16 inCount, BlugsRef inList )
{
	UInt16	nRows, realCount = 0, nDescendants;
	
	nRows = bl_NRows(inList);
	do
	{
		nDescendants = _BLCountDescendants( inStartRow, inList );
		realCount += nDescendants + 1;
		inStartRow += nDescendants + 1;
	} while (realCount < inCount && inStartRow <= nRows);
	return realCount;
}

//===============================
BL_PROC OSErr BLMoveMarkedRows( UInt16 inAfterThisRow, UInt16 inDisclosureLevel, BlugsRef inList )
{
	OSErr	err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		// Allow inAfterThisRow to be 0.
		if (inAfterThisRow) err = _BLValidateRow( inAfterThisRow, inList );
		if (!err)
		{
			UInt16	min, max;
			
			if (_BLValidDisclosureTarget( true, inAfterThisRow, inDisclosureLevel, &min, &max, inList ))
			{
				UInt16				row, nRows;
				BLRowDataArrayHdl	rowData;
				BLRowDataPtr		dataP;
				UInt8				state;
				Boolean				saveAutodraw;
				
				saveAutodraw = bl_Autodraw(inList);
				bl_Clear((*inList)->listFlags,blAutodraw);
				nRows = bl_NRows(inList);
				rowData = bl_RowData(inList);
				state = HGetState( (Handle)rowData );
				HLock( (Handle)rowData );
				dataP = bl_NthRowDataAddr(1,rowData);
				for (row = 1; row <= nRows; row++, dataP++)
				{
					if (bl_Test(dataP->flags,blRowMarkedForMovement))
					{
						UInt16			nDescendants, i;
						BLRowDataPtr	anotherDataP;
						
						anotherDataP = dataP;
						nDescendants = _BLCountDescendants( row, inList );
						for (i = 0; i <= nDescendants; i++, anotherDataP++)
							bl_Clear(anotherDataP->flags,blRowMarkedForMovement);
						_BLMoveRows( row, inAfterThisRow, 1, inDisclosureLevel, inList );
						if (inAfterThisRow <= row)
						{
							dataP += nDescendants;
							row += nDescendants;
							inAfterThisRow += (nDescendants + 1);
						}
						else
						{
							dataP--;
							row--;
						}
					} // if marked
				} // for
				if (saveAutodraw) BLSetAutodraw( true, inList );
			}
		}
	}
	return err;
}

//===============================
// Rearranges row data entries to reflect new ordering
void _BLRearrangeRowData( UInt16 inSortArray[], BlugsRef inList )
{
	UInt16				nRows;
	BLRowDataArrayHdl	newArray;
	
	nRows = bl_NRows(inList);
	newArray = (BLRowDataArrayHdl)NewHandle( nRows * sizeof(BLRowDataRec) );
	if (newArray)
	{
		BLRowDataArrayHdl	oldArray;
		BLRowDataPtr		source, dest;
		UInt16				i;
		
		oldArray = bl_RowData(inList);
		dest = (BLRowDataPtr)*newArray;
		for (i = 0; i < nRows; i++)
		{
			UInt16	whichRow;
			
			whichRow = inSortArray[i];
			_AssertStr( whichRow, "Internal error: row 0 does not exist." );
			source = bl_NthRowDataAddr(whichRow,oldArray);
			BlockMoveData( (Ptr)source, (Ptr)dest, sizeof(BLRowDataRec) );
			dest++;
		}
		bl_RowData(inList) = newArray;
		DisposeHandle( (Handle)oldArray );
		_BLRecalc( blRecalcRowOffsets, 1, inList );
	}
}