1/*
2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "table/Table.h"
7
8#include <new>
9
10
11// #pragma mark - TableField
12
13
14class TableField : public BField {
15public:
16	TableField(int32 rowIndex)
17		:
18		fRowIndex(rowIndex)
19	{
20	}
21
22	int32 RowIndex() const
23	{
24		return fRowIndex;
25	}
26
27	void SetRowIndex(int32 rowIndex)
28	{
29		fRowIndex = rowIndex;
30	}
31
32private:
33	int32	fRowIndex;
34};
35
36
37// #pragma mark - TableModelListener
38
39
40TableModelListener::~TableModelListener()
41{
42}
43
44
45void
46TableModelListener::TableRowsAdded(TableModel* model, int32 rowIndex,
47	int32 count)
48{
49}
50
51
52void
53TableModelListener::TableRowsRemoved(TableModel* model, int32 rowIndex,
54	int32 count)
55{
56}
57
58
59void
60TableModelListener::TableRowsChanged(TableModel* model, int32 rowIndex,
61	int32 count)
62{
63}
64
65
66void
67TableModelListener::TableModelReset(TableModel* model)
68{
69}
70
71
72// #pragma mark - TableModel
73
74
75TableModel::~TableModel()
76{
77}
78
79
80bool
81TableModel::AddListener(TableModelListener* listener)
82{
83	return fListeners.AddItem(listener);
84}
85
86
87void
88TableModel::RemoveListener(TableModelListener* listener)
89{
90	fListeners.RemoveItem(listener);
91}
92
93
94void
95TableModel::NotifyRowsAdded(int32 rowIndex, int32 count)
96{
97	int32 listenerCount = fListeners.CountItems();
98	for (int32 i = listenerCount - 1; i >= 0; i--) {
99		TableModelListener* listener = fListeners.ItemAt(i);
100		listener->TableRowsAdded(this, rowIndex, count);
101	}
102}
103
104
105void
106TableModel::NotifyRowsRemoved(int32 rowIndex, int32 count)
107{
108	int32 listenerCount = fListeners.CountItems();
109	for (int32 i = listenerCount - 1; i >= 0; i--) {
110		TableModelListener* listener = fListeners.ItemAt(i);
111		listener->TableRowsRemoved(this, rowIndex, count);
112	}
113}
114
115
116void
117TableModel::NotifyRowsChanged(int32 rowIndex, int32 count)
118{
119	int32 listenerCount = fListeners.CountItems();
120	for (int32 i = listenerCount - 1; i >= 0; i--) {
121		TableModelListener* listener = fListeners.ItemAt(i);
122		listener->TableRowsChanged(this, rowIndex, count);
123	}
124}
125
126
127void
128TableModel::NotifyTableModelReset()
129{
130	int32 listenerCount = fListeners.CountItems();
131	for (int32 i = listenerCount - 1; i >= 0; i--) {
132		TableModelListener* listener = fListeners.ItemAt(i);
133		listener->TableModelReset(this);
134	}
135}
136
137
138// #pragma mark - TableSelectionModel
139
140TableSelectionModel::TableSelectionModel(Table* table)
141	:
142	fTable(table),
143	fRows(NULL),
144	fRowCount(-1)
145{
146}
147
148
149TableSelectionModel::~TableSelectionModel()
150{
151	delete[] fRows;
152}
153
154
155int32
156TableSelectionModel::CountRows()
157{
158	_Update();
159
160	return fRowCount;
161}
162
163
164int32
165TableSelectionModel::RowAt(int32 index)
166{
167	_Update();
168
169	return index >= 0 && index < fRowCount ? fRows[index] : -1;
170}
171
172
173void
174TableSelectionModel::_SelectionChanged()
175{
176	if (fRowCount >= 0) {
177		fRowCount = -1;
178		delete[] fRows;
179		fRows = NULL;
180	}
181}
182
183
184void
185TableSelectionModel::_Update()
186{
187	if (fRowCount >= 0)
188		return;
189
190	// count the rows
191	fRowCount = 0;
192	BRow* row = NULL;
193	while ((row = fTable->CurrentSelection(row)) != NULL)
194		fRowCount++;
195
196	if (fRowCount == 0)
197		return;
198
199	// allocate row array
200	fRows = new(std::nothrow) int32[fRowCount];
201	if (fRows == NULL) {
202		fRowCount = 0;
203		return;
204	}
205
206	// get the rows
207	row = NULL;
208	int32 index = 0;
209	while ((row = fTable->CurrentSelection(row)) != NULL)
210		fRows[index++] = fTable->_ModelIndexOfRow(row);
211}
212
213
214// #pragma mark - TableToolTipProvider
215
216
217TableToolTipProvider::~TableToolTipProvider()
218{
219}
220
221
222// #pragma mark - TableListener
223
224
225TableListener::~TableListener()
226{
227}
228
229
230void
231TableListener::TableSelectionChanged(Table* table)
232{
233}
234
235
236void
237TableListener::TableRowInvoked(Table* table, int32 rowIndex)
238{
239}
240
241
242void
243TableListener::TableCellMouseDown(Table* table, int32 rowIndex,
244	int32 columnIndex, BPoint screenWhere, uint32 buttons)
245{
246}
247
248
249void
250TableListener::TableCellMouseUp(Table* table, int32 rowIndex, int32 columnIndex,
251	BPoint screenWhere, uint32 buttons)
252{
253}
254
255
256// #pragma mark - Column
257
258
259class Table::Column : public AbstractColumn {
260public:
261								Column(TableModel* model,
262									TableColumn* tableColumn);
263	virtual						~Column();
264
265	virtual	void				SetModel(AbstractTableModelBase* model);
266
267protected:
268	virtual	void				DrawTitle(BRect rect, BView* targetView);
269	virtual	void				DrawField(BField* field, BRect rect,
270									BView* targetView);
271	virtual	int					CompareFields(BField* field1, BField* field2);
272
273	virtual	void				GetColumnName(BString* into) const;
274	virtual	float				GetPreferredWidth(BField* field,
275									BView* parent) const;
276
277private:
278			TableModel*			fModel;
279};
280
281
282Table::Column::Column(TableModel* model, TableColumn* tableColumn)
283	:
284	AbstractColumn(tableColumn),
285	fModel(model)
286{
287}
288
289
290Table::Column::~Column()
291{
292}
293
294
295void
296Table::Column::SetModel(AbstractTableModelBase* model)
297{
298	fModel = dynamic_cast<TableModel*>(model);
299}
300
301
302void
303Table::Column::DrawTitle(BRect rect, BView* targetView)
304{
305	fTableColumn->DrawTitle(rect, targetView);
306}
307
308
309void
310Table::Column::DrawField(BField* _field, BRect rect, BView* targetView)
311{
312	TableField* field = dynamic_cast<TableField*>(_field);
313	if (field == NULL)
314		return;
315
316	int32 modelIndex = fTableColumn->ModelIndex();
317	BVariant value;
318	if (!fModel->GetValueAt(field->RowIndex(), modelIndex, value))
319		return;
320	fTableColumn->DrawValue(value, rect, targetView);
321}
322
323
324int
325Table::Column::CompareFields(BField* _field1, BField* _field2)
326{
327	TableField* field1 = dynamic_cast<TableField*>(_field1);
328	TableField* field2 = dynamic_cast<TableField*>(_field2);
329
330	if (field1 == field2)
331		return 0;
332
333	if (field1 == NULL)
334		return -1;
335	if (field2 == NULL)
336		return 1;
337
338	int32 modelIndex = fTableColumn->ModelIndex();
339	BVariant value1;
340	bool valid1 = fModel->GetValueAt(field1->RowIndex(), modelIndex, value1);
341	BVariant value2;
342	bool valid2 = fModel->GetValueAt(field2->RowIndex(), modelIndex, value2);
343
344	if (!valid1)
345		return valid2 ? -1 : 0;
346	if (!valid2)
347		return 1;
348
349	return fTableColumn->CompareValues(value1, value2);
350}
351
352
353void
354Table::Column::GetColumnName(BString* into) const
355{
356	fTableColumn->GetColumnName(into);
357}
358
359
360float
361Table::Column::GetPreferredWidth(BField* _field, BView* parent) const
362{
363	TableField* field = dynamic_cast<TableField*>(_field);
364	if (field == NULL)
365		return Width();
366
367	int32 modelIndex = fTableColumn->ModelIndex();
368	BVariant value;
369	if (!fModel->GetValueAt(field->RowIndex(), modelIndex, value))
370		return Width();
371	return fTableColumn->GetPreferredWidth(value, parent);
372}
373
374
375// #pragma mark - Table
376
377
378Table::Table(const char* name, uint32 flags, border_style borderStyle,
379	bool showHorizontalScrollbar)
380	:
381	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
382	fModel(NULL),
383	fToolTipProvider(NULL),
384	fSelectionModel(this),
385	fIgnoreSelectionChange(0)
386{
387}
388
389
390Table::Table(TableModel* model, const char* name, uint32 flags,
391	border_style borderStyle, bool showHorizontalScrollbar)
392	:
393	AbstractTable(name, flags, borderStyle, showHorizontalScrollbar),
394	fModel(NULL),
395	fToolTipProvider(NULL),
396	fSelectionModel(this),
397	fIgnoreSelectionChange(0)
398{
399	SetTableModel(model);
400}
401
402
403Table::~Table()
404{
405	// rows are deleted by the BColumnListView destructor automatically
406}
407
408
409void
410Table::SetTableModel(TableModel* model)
411{
412	if (model == fModel)
413		return;
414
415	if (fModel != NULL) {
416		fModel->RemoveListener(this);
417
418		fRows.MakeEmpty();
419		Clear();
420
421		for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
422			column->SetModel(NULL);
423	}
424
425	fModel = model;
426
427	if (fModel == NULL)
428		return;
429
430	fModel->AddListener(this);
431
432	for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
433		column->SetModel(fModel);
434
435	TableRowsAdded(fModel, 0, fModel->CountRows());
436}
437
438
439void
440Table::SetToolTipProvider(TableToolTipProvider* toolTipProvider)
441{
442	fToolTipProvider = toolTipProvider;
443}
444
445
446TableSelectionModel*
447Table::SelectionModel()
448{
449	return &fSelectionModel;
450}
451
452
453void
454Table::SelectRow(int32 rowIndex, bool extendSelection)
455{
456	BRow* row = fRows.ItemAt(rowIndex);
457	if (row == NULL)
458		return;
459
460	if (!extendSelection) {
461		fIgnoreSelectionChange++;
462		DeselectAll();
463		fIgnoreSelectionChange--;
464	}
465
466	AddToSelection(row);
467}
468
469
470void
471Table::DeselectRow(int32 rowIndex)
472{
473	if (BRow* row = fRows.ItemAt(rowIndex))
474		Deselect(row);
475}
476
477
478void
479Table::DeselectAllRows()
480{
481	DeselectAll();
482}
483
484
485bool
486Table::AddTableListener(TableListener* listener)
487{
488	return fListeners.AddItem(listener);
489}
490
491
492void
493Table::RemoveTableListener(TableListener* listener)
494{
495	fListeners.RemoveItem(listener);
496}
497
498
499status_t
500Table::GetCellRectAt(int32 rowIndex, int32 colIndex, BRect& _output) const
501{
502	BRow* row = fRows.ItemAt(rowIndex);
503	if (row == NULL)
504		return B_ENTRY_NOT_FOUND;
505
506	AbstractColumn* column = fColumns.ItemAt(colIndex);
507	if (column == NULL)
508		return B_ENTRY_NOT_FOUND;
509
510	_output = GetFieldRect(row, column);
511
512	return B_OK;
513}
514
515
516bool
517Table::GetToolTipAt(BPoint point, BToolTip** _tip)
518{
519	if (fToolTipProvider == NULL)
520		return AbstractTable::GetToolTipAt(point, _tip);
521
522	// get the table row
523	BRow* row = RowAt(point);
524	int32 rowIndex = row != NULL ? _ModelIndexOfRow(row) : -1;
525	if (rowIndex < 0)
526		return AbstractTable::GetToolTipAt(point, _tip);
527
528	// get the table column
529	BColumn* column = ColumnAt(point);
530	int32 columnIndex = column != NULL ? column->LogicalFieldNum() : -1;
531
532	return fToolTipProvider->GetToolTipForTableCell(rowIndex, columnIndex,
533		_tip);
534}
535
536
537void
538Table::SelectionChanged()
539{
540	if (fIgnoreSelectionChange > 0)
541		return;
542
543	fSelectionModel._SelectionChanged();
544
545	if (!fListeners.IsEmpty()) {
546		int32 listenerCount = fListeners.CountItems();
547		for (int32 i = listenerCount - 1; i >= 0; i--)
548			fListeners.ItemAt(i)->TableSelectionChanged(this);
549	}
550}
551
552
553AbstractTable::AbstractColumn*
554Table::CreateColumn(TableColumn* column)
555{
556	return new Column(fModel, column);
557}
558
559
560void
561Table::ColumnMouseDown(AbstractColumn* column, BRow* row, BField* field,
562	BPoint screenWhere, uint32 buttons)
563{
564	if (!fListeners.IsEmpty()) {
565		// get the table row and column indices
566		int32 rowIndex = _ModelIndexOfRow(row);
567		int32 columnIndex = column->LogicalFieldNum();
568		if (rowIndex < 0 || columnIndex < 0)
569			return;
570
571		// notify listeners
572		int32 listenerCount = fListeners.CountItems();
573		for (int32 i = listenerCount - 1; i >= 0; i--) {
574			fListeners.ItemAt(i)->TableCellMouseDown(this, rowIndex,
575				columnIndex, screenWhere, buttons);
576		}
577	}
578}
579
580
581void
582Table::ColumnMouseUp(AbstractColumn* column, BRow* row, BField* field,
583	BPoint screenWhere, uint32 buttons)
584{
585	if (!fListeners.IsEmpty()) {
586		// get the table row and column indices
587		int32 rowIndex = _ModelIndexOfRow(row);
588		int32 columnIndex = column->LogicalFieldNum();
589		if (rowIndex < 0 || columnIndex < 0)
590			return;
591
592		// notify listeners
593		int32 listenerCount = fListeners.CountItems();
594		for (int32 i = listenerCount - 1; i >= 0; i--) {
595			fListeners.ItemAt(i)->TableCellMouseUp(this, rowIndex, columnIndex,
596				screenWhere, buttons);
597		}
598	}
599}
600
601
602void
603Table::TableRowsAdded(TableModel* model, int32 rowIndex, int32 count)
604{
605	// create the rows
606	int32 endRow = rowIndex + count;
607	int32 columnCount = fModel->CountColumns();
608
609	for (int32 i = rowIndex; i < endRow; i++) {
610		// create row
611		BRow* row = new(std::nothrow) BRow();
612		if (row == NULL) {
613			// TODO: Report error!
614			return;
615		}
616
617		// add dummy fields to row
618		for (int32 columnIndex = 0; columnIndex < columnCount; columnIndex++) {
619			// It would be nice to create only a single field and set it for all
620			// columns, but the row takes ultimate ownership, so it have to be
621			// individual objects.
622			TableField* field = new(std::nothrow) TableField(i);
623			if (field == NULL) {
624				// TODO: Report error!
625				delete row;
626				return;
627			}
628
629			row->SetField(field, columnIndex);
630		}
631
632		// add row
633		if (!fRows.AddItem(row, i)) {
634			// TODO: Report error!
635			delete row;
636			return;
637		}
638
639		AddRow(row, i);
640	}
641
642	// re-index the subsequent rows
643	_UpdateRowIndices(endRow);
644}
645
646
647void
648Table::TableRowsRemoved(TableModel* model, int32 rowIndex, int32 count)
649{
650	if (rowIndex == 0 && count == fRows.CountItems()) {
651		fRows.MakeEmpty();
652		Clear();
653		return;
654	}
655
656	for (int32 i = rowIndex + count - 1; i >= rowIndex; i--) {
657		if (BRow* row = fRows.RemoveItemAt(i)) {
658			RemoveRow(row);
659			delete row;
660		}
661	}
662
663	// re-index the subsequent rows
664	_UpdateRowIndices(rowIndex);
665}
666
667
668void
669Table::TableRowsChanged(TableModel* model, int32 rowIndex, int32 count)
670{
671	int32 endIndex = rowIndex + count;
672	for (int32 i = rowIndex; i < endIndex; i++) {
673		if (BRow* row = fRows.ItemAt(i))
674			UpdateRow(row);
675	}
676}
677
678
679void
680Table::TableModelReset(TableModel* model)
681{
682	Clear();
683	TableRowsAdded(model, 0, model->CountRows());
684}
685
686
687void
688Table::ItemInvoked()
689{
690	if (fListeners.IsEmpty())
691		return;
692
693	int32 index = _ModelIndexOfRow(CurrentSelection());
694	if (index < 0)
695		return;
696
697	int32 listenerCount = fListeners.CountItems();
698	for (int32 i = listenerCount - 1; i >= 0; i--)
699		fListeners.ItemAt(i)->TableRowInvoked(this, index);
700}
701
702
703void
704Table::_UpdateRowIndices(int32 fromIndex)
705{
706	for (int32 i = fromIndex; BRow* row = fRows.ItemAt(i); i++) {
707		for (int32 k = 0;
708			TableField* field = dynamic_cast<TableField*>(row->GetField(k));
709			k++) {
710			field->SetRowIndex(i);
711		}
712	}
713}
714
715
716int32
717Table::_ModelIndexOfRow(BRow* row)
718{
719	if (row == NULL)
720		return -1;
721
722	TableField* field = dynamic_cast<TableField*>(row->GetField(0));
723	if (field == NULL)
724		return -1;
725
726	return field->RowIndex();
727}
728