/******************************************************************************
 JXStringList.cc

	Maintains a column of strings stored in a JPtrArray<JString>.
	This is very different from a JStringTable which requires a JTableData
	object.

	Note that we can only receive notification when the elements of the
	JPtrArray change.  i.e. When the JString pointer changes, -not- when the
	JString data changes.

	Since recalculating the required width after every change would take
	too much time, we update the width whenever we encounter a string
	that is wider than our current width.

	BASE CLASS = JXTable

	Copyright © 1996 by John Lindal. All rights reserved.

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

#include <JXStringList.h>
#include <JXUpdateStringListWidth.h>
#include <JXWindow.h>
#include <JXColormap.h>
#include <jXGlobals.h>
#include <JPainter.h>
#include <JFontManager.h>
#include <JString.h>
#include <jAssert.h>

const JCoordinate kHMarginWidth = 3;
const JCoordinate kVMarginWidth = 1;

/******************************************************************************
 Create (static)

	Work-around for egcs thunks bug.

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

JXStringList*
JXStringList::Create
	(
	JXScrollbarSet*		scrollbarSet,
	JXContainer*		enclosure,
	const HSizingOption	hSizing,
	const VSizingOption	vSizing,
	const JCoordinate	x,
	const JCoordinate	y,
	const JCoordinate	w,
	const JCoordinate	h
	)
{
	JXStringList* list =
		new JXStringList(scrollbarSet, enclosure, hSizing, vSizing, x,y, w,h);
	assert( list != NULL );
	list->JXStringListX();
	return list;
}

/******************************************************************************
 Constructor (protected)

	Derived classes must call JXStringListX() in their Create() functions.
	Until this is called, the table is empty.

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

JXStringList::JXStringList
	(
	JXScrollbarSet*		scrollbarSet,
	JXContainer*		enclosure,
	const HSizingOption	hSizing,
	const VSizingOption	vSizing,
	const JCoordinate	x,
	const JCoordinate	y,
	const JCoordinate	w,
	const JCoordinate	h
	)
	:
	JXTable(1,w, scrollbarSet, enclosure, hSizing,vSizing, x,y, w,h)
{
	itsList = NULL;

	itsFontName = new JString;
	assert( itsFontName != NULL );

	itsMinColWidth = 1;

	itsStyles = new JRunArray<JFontStyle>;
	assert( itsStyles != NULL );

	SetFont(JGetDefaultFontName(), kJXDefaultFontSize);

	JXColormap* colormap         = GetColormap();
	const JColorIndex blackColor = colormap->GetBlackColor();
	SetRowBorderInfo(0, blackColor);
	SetColBorderInfo(0, blackColor);

//	AppendCol();	// egcs thunks
}

// protected -- work-around for egcs thunks bug

void
JXStringList::JXStringListX()
{
	AppendCol();
}

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

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

JXStringList::~JXStringList()
{
	delete itsFontName;
	delete itsStyles;
}

/******************************************************************************
 TableDrawCell (virtual protected)

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

void
JXStringList::TableDrawCell
	(
	JPainter&		p,
	const JPoint&	cell,
	const JRect&	rect
	)
{
	HilightIfSelected(p, cell, rect);

	p.SetFont(*itsFontName, itsFontSize, itsStyles->GetElement(cell.y));

	const JString* str = itsList->NthElement(cell.y);

	// check that column is wide enough

	const JCoordinate w = p.GetStringWidth(*str) + 2*kHMarginWidth;
	if (w > GetColWidth(1))
		{
		itsMinColWidth = w;
		JXUpdateStringListWidth* task = new JXUpdateStringListWidth(this);
		assert( task != NULL );
		(JXGetApplication())->InstallUrgentTask(task);
		}

	// draw string

	JRect r = rect;
	r.left += kHMarginWidth;
	p.String(r, *str, JPainter::kHAlignLeft, JPainter::kVAlignCenter);
}

/******************************************************************************
 SetStringList

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

void
JXStringList::SetStringList
	(
	const JPtrArray<JString>* list
	)
{
	if (itsList != NULL)
		{
		StopListening(itsList);
		}

	itsList = list;

	itsMinColWidth = 1;
	itsStyles->RemoveAll();
	if (itsList != NULL)
		{
		ListenTo(itsList);

		const JSize strCount = itsList->GetElementCount();
		if (strCount > 0)
			{
			itsStyles->AppendElements(JFontStyle(), strCount);

			const JSize rowCount = GetRowCount();
			for (JIndex i=rowCount+1; i<=strCount; i++)
				{
				AppendRow();
				}
			}
		}
	else
		{
		RemoveAllRows();
		}
}

/******************************************************************************
 GetFont

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

void
JXStringList::GetFont
	(
	JString*	name,
	JSize*		size
	)
	const
{
	*name = *itsFontName;
	*size = itsFontSize;
}

/******************************************************************************
 SetFont

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

void
JXStringList::SetFont
	(
	const JCharacter*	name,
	const JSize			size
	)
{
	*itsFontName   = name;
	itsFontSize    = size;
	itsMinColWidth = 1;

	const JSize fontHeight =
		(GetFontManager())->GetLineHeight(*itsFontName, itsFontSize, JFontStyle());
	const JCoordinate rowHeight = fontHeight + 2*kVMarginWidth;
	SetDefaultRowHeight(rowHeight);
	SetAllRowHeights(rowHeight);
}

/******************************************************************************
 SetStyles

	The given list must have the same length as the table.

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

void
JXStringList::SetStyles
	(
	const JRunArray<JFontStyle>& styleList
	)
{
	assert( styleList.GetElementCount() == GetRowCount() );

	*itsStyles = styleList;
	Refresh();
}

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

	Listen for changes in our list.

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

void
JXStringList::Receive
	(
	JBroadcaster*	sender,
	const Message&	message
	)
{
	if (sender == const_cast<JPtrArray<JString>*>(itsList) &&
		message.Is(JOrderedSetT::kElementsInserted))
		{
		const JOrderedSetT::ElementsInserted* info =
			dynamic_cast(const JOrderedSetT::ElementsInserted*, &message);
		assert( info != NULL );
		const JIndex firstIndex = info->GetFirstIndex();
		const JSize count       = info->GetCount();
		itsStyles->InsertElementsAtIndex(firstIndex, JFontStyle(), count);
		for (JIndex i=1; i<=count; i++)
			{
			InsertRow(firstIndex);
			}
		}

	else if (sender == const_cast<JPtrArray<JString>*>(itsList) &&
			 message.Is(JOrderedSetT::kElementsRemoved))
		{
		const JOrderedSetT::ElementsRemoved* info =
			dynamic_cast(const JOrderedSetT::ElementsRemoved*, &message);
		assert( info != NULL );
		const JIndex firstIndex = info->GetFirstIndex();
		const JSize count       = info->GetCount();
		itsMinColWidth = 1;
		itsStyles->RemoveNextElements(firstIndex, count);
		for (JIndex i=1; i<=count; i++)
			{
			RemoveRow(firstIndex);
			}
		}

	else if (sender == const_cast<JPtrArray<JString>*>(itsList) &&
			 message.Is(JOrderedSetT::kAllElementsRemoved))
		{
		itsMinColWidth = 1;
		itsStyles->RemoveAll();
		RemoveAllRows();
		}

	else if (sender == const_cast<JPtrArray<JString>*>(itsList) &&
			 message.Is(JOrderedSetT::kElementMoved))
		{
		const JOrderedSetT::ElementMoved* info =
			dynamic_cast(const JOrderedSetT::ElementMoved*, &message);
		assert( info != NULL );
		const JIndex origIndex = info->GetOrigIndex();
		const JIndex newIndex  = info->GetNewIndex();
		itsStyles->MoveElementToIndex(origIndex, newIndex);
		MoveRow(origIndex, newIndex);
		}

	else if (sender == const_cast<JPtrArray<JString>*>(itsList) &&
			 message.Is(JOrderedSetT::kElementsSwapped))
		{
		const JOrderedSetT::ElementsSwapped* info =
			dynamic_cast(const JOrderedSetT::ElementsSwapped*, &message);
		assert( info != NULL );
		const JIndex index1 = info->GetIndex1();
		const JIndex index2 = info->GetIndex2();
		itsStyles->SwapElements(index1, index2);
//		SwapRows(index1, index2);
		}

	else if (sender == const_cast<JPtrArray<JString>*>(itsList) &&
			 message.Is(JOrderedSetT::kSorted))
		{
		assert( 0 );	// we don't allow this
		}

	else if (sender == const_cast<JPtrArray<JString>*>(itsList) &&
			 message.Is(JOrderedSetT::kElementChanged))
		{
		itsMinColWidth = 1;
		Refresh();
		}

	else
		{
		JXTable::Receive(sender, message);
		}
}

/******************************************************************************
 ApertureResized (virtual protected)

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

void
JXStringList::ApertureResized
	(
	const JCoordinate dw,
	const JCoordinate dh
	)
{
	JXTable::ApertureResized(dw,dh);
	AdjustColWidth();
}

/******************************************************************************
 AdjustColWidth (private)

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

void
JXStringList::AdjustColWidth()
{
	JCoordinate w = GetApertureWidth();
	if (w < itsMinColWidth)
		{
		w = itsMinColWidth;
		}
	SetColWidth(1,w);
}
