/*===============================
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 ¥¥¥Module Data¥¥¥

#define blTriangleHeight		12
#define blTriangleWidth			12
#define blTriangleFromTop		2
#define blTriangleFromLeft		8
//#define blTriangleRightEdge		blTriangleFromLeft + blTriangleWidth
#define blFirstIndent			blTriangleFromLeft + blTriangleWidth + 4;

static void		local_BLToggleTriangle( UInt16 inRow, Boolean inExpanding, BlugsRef inList );
static UInt16	local_BLCountDisclosedRows( UInt16 inNRows, BlugsRef inList );


//===============================
// Should the triangle be drawn at the top, middle, or bottom of the cell?
// How can we make sure Blugs' drawing of the triangle is compatible with where the end user
//	decides to draw content?
// I guess we publish where the triangle is drawn and let handler developers worry about
//	drawing their text or whatever starting x pixels from the top of the cell.
// Parameter inDisclosureLevel and call to OffsetRect:
//	these were contributed by Andreas Petterson Feb 22 2003
// Moses added the inList parameter to support the check for the
//	blIndentTriangles feature Feb 26 2003
Rect _BLTriangleRect( Rect inCellRect, UInt16 inDisclosureLevel, BlugsRef inList )
{
	Rect		r;

	r.top = inCellRect.top + blTriangleFromTop;
	r.bottom = r.top + blTriangleHeight;
	r.left = inCellRect.left + blTriangleFromLeft;
	r.right = r.left + blTriangleWidth;
	// For some reason this causes the triangle to be drawn much lower in the cell.
	// Uncomment this and fix it when you want to figure out what is wrong!
/*#if TARGET_API_MAC_CARBON
	if (bl_Aqua)
	{
		SInt32	metric;
		OSErr	err;

		r.top = inCellRect.top;
		err = GetThemeMetric( kThemeMetricDisclosureButtonWidth, &metric );
		if (!err) r.right = r.left + metric;
		err = GetThemeMetric( kThemeMetricDisclosureButtonHeight, &metric );
		if (!err) r.bottom = r.top + metric;
	}
#endif*/
	if (bl_IndentTriangles(inList))
		OffsetRect( &r, inDisclosureLevel * blTriangleWidth, 0 );
	return r;
}

//===============================
// The constant blFirstIndent is used for row 1 of a disclosure list.
// Non-disclosure indent, and subsequent indents in row 1, and indent for other rows,
//	is based on the indent value in the list structure.
// This actually works for titles, but for vertical titles (cell.col = 0) there should
//	probably not be any indent at all, so it always returns 4 just to accommodate the
//	button bevels.
//	For horizontal titles (cell.row = 0) we use basic indent.
UInt16 _BLIndent( BLCell inCell, BlugsRef inList )
{
	UInt16	totalIndent;
	
	if (inCell.col == 0) totalIndent = 4;
	else
	{
		UInt8	indent = bl_Indent(inList);
		
		if (inCell.row != 0 && bl_Disclosure(inList) && inCell.col == (*inList)->disclosureColumn)
		{
			UInt16		disclosureLevel;
			
			disclosureLevel = _BLGetRowDisclosureLevel( inCell.row, inList );
			totalIndent = blFirstIndent;
		#if __BL_USE_CARBON_API__
			if (bl_Aqua)
			{
				SInt32	metric;
				OSErr	err;
				
				err = GetThemeMetric( kThemeMetricDisclosureButtonWidth, &metric );
				if (!err) totalIndent = blTriangleFromLeft + metric + 4;
			}
		#endif
			totalIndent += (indent * disclosureLevel);
		}
		else totalIndent = indent;
	}
	return totalIndent;
}

//===============================
// The triangle is already displayed in a pressed state.
// Draw cell offscreen without triangle.
// Draw intermediate onscreen.
// Blit offscreen cell.
// Draw target position, pressed, triangle onscreen.
// Draw cell offscreen with triangle.
static void local_BLToggleTriangle( UInt16 inRow, Boolean inExpanding, BlugsRef inList )
{
	BLCell			cell;
	OSErr			err;
	Rect			cellRect;
	
	cell.col = (*inList)->disclosureColumn;
	cell.row = inRow;
	err = _BLCellRect( cell, &cellRect, inList );
	if (!err)
	{
		Rect			triangleRect;
		GWorldPtr		saveWorld;
		RgnHandle		cellRgn = NewRgn(), saveClip;
		Boolean			restore;
		
		RectRgn( cellRgn, &cellRect );
		triangleRect = _BLTriangleRect( cellRect, _BLGetRowDisclosureLevel( cell.row, inList), inList );
		restore = _BLSwapPort( GetWindowPort( bl_Window(inList) ), &saveWorld );
		saveClip = _BLClipToViewRectIntersection( &cellRect, inList );
		if (saveClip)
		{
			// Draw offscreen without triangle.
			//_BLUpdateCell( blUpdateNoOptions, cell, inList );
			if (bl_AllegroAppearance)
			{
				ThemeButtonDrawInfo	newInfo, prevInfo;
				ThemeEraseUPP		eraseProc;
				OSStatus			status;
				BLDisclosureData	data;
				
				data.cell = cell;
				data.list = inList;
				eraseProc = NewThemeEraseUPP( (ThemeEraseProcPtr)_BLDisclosureEraseProc );
				prevInfo.adornment = 0;
				newInfo.adornment = 0;
				prevInfo.state = kThemeStatePressed;
				newInfo.state = kThemeStateActive;
				if (inExpanding)
				{
					prevInfo.value = kThemeDisclosureRight;
					newInfo.value = kThemeDisclosureDown;
				}
				else
				{
					prevInfo.value = kThemeDisclosureDown;
					newInfo.value = kThemeDisclosureRight;
				}
				status = DrawThemeButton( &triangleRect, kThemeDisclosureButton, &newInfo, &prevInfo,
											eraseProc, nil, (UInt32)&data );
				DisposeThemeEraseUPP( eraseProc );
			}
			else
			{
				UInt32		ticks;
				
				// Relocated from above.
				_BLUpdateCell( blUpdateNoOptions, cell, inList );
				Delay( 2, &ticks );
				// Draw unpressed intermediate.
				_BLDrawDisclosureTriangle( cell, triangleRect, blTriStateIntermediate, true, inList );
				Delay( 2, &ticks );
				_BLUpdate( blUpdateBlit, cellRgn, inList );
				// Draw triangle partially toggled.
				_BLDrawDisclosureTriangle( cell, triangleRect, (inExpanding) ? blTriStateDownPressed :
											blTriStateRightPressed, true, inList );
				Delay( 2, &ticks );
			}
			// Update the cell's disclosure triangle state and expanded setting.
			_BLSetRowFlags( inRow, blRowIsExpanded | blTrianglePressed,
							(inExpanding) ? blRowIsExpanded : 0, inList );
			_BLUpdateCell( blUpdateBlit | blUpdateTriangle, cell, inList );
			SetClip( saveClip );
			DisposeRgn( saveClip );
		} // if saveClip
		DisposeRgn( cellRgn );
		if (restore) SetGWorld( saveWorld, nil );
	} // no error getting cell rect
}


#pragma mark -
//===============================
BL_PROC OSErr BLGetRowDisclosureLevel( UInt16 inRow, UInt16* outDisclosureLevel, BlugsRef inList )
{
	OSErr	err;
	UInt16	level = 0;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		err = _BLValidateRow( inRow, inList );
		if (!err) level = _BLGetRowDisclosureLevel( inRow, inList );
	}
	*outDisclosureLevel = level;
	return err;
}

//===============================
BL_PROC OSErr BLSetRowDisclosureLevel( UInt16 inRow, UInt16 inDisclosureLevel, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		err = _BLValidateRow( inRow, inList );
		if (!err)
		{
			// 10/1/2001 Moses -->1.1
			// Added check for disclosure.
			if (bl_Disclosure(inList))
			{
				UInt16	min, max;
				
				if (_BLValidDisclosureTarget( false, inRow - 1, inDisclosureLevel, &min, &max, inList ))
				{
					_BLSetDisclosureLevel( inRow, inDisclosureLevel, inList );
					_BLUpdateView( blUpdateBlit, inList );
				}
			}
		}
	}
	return err;
}

//===============================
// Returns the row number of a row's parent, 0 if it's parentless
BL_PROC UInt16 BLGetParentRow( UInt16 inRow, BlugsRef inList )
{
	UInt16		childLevel, parent = 0;
	OSErr		err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		if (inRow && bl_Disclosure(inList))
		{
			childLevel = _BLGetRowDisclosureLevel( inRow, inList );
			parent = inRow - 1;
			while (parent)
			{
				if (_BLGetRowDisclosureLevel( parent, inList ) < childLevel) break;
				parent--;
			}
		}
	}
	return parent;
}

//===============================
BL_PROC Boolean BLRowIsDisclosed( UInt16 inRow, BlugsRef inList )
{
	Boolean	disclosed = false;
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		//_AssertStr( bl_Disclosure(inList), "Warning: this function is unnecessary for non-disclosure lists." );
		if (!bl_Disclosure(inList)) disclosed = true;
		else
		{
			err = _BLValidateRow( inRow, inList );
			if (!err) disclosed = _BLRowIsDisclosed( inRow, inList );
		}
	}
	return disclosed;
}


//===============================
// If deepExpand is true, expand a row and all its descendants
BL_PROC OSErr BLExpandRow( Boolean inDeepExpand, UInt16 inRow, BlugsRef inList )
{
	OSErr	err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		if (!bl_Disclosure(inList)) err = paramErr;
		else
		{
			err = _BLValidateRow( inRow, inList );
			if (!err)
			{
				if (!bl_Test(_BLGetRowFlags( inRow, inList ),blRowIsExpanded))
				{
					BLRowExpandProcPtr	proc = (*inList)->callbacks.rowExpandProc;
					
					local_BLToggleTriangle( inRow, true, inList );
					if (inDeepExpand)
					{
						UInt16	nDescendants, i;
						
						nDescendants = _BLCountDescendants( inRow, inList );
						for (i = 0; i <= nDescendants; i++)
						{
							if (bl_Test(_BLGetRowFlags( inRow + i, inList ),blRowHasChildren))
							{
								if (proc)
								{
									// We have farther to go if rows are being added as we count
									//	thru them.
									//UInt16	nRowsBefore, nRowsAfter;
									
									//nRowsBefore = bl_NRows(inList);
									(*proc)( true, inRow + i, inList );
									//nRowsAfter = bl_NRows(inList);
									//_AssertStr( nRowsAfter >= nRowsBefore,
									//			"Internal warning: row expand proc deleted rows." );
									//nDescendants += nRowsAfter - nRowsBefore;
									nDescendants = _BLCountDescendants( inRow, inList );
								}
								_BLSetRowFlags( inRow + i, blRowIsExpanded, blRowIsExpanded, inList );
							}
						}
					}
					else // not deep expand
					{
						// Call the expand proc if one is installed
						if (proc) (*proc)( true, inRow, inList );
					}
					_BLRecalcDisclosure( true, inRow, inList );
					if (bl_Sorted(inList))
					{
						Boolean	wasAutodraw = bl_Autodraw(inList);
						
						// _BLSort will try to redraw, which we have to do anyway, so we defer.
						// We retain the autodraw state.
						bl_Clear((*inList)->listFlags,blAutodraw);
						_BLSort( blSortStateNoChange, inList );
						if (wasAutodraw) bl_Set((*inList)->listFlags,blAutodraw);
					}
					_BLUpdateWorld( blUpdateBlit, nil, inList );
				}
			}
		}
	} // valid list
	return err;
}

//===============================
BL_PROC OSErr BLCollapseRow( Boolean inDeepCollapse, UInt16 inRow, BlugsRef inList )
{
	OSErr	err;
	
	_CheckHandlerFlag(inList)
	err = _BLValidateList( inList );
	if (!err)
	{
		if (!bl_Disclosure(inList)) err = paramErr;
		else
		{
			err = _BLValidateRow( inRow, inList );
			if (!err)
			{
				UInt16	flags;
				
				flags = _BLGetRowFlags( inRow, inList );
				if (bl_Test(flags,blRowIsExpanded))
				{
					BLRowExpandProcPtr	proc;
					UInt16				nDescendants;
					
					proc = (*inList)->callbacks.rowExpandProc;
					if (!bl_Test(flags,blUndisclosed)) local_BLToggleTriangle( inRow, false, inList );
					nDescendants = _BLCountDescendants( inRow, inList );
					// Moses --> 1.2
					// End inline edit if collapsing containing row.
					if (bl_InlineNow(inList))
					{
						BLCell	inlineCell;
						
						if (BLGetCellFromUID( &bl_InlineCell(inList), &inlineCell, inList ) == noErr)
						{
							if (inRow < inlineCell.row && inRow + nDescendants >= inlineCell.row)
								_BLEndInlineEdit( true, inList );
						}
					}
					if (inDeepCollapse)
					{
						UInt16		i;
						
						for (i = 0; i <= nDescendants; i++)
						{
							if (bl_Test(_BLGetRowFlags( inRow + i, inList ),blRowHasChildren))
							{
								if (proc)
								{
									// We have not as far to go if rows are being deleted as
									//	we count thru them.
									//UInt16	nRowsBefore, nRowsAfter;
									
									//nRowsBefore = bl_NRows(inList);
									(*proc)( false, inRow + i, inList );
									//nRowsAfter = bl_NRows(inList);
									//nDescendants -= nRowsBefore - nRowsAfter;
									nDescendants = _BLCountDescendants( inRow, inList );
								}
								_BLSetRowFlags( inRow + i, blRowIsExpanded, 0, inList );
							}
						}
					}
					else
					{
						// When a row is collapsed, all cells that become hidden also become deselected.
						if (nDescendants)
						{
							BLCell	first, last;
							
							first.row = inRow + 1;
							first.col = 1;
							last.row = inRow + nDescendants;
							last.col = bl_NCols(inList);
							_BLDeselectRange( blSelectNoOptions, first, last, inList );
						}
						// Call the expand proc if one is installed
						if (proc) (*proc)( false, inRow, inList );
					}
					_BLRecalcDisclosure( true, inRow, inList );
					_BLUpdateWorld( blUpdateBlit, nil, inList );
				}
			}
		}
	}
	return err;
}

//===============================
BL_PROC UInt16 BLCountDescendants( UInt16 inRow, BlugsRef inList )
{
	UInt16	nChildren = 0;
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		//_AssertStr( bl_Disclosure(inList), "Warning: this routine does nothing with a non-disclosure list." );
		if (bl_Disclosure(inList))
		{
			err = _BLValidateRow( inRow, inList );
			if (!err) nChildren = _BLCountDescendants( inRow, inList );
		}
	}
	return nChildren;
}

//===============================
// Returns the number of descendants of any disclosure level greater than the row, not just
//	immediate children.
UInt16 _BLCountDescendants( UInt16 inRow, BlugsRef inList )
{
	UInt16			parentLevel, nRows, nChildren = 0;
	
	parentLevel = _BLGetRowDisclosureLevel( inRow, inList );
	nRows = bl_NRows(inList);
	while (true)
	{
		if (inRow >= nRows) break;
		inRow++;
		if (_BLGetRowDisclosureLevel( inRow, inList ) <= parentLevel) break;
		nChildren++;
	}
	return nChildren;
}

//===============================
// The public routine was called internally within loops, so I made this private version
//	to be a little faster, with parameter checking unnecessary.
Boolean _BLRowIsDisclosed( UInt16 inRow, BlugsRef inList )
{
	return !bl_Test(_BLGetRowFlags( inRow, inList ),blUndisclosed);
}

//===============================
// Calculates status of blUndisclosed flag.
// If descendants only, calculates for descendants with startHere as parent.
// Otherwise, calculates for all rows to the end of the list.
// Need to call this routine before recalculating visible heights and offsets.
void _BLRecalcDisclosure( Boolean inDescendantsOnly, UInt16 inStartRow, BlugsRef inList )
{
	UInt16		parentLevel, i, rowsToAdjust, firstChild;
	
	if (!inStartRow) inStartRow = 1;
	parentLevel = _BLGetRowDisclosureLevel( inStartRow, inList );
	if (inDescendantsOnly)
	{
		rowsToAdjust = _BLCountDescendants( inStartRow, inList );
		// now we actually start at the first child
		firstChild = inStartRow + 1;
	}
	else
	{
		rowsToAdjust = bl_NRows(inList) - inStartRow + 1;
		firstChild = inStartRow;
	}
	for (i = 0; i < rowsToAdjust; i++)
	{
		// We reveal only the rows whose parents are disclosed and expanded.
		UInt16	thisParent, flags;
		
		flags = 0;
		thisParent = BLGetParentRow( firstChild + i, inList );
		if (thisParent)
		{
			UInt16	parentFlags;
			
			parentFlags = _BLGetRowFlags( thisParent, inList );
			if (bl_Test(parentFlags,blUndisclosed)) bl_Set(flags,blUndisclosed);
			else
			{
				if (!bl_Test(parentFlags,blRowIsExpanded)) bl_Set(flags,blUndisclosed);
				//else bl_Clear(flags,blUndisclosed);
			}
		}
		//else bl_Clear(flags,blUndisclosed);
		_BLSetRowFlags( firstChild + i, blUndisclosed, flags, inList );
	}
	_BLRecalc( blRecalcRowOffsets | blRecalcVisCells | blRecalcVScroll | blRecalcThumb, inStartRow, inList );
}

//===============================
// Sets the disclosure level for a row and its children.
// This function does not validate or repair inNewLevel.
void _BLSetDisclosureLevel( UInt16 inRow, UInt16 inNewLevel, BlugsRef inList )
{
	UInt16		oldLevel;
	
	// Record for the first (i.e., parent) row.
	oldLevel = _BLGetRowDisclosureLevel( inRow, inList );
	if (oldLevel != inNewLevel)
	{
		SInt32			dLevel; // Amount to increase the level
		UInt16			nChildren, i;
		
		dLevel = inNewLevel - oldLevel;
		nChildren = BLCountDescendants( inRow, inList );
		for (i = 0; i <= nChildren; i++) _BLSetRowDisclosureLevel( inRow + i, oldLevel + dLevel, inList );
		_BLRecalcDisclosure( true, (inRow == 1) ? inRow : BLGetParentRow( inRow, inList ), inList );
	}
}

//===============================
UInt16 _BLDeepestDisclosureLevel( BlugsRef inList )
{
	UInt16		row, nRows, candidate = 0;
	
	nRows = bl_NRows(inList);
	for (row = 1; row <= nRows; row++)
	{
		UInt16	level;
		
		level = _BLGetRowDisclosureLevel( row, inList );
		if (level > candidate) candidate = level;
	}
	return candidate;
}

//===============================
// Find a range of rows at the given disclosure level.
// If it finds more than one row at the given level, unbroken by rows of a shallower level,
//	it returns true, and returns the first row of the range, the total (rangeCount) including
//	children, and the count of rows at exactly this level (thisLevelCount).
Boolean _BLFindDisclosureRange( UInt16 inStartRow, UInt16 inLevel, UInt16* outRangeStart,
								UInt16* outRangeCount, UInt16* outThisLevelCount, BlugsRef inList )
{
	UInt16		nRows, row, myRangeStart = 0, myRangeCount = 0, myThisLevelCount = 0;
	Boolean		foundRange = false;
	
	nRows = bl_NRows(inList);
	for (row = inStartRow; row <= nRows; row++)
	{
		UInt16	thisLevel;
		
		thisLevel = _BLGetRowDisclosureLevel( row, inList );
		// If we are at a shallower disclosure level...
		if (thisLevel < inLevel) 
		{
			// If we have been counting some rows at the target level, we have a result
			if (myThisLevelCount > 1) break;
			// If we only found one of the given level, reset and keep looking
			if (myThisLevelCount == 1)
			{
				myRangeStart = myRangeCount = myThisLevelCount = 0;
			}
		}
		// Otherwise we are at the desired level or lower
		else
		{
			// If we're already in the middle of a count, continue it
			if (!myRangeStart) myRangeStart = row;
			if (thisLevel == inLevel) myThisLevelCount++;
			myRangeCount++;
		}
	}
	if (myThisLevelCount > 1)
	{
		*outRangeStart = myRangeStart;
		*outRangeCount = myRangeCount;
		*outThisLevelCount = myThisLevelCount;
		foundRange = true;
	}
	return foundRange;
}

//===============================
Handle _BLGetDisclosedRows( UInt16* outCount, BlugsRef inList )
{
	UInt16		nRows, nDisclosedRows;
	Handle		array;
	
	nRows = bl_NRows(inList);
	nDisclosedRows = local_BLCountDisclosedRows( nRows, inList );
	array = _VANew( nDisclosedRows, sizeof(UInt16), false );
	if (array)
	{
		UInt16	row = 1, *p;
				
		p = (UInt16*)*array;
		if (bl_Disclosure(inList))
		{
			UInt16	i = 0;
			
			for (; row <= nRows; row++)
			{
				if (i >= nDisclosedRows) break;
				if (!bl_Test(_BLGetRowFlags( row, inList ),blUndisclosed))
				{
					p[i] = row;
					i++;
				}
			}
		}
		else for (; row <= nRows; row++) p[row-1] = row;
	}
	*outCount = nDisclosedRows;
	return array;
}


//===============================
// Works for disclosure and nondisclosure; gets the next visible/disclosed row after this one.
// Returns 0 if there isn't one.
UInt16 _BLNextDisclosedRow( UInt16 inRow, BlugsRef inList )
{
	UInt16	nRows = bl_NRows(inList), nextRow = 0;
	
	if (inRow < nRows)
	{
		inRow++;	// Start with the row after this one.
		if (bl_Disclosure(inList))
		{
			while (inRow <= nRows)
			{
				if (!bl_Test(_BLGetRowFlags( inRow, inList ),blUndisclosed))
				{
					nextRow = inRow;
					break;
				}
				inRow++;
			}
		}
		// Not disclosure
		else nextRow = inRow;
	}
	return nextRow;
}

//===============================
// Works for disclosure and nondisclosure.
// Gets the first visible/disclosed row previous to this one.
// Returns 0 if there isn't one.
UInt16 _BLPreviousDisclosedRow( UInt16 inRow, BlugsRef inList )
{
	UInt16	previousRow = 0;
	
	if (inRow > 1)
	{
		inRow--; // Start with the row before this one.
		if (bl_Disclosure(inList))
		{
			while (inRow > 0)
			{
				if (!bl_Test(_BLGetRowFlags( inRow, inList ),blUndisclosed))
				{
					previousRow = inRow;
					break;
				}
				inRow--;
			}
		}
		// Not disclosure
		else previousRow = inRow;
	}
	return previousRow;
}

//===============================
UInt16 local_BLCountDisclosedRows( UInt16 inNRows, BlugsRef inList )
{
	UInt16	nDisclosedRows;
	
	if (bl_Disclosure(inList))
	{
		UInt16			row;
		
		nDisclosedRows = 0;
		for (row = 1; row <= inNRows; row++)
		{
			if (!bl_Test(_BLGetRowFlags( row, inList ),blUndisclosed)) nDisclosedRows++;
		}
	}
	else nDisclosedRows = inNRows;
	return nDisclosedRows;
}

//===============================
// If inMoving is true, we are trying to move between inPrevRow and inPrevRow + 1.
//	Otherwise we are trying to change the level of a row that's already in place as
//	inPrevRow + 1.
// If not inMoving, we have to take into account the row and its descendants -- count past them.
// In the Drag code we are considering dropping a row, so we need to take adjacent rows
//	into consideration.
Boolean _BLValidDisclosureTarget( Boolean inMoving, UInt16 inPrevRow, UInt16 inDisclosureLevel,
									UInt16* outMin, UInt16* outMax, BlugsRef inList )
{
	UInt16	min = 0, max = 0;
	
	
	if (inPrevRow)
	{
		UInt16			nRows;
		
		nRows = bl_NRows(inList);
		// Find out what disclosure level range to clamp to.
		max = _BLGetRowDisclosureLevel( inPrevRow, inList );
		if (bl_Test(_BLGetRowFlags(inPrevRow, inList ),blRowHasChildren)) max++;
		if (!inMoving)
		{
			UInt16	descendants, row = inPrevRow + 1;
			
			descendants = _BLCountDescendants( row, inList );
			if (nRows > row + descendants)
			{
				// Count past this row and its descendants.
				// The next row we get to will have level less than or equal to inRow's.
				min = _BLGetRowDisclosureLevel( row + descendants + 1, inList );
			}
		}
		else if (nRows > inPrevRow) min = _BLGetRowDisclosureLevel( inPrevRow + 1, inList );
	}
	if (outMin) *outMin = min;
	if (outMax) *outMax = max;
	return (inDisclosureLevel >= min && inDisclosureLevel <= max);
}

//===============================
BL_PROC UInt16 BLGetDisclosureTriangleColumn( BlugsRef inList )
{
	OSErr	err;
	UInt16	col = 1;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		col = (*inList)->disclosureColumn;
		if (!col) col = 1;
	}
	return col;
}

//===============================
BL_PROC OSErr BLSetDisclosureTriangleColumn( UInt16 inColumn, BlugsRef inList )
{
	OSErr	err;
	
	err = _BLValidateList( inList );
	if (!err)
	{
		if (!inColumn) inColumn = 1;
		err = _BLValidateColumn( inColumn, inList );
		if (!err)
		{
			if ((*inList)->disclosureColumn != inColumn)
			{
				(*inList)->disclosureColumn = inColumn;
				_BLUpdateView( blUpdateBlit, inList );
			}
		}
	}
	return err;
}