/******************************************************************************
 JTableSelection.cc

	Class for storing which cells are selected in a JTable.

	BASE CLASS = JAuxTableData<JBoolean>

	Copyright © 1997 by John Lindal & Glenn Bach. All rights reserved.

 ******************************************************************************/

#include <JTableSelection.h>
#include <JTable.h>
#include <JMinMax.h>
#include <jAssert.h>

/******************************************************************************
 Constructor

 ******************************************************************************/

JTableSelection::JTableSelection
	(
	JTable* table
	)
	:
	JAuxTableData<JBoolean>(table, kFalse)
{
}

/******************************************************************************
 Copy constructor

 ******************************************************************************/

JTableSelection::JTableSelection
	(
	JTable*					table,
	const JTableSelection&	source
	)
	:
	JAuxTableData<JBoolean>(table, source)
{
}

/******************************************************************************
 Destructor

 ******************************************************************************/

JTableSelection::~JTableSelection()
{
}

/******************************************************************************
 ExtendSelection

	Undoes the latest extend operation, sets the new boat cell, and
	selects the cells between boat and anchor.

 ******************************************************************************/

JBoolean
JTableSelection::ExtendSelection
	(
	const JPoint& newBoat
	)
{
	if (newBoat == itsBoat)
		{
		return kTrue;
		}
	else if (UndoSelection())
		{
		itsBoat = newBoat;
		SelectRect(itsBoat, itsAnchor, kTrue);
		return kTrue;
		}
	else
		{
		return kFalse;
		}
}

/******************************************************************************
 UndoSelection (private)

	Undoes the latest extend operation.

 ******************************************************************************/

JBoolean
JTableSelection::UndoSelection()
{
	if (OKToExtendSelection())
		{
		SelectRect(itsBoat, itsAnchor, kFalse);
		return kTrue;
		}
	else
		{
		return kFalse;
		}
}

/******************************************************************************
 Receive (virtual protected)

	Update our boat and anchor cells.

	When a row or column is moved, there is nothing to do but reselect
	the same area in the new arrangement.

 ******************************************************************************/

void
JTableSelection::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	JTable* table = GetTable();

	JBoolean reselect = kFalse;
	if (sender == table && message.Is(JTable::kPrepareForTableDataMessage))
		{
		itsReselectAfterChangeFlag = UndoSelection();
		}

	// let JAuxTableData update the data first

	JAuxTableData<JBoolean>::Receive(sender, message);

	// rows changed

	if (sender == table && message.Is(JTableData::kRowInserted))
		{
		const JTableData::RowInserted* info =
			dynamic_cast(const JTableData::RowInserted*, &message);
		assert( info != NULL );
		info->AdjustCell(&itsBoat);
		info->AdjustCell(&itsAnchor);
		reselect = kTrue;
		}

	else if (sender == table && message.Is(JTableData::kRowDuplicated))
		{
		const JTableData::RowDuplicated* info =
			dynamic_cast(const JTableData::RowDuplicated*, &message);
		assert( info != NULL );
		info->AdjustCell(&itsBoat);
		info->AdjustCell(&itsAnchor);
		reselect = kTrue;
		}

	else if (sender == table && message.Is(JTableData::kRowRemoved))
		{
		const JTableData::RowRemoved* info =
			dynamic_cast(const JTableData::RowRemoved*, &message);
		assert( info != NULL );
		const JPoint origBoat   = itsBoat;
		const JPoint origAnchor = itsAnchor;
		info->AdjustCell(&itsBoat);
		info->AdjustCell(&itsAnchor);
		AdjustIndexAfterRemove(origBoat.y, origAnchor.y, GetRowCount(),
							   &(itsBoat.y), &(itsAnchor.y));
		reselect = kTrue;
		}

	else if (sender == table && message.Is(JTableData::kAllRowsRemoved))
		{
		ClearSelection();
		}

	// columns changed

	else if (sender == table && message.Is(JTableData::kColInserted))
		{
		const JTableData::ColInserted* info =
			dynamic_cast(const JTableData::ColInserted*, &message);
		assert( info != NULL );
		info->AdjustCell(&itsBoat);
		info->AdjustCell(&itsAnchor);
		reselect = kTrue;
		}

	else if (sender == table && message.Is(JTableData::kColDuplicated))
		{
		const JTableData::ColDuplicated* info =
			dynamic_cast(const JTableData::ColDuplicated*, &message);
		assert( info != NULL );
		info->AdjustCell(&itsBoat);
		info->AdjustCell(&itsAnchor);
		reselect = kTrue;
		}

	else if (sender == table && message.Is(JTableData::kColRemoved))
		{
		const JTableData::ColRemoved* info =
			dynamic_cast(const JTableData::ColRemoved*, &message);
		assert( info != NULL );
		const JPoint origBoat   = itsBoat;
		const JPoint origAnchor = itsAnchor;
		info->AdjustCell(&itsBoat);
		info->AdjustCell(&itsAnchor);
		AdjustIndexAfterRemove(origBoat.x, origAnchor.x, GetColCount(),
							   &(itsBoat.x), &(itsAnchor.x));
		reselect = kTrue;
		}

	else if (sender == table && message.Is(JTableData::kAllColsRemoved))
		{
		ClearSelection();
		}

	// everything changed

	else if (sender == table && message.Is(JTable::kTableDataChanged))
		{
		ClearSelection();
		}

	// select new area

	if (reselect && itsReselectAfterChangeFlag && OKToExtendSelection())
		{
		SelectRect(itsBoat, itsAnchor, kTrue);
		}
}

/******************************************************************************
 AdjustIndexAfterRemove (private)

 ******************************************************************************/

void
JTableSelection::AdjustIndexAfterRemove
	(
	const JIndex	origBoat,
	const JIndex	origAnchor,
	const JIndex	maxIndex,
	JCoordinate*	boat,
	JCoordinate*	anchor
	)
	const
{
	if (*boat == 0 && *anchor > 0 && origAnchor < origBoat)
		{
		*boat = JMin(origBoat-1, maxIndex);
		}
	else if (*boat == 0 && *anchor > 0)
		{
		*boat = JMin(origBoat, maxIndex);
		}
	else if (*anchor == 0 && *boat > 0 && origBoat < origAnchor)
		{
		*anchor = JMin(origAnchor-1, maxIndex);
		}
	else if (*anchor == 0 && *boat > 0)
		{
		*anchor = JMin(origAnchor, maxIndex);
		}
}

/******************************************************************************
 GetSingleSelectedCell

 ******************************************************************************/

JBoolean
JTableSelection::GetSingleSelectedCell
	(
	JPoint* cell
	)
	const
{
	JPoint cell1, cell2;
	if (GetFirstSelectedCell(&cell1) &&
		GetLastSelectedCell(&cell2) &&
		cell1 == cell2)
		{
		*cell = cell1;
		return kTrue;
		}
	else
		{
		return kFalse;
		}
}

/******************************************************************************
 GetFirstSelectedCell

	Return the first selected cell, starting from the upper left and going in
	the specified direction.

 ******************************************************************************/

JBoolean
JTableSelection::GetFirstSelectedCell
	(
	JPoint*										cell,
	const JTableSelectionIterator::Direction	d
	)
	const
{
	JTableSelectionIterator iter(this, d, kJIteratorStartAtBeginning);
	return iter.Next(cell);
}

/******************************************************************************
 GetLastSelectedCell

	Return the first selected cell, starting from the lower right and going in
	the specified direction.

 ******************************************************************************/

JBoolean
JTableSelection::GetLastSelectedCell
	(
	JPoint*										cell,
	const JTableSelectionIterator::Direction	d
	)
	const
{
	JTableSelectionIterator iter(this, d, kJIteratorStartAtEnd);
	return iter.Prev(cell);
}

/******************************************************************************
 InvertSelection (static private)

	Not inline because we need a pointer to it.

 ******************************************************************************/

JBoolean
JTableSelection::InvertSelection
	(
	const JBoolean& b
	)
{
	return JNegate(b);
}
