⛏️ index : haiku.git

/*
 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
 * Distributed under the terms of the MIT License.
 */

#include "table/Table.h"

#include <new>


// #pragma mark - TableField


class TableField : public BField {
public:
	TableField(int32 rowIndex)
		:
		fRowIndex(rowIndex)
	{
	}

	int32 RowIndex() const
	{
		return fRowIndex;
	}

	void SetRowIndex(int32 rowIndex)
	{
		fRowIndex = rowIndex;
	}

private:
	int32	fRowIndex;
};


// #pragma mark - TableModelListener


TableModelListener::~TableModelListener()
{
}


void
TableModelListener::TableRowsAdded(TableModel* model, int32 rowIndex,
	int32 count)
{
}


void
TableModelListener::TableRowsRemoved(TableModel* model, int32 rowIndex,
	int32 count)
{
}


void
TableModelListener::TableRowsChanged(TableModel* model, int32 rowIndex,
	int32 count)
{
}


void
TableModelListener::TableModelReset(TableModel* model)
{
}


// #pragma mark - TableModel


TableModel::~TableModel()
{
}


bool
TableModel::AddListener(TableModelListener* listener)
{
	return fListeners.AddItem(listener);
}


void
TableModel::RemoveListener(TableModelListener* listener)
{
	fListeners.RemoveItem(listener);
}


void
TableModel::NotifyRowsAdded(int32 rowIndex, int32 count)
{
	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--) {
		TableModelListener* listener = fListeners.ItemAt(i);
		listener->TableRowsAdded(this, rowIndex, count);
	}
}


void
TableModel::NotifyRowsRemoved(int32 rowIndex, int32 count)
{
	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--) {
		TableModelListener* listener = fListeners.ItemAt(i);
		listener->TableRowsRemoved(this, rowIndex, count);
	}
}


void
TableModel::NotifyRowsChanged(int32 rowIndex, int32 count)
{
	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--) {
		TableModelListener* listener = fListeners.ItemAt(i);
		listener->TableRowsChanged(this, rowIndex, count);
	}
}


void
TableModel::NotifyTableModelReset()
{
	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--) {
		TableModelListener* listener = fListeners.ItemAt(i);
		listener->TableModelReset(this);
	}
}


// #pragma mark - TableSelectionModel

TableSelectionModel::TableSelectionModel(Table* table)
	:
	fTable(table),
	fRows(NULL),
	fRowCount(-1)
{
}


TableSelectionModel::~TableSelectionModel()
{
	delete[] fRows;
}


int32
TableSelectionModel::CountRows()
{
	_Update();

	return fRowCount;
}


int32
TableSelectionModel::RowAt(int32 index)
{
	_Update();

	return index >= 0 && index < fRowCount ? fRows[index] : -1;
}


void
TableSelectionModel::_SelectionChanged()
{
	if (fRowCount >= 0) {
		fRowCount = -1;
		delete[] fRows;
		fRows = NULL;
	}
}


void
TableSelectionModel::_Update()
{
	if (fRowCount >= 0)
		return;

	// count the rows
	fRowCount = 0;
	BRow* row = NULL;
	while ((row = fTable->CurrentSelection(row)) != NULL)
		fRowCount++;

	if (fRowCount == 0)
		return;

	// allocate row array
	fRows = new(std::nothrow) int32[fRowCount];
	if (fRows == NULL) {
		fRowCount = 0;
		return;
	}

	// get the rows
	row = NULL;
	int32 index = 0;
	while ((row = fTable->CurrentSelection(row)) != NULL)
		fRows[index++] = fTable->_ModelIndexOfRow(row);
}


// #pragma mark - TableToolTipProvider


TableToolTipProvider::~TableToolTipProvider()
{
}


// #pragma mark - TableListener


TableListener::~TableListener()
{
}


void
TableListener::TableSelectionChanged(Table* table)
{
}


void
TableListener::TableRowInvoked(Table* table, int32 rowIndex)
{
}


void
TableListener::TableCellMouseDown(Table* table, int32 rowIndex,
	int32 columnIndex, BPoint screenWhere, uint32 buttons)
{
}


void
TableListener::TableCellMouseUp(Table* table, int32 rowIndex, int32 columnIndex,
	BPoint screenWhere, uint32 buttons)
{
}


// #pragma mark - Column


class Table::Column : public AbstractColumn {
public:
								Column(TableModel* model,
									TableColumn* tableColumn);
	virtual						~Column();

	virtual	void				SetModel(AbstractTableModelBase* model);

protected:
	virtual	void				DrawTitle(BRect rect, BView* targetView);
	virtual	void				DrawField(BField* field, BRect rect,
									BView* targetView);
	virtual	int					CompareFields(BField* field1, BField* field2);

	virtual	void				GetColumnName(BString* into) const;
	virtual	float				GetPreferredWidth(BField* field,
									BView* parent) const;

private:
			TableModel*			fModel;
};


Table::Column::Column(TableModel* model, TableColumn* tableColumn)
	:
	AbstractColumn(tableColumn),
	fModel(model)
{
}


Table::Column::~Column()
{
}


void
Table::Column::SetModel(AbstractTableModelBase* model)
{
	fModel = dynamic_cast<TableModel*>(model);
}


void
Table::Column::DrawTitle(BRect rect, BView* targetView)
{
	fTableColumn->DrawTitle(rect, targetView);
}


void
Table::Column::DrawField(BField* _field, BRect rect, BView* targetView)
{
	TableField* field = dynamic_cast<TableField*>(_field);
	if (field == NULL)
		return;

	int32 modelIndex = fTableColumn->ModelIndex();
	BVariant value;
	if (!fModel->GetValueAt(field->RowIndex(), modelIndex, value))
		return;
	fTableColumn->DrawValue(value, rect, targetView);
}


int
Table::Column::CompareFields(BField* _field1, BField* _field2)
{
	TableField* field1 = dynamic_cast<TableField*>(_field1);
	TableField* field2 = dynamic_cast<TableField*>(_field2);

	if (field1 == field2)
		return 0;

	if (field1 == NULL)
		return -1;
	if (field2 == NULL)
		return 1;

	int32 modelIndex = fTableColumn->ModelIndex();
	BVariant value1;
	bool valid1 = fModel->GetValueAt(field1->RowIndex(), modelIndex, value1);
	BVariant value2;
	bool valid2 = fModel->GetValueAt(field2->RowIndex(), modelIndex, value2);

	if (!valid1)
		return valid2 ? -1 : 0;
	if (!valid2)
		return 1;

	return fTableColumn->CompareValues(value1, value2);
}


void
Table::Column::GetColumnName(BString* into) const
{
	fTableColumn->GetColumnName(into);
}


float
Table::Column::GetPreferredWidth(BField* _field, BView* parent) const
{
	TableField* field = dynamic_cast<TableField*>(_field);
	if (field == NULL)
		return Width();

	int32 modelIndex = fTableColumn->ModelIndex();
	BVariant value;
	if (!fModel->GetValueAt(field->RowIndex(), modelIndex, value))
		return Width();
	return fTableColumn->GetPreferredWidth(value, parent);
}


// #pragma mark - Table


Table::Table(const char* name, uint32 flags, border_style borderStyle,
	bool showHorizontalScrollbar)
	:
	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
	fModel(NULL),
	fToolTipProvider(NULL),
	fSelectionModel(this),
	fIgnoreSelectionChange(0)
{
}


Table::Table(TableModel* model, const char* name, uint32 flags,
	border_style borderStyle, bool showHorizontalScrollbar)
	:
	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
	fModel(NULL),
	fToolTipProvider(NULL),
	fSelectionModel(this),
	fIgnoreSelectionChange(0)
{
	SetTableModel(model);
}


Table::~Table()
{
	// rows are deleted by the BColumnListView destructor automatically
}


void
Table::SetTableModel(TableModel* model)
{
	if (model == fModel)
		return;

	if (fModel != NULL) {
		fModel->RemoveListener(this);

		fRows.MakeEmpty();
		Clear();

		for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
			column->SetModel(NULL);
	}

	fModel = model;

	if (fModel == NULL)
		return;

	fModel->AddListener(this);

	for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
		column->SetModel(fModel);

	TableRowsAdded(fModel, 0, fModel->CountRows());
}


void
Table::SetToolTipProvider(TableToolTipProvider* toolTipProvider)
{
	fToolTipProvider = toolTipProvider;
}


TableSelectionModel*
Table::SelectionModel()
{
	return &fSelectionModel;
}


void
Table::SelectRow(int32 rowIndex, bool extendSelection)
{
	BRow* row = fRows.ItemAt(rowIndex);
	if (row == NULL)
		return;

	if (!extendSelection) {
		fIgnoreSelectionChange++;
		DeselectAll();
		fIgnoreSelectionChange--;
	}

	AddToSelection(row);
}


void
Table::DeselectRow(int32 rowIndex)
{
	if (BRow* row = fRows.ItemAt(rowIndex))
		Deselect(row);
}


void
Table::DeselectAllRows()
{
	DeselectAll();
}


bool
Table::AddTableListener(TableListener* listener)
{
	return fListeners.AddItem(listener);
}


void
Table::RemoveTableListener(TableListener* listener)
{
	fListeners.RemoveItem(listener);
}


status_t
Table::GetCellRectAt(int32 rowIndex, int32 colIndex, BRect& _output) const
{
	BRow* row = fRows.ItemAt(rowIndex);
	if (row == NULL)
		return B_ENTRY_NOT_FOUND;

	AbstractColumn* column = fColumns.ItemAt(colIndex);
	if (column == NULL)
		return B_ENTRY_NOT_FOUND;

	_output = GetFieldRect(row, column);

	return B_OK;
}


bool
Table::GetToolTipAt(BPoint point, BToolTip** _tip)
{
	if (fToolTipProvider == NULL)
		return AbstractTable::GetToolTipAt(point, _tip);

	// get the table row
	BRow* row = RowAt(point);
	int32 rowIndex = row != NULL ? _ModelIndexOfRow(row) : -1;
	if (rowIndex < 0)
		return AbstractTable::GetToolTipAt(point, _tip);

	// get the table column
	BColumn* column = ColumnAt(point);
	int32 columnIndex = column != NULL ? column->LogicalFieldNum() : -1;

	return fToolTipProvider->GetToolTipForTableCell(rowIndex, columnIndex,
		_tip);
}


void
Table::SelectionChanged()
{
	if (fIgnoreSelectionChange > 0)
		return;

	fSelectionModel._SelectionChanged();

	if (!fListeners.IsEmpty()) {
		int32 listenerCount = fListeners.CountItems();
		for (int32 i = listenerCount - 1; i >= 0; i--)
			fListeners.ItemAt(i)->TableSelectionChanged(this);
	}
}


AbstractTable::AbstractColumn*
Table::CreateColumn(TableColumn* column)
{
	return new Column(fModel, column);
}


void
Table::ColumnMouseDown(AbstractColumn* column, BRow* row, BField* field,
	BPoint screenWhere, uint32 buttons)
{
	if (!fListeners.IsEmpty()) {
		// get the table row and column indices
		int32 rowIndex = _ModelIndexOfRow(row);
		int32 columnIndex = column->LogicalFieldNum();
		if (rowIndex < 0 || columnIndex < 0)
			return;

		// notify listeners
		int32 listenerCount = fListeners.CountItems();
		for (int32 i = listenerCount - 1; i >= 0; i--) {
			fListeners.ItemAt(i)->TableCellMouseDown(this, rowIndex,
				columnIndex, screenWhere, buttons);
		}
	}
}


void
Table::ColumnMouseUp(AbstractColumn* column, BRow* row, BField* field,
	BPoint screenWhere, uint32 buttons)
{
	if (!fListeners.IsEmpty()) {
		// get the table row and column indices
		int32 rowIndex = _ModelIndexOfRow(row);
		int32 columnIndex = column->LogicalFieldNum();
		if (rowIndex < 0 || columnIndex < 0)
			return;

		// notify listeners
		int32 listenerCount = fListeners.CountItems();
		for (int32 i = listenerCount - 1; i >= 0; i--) {
			fListeners.ItemAt(i)->TableCellMouseUp(this, rowIndex, columnIndex,
				screenWhere, buttons);
		}
	}
}


void
Table::TableRowsAdded(TableModel* model, int32 rowIndex, int32 count)
{
	// create the rows
	int32 endRow = rowIndex + count;
	int32 columnCount = fModel->CountColumns();

	for (int32 i = rowIndex; i < endRow; i++) {
		// create row
		BRow* row = new(std::nothrow) BRow();
		if (row == NULL) {
			// TODO: Report error!
			return;
		}

		// add dummy fields to row
		for (int32 columnIndex = 0; columnIndex < columnCount; columnIndex++) {
			// It would be nice to create only a single field and set it for all
			// columns, but the row takes ultimate ownership, so it have to be
			// individual objects.
			TableField* field = new(std::nothrow) TableField(i);
			if (field == NULL) {
				// TODO: Report error!
				delete row;
				return;
			}

			row->SetField(field, columnIndex);
		}

		// add row
		if (!fRows.AddItem(row, i)) {
			// TODO: Report error!
			delete row;
			return;
		}

		AddRow(row, i);
	}

	// re-index the subsequent rows
	_UpdateRowIndices(endRow);
}


void
Table::TableRowsRemoved(TableModel* model, int32 rowIndex, int32 count)
{
	if (rowIndex == 0 && count == fRows.CountItems()) {
		fRows.MakeEmpty();
		Clear();
		return;
	}

	for (int32 i = rowIndex + count - 1; i >= rowIndex; i--) {
		if (BRow* row = fRows.RemoveItemAt(i)) {
			RemoveRow(row);
			delete row;
		}
	}

	// re-index the subsequent rows
	_UpdateRowIndices(rowIndex);
}


void
Table::TableRowsChanged(TableModel* model, int32 rowIndex, int32 count)
{
	int32 endIndex = rowIndex + count;
	for (int32 i = rowIndex; i < endIndex; i++) {
		if (BRow* row = fRows.ItemAt(i))
			UpdateRow(row);
	}
}


void
Table::TableModelReset(TableModel* model)
{
	Clear();
	TableRowsAdded(model, 0, model->CountRows());
}


void
Table::ItemInvoked()
{
	if (fListeners.IsEmpty())
		return;

	int32 index = _ModelIndexOfRow(CurrentSelection());
	if (index < 0)
		return;

	int32 listenerCount = fListeners.CountItems();
	for (int32 i = listenerCount - 1; i >= 0; i--)
		fListeners.ItemAt(i)->TableRowInvoked(this, index);
}


void
Table::_UpdateRowIndices(int32 fromIndex)
{
	for (int32 i = fromIndex; BRow* row = fRows.ItemAt(i); i++) {
		for (int32 k = 0;
			TableField* field = dynamic_cast<TableField*>(row->GetField(k));
			k++) {
			field->SetRowIndex(i);
		}
	}
}


int32
Table::_ModelIndexOfRow(BRow* row)
{
	if (row == NULL)
		return -1;

	TableField* field = dynamic_cast<TableField*>(row->GetField(0));
	if (field == NULL)
		return -1;

	return field->RowIndex();
}