xref: /haiku/src/apps/debuganalyzer/gui/table/Table.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
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 
14 class TableField : public BField {
15 public:
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 
32 private:
33 	int32	fRowIndex;
34 };
35 
36 
37 // #pragma mark - TableModelListener
38 
39 
40 TableModelListener::~TableModelListener()
41 {
42 }
43 
44 
45 void
46 TableModelListener::TableRowsAdded(TableModel* model, int32 rowIndex,
47 	int32 count)
48 {
49 }
50 
51 
52 void
53 TableModelListener::TableRowsRemoved(TableModel* model, int32 rowIndex,
54 	int32 count)
55 {
56 }
57 
58 
59 void
60 TableModelListener::TableRowsChanged(TableModel* model, int32 rowIndex,
61 	int32 count)
62 {
63 }
64 
65 
66 void
67 TableModelListener::TableModelReset(TableModel* model)
68 {
69 }
70 
71 
72 // #pragma mark - TableModel
73 
74 
75 TableModel::~TableModel()
76 {
77 }
78 
79 
80 bool
81 TableModel::AddListener(TableModelListener* listener)
82 {
83 	return fListeners.AddItem(listener);
84 }
85 
86 
87 void
88 TableModel::RemoveListener(TableModelListener* listener)
89 {
90 	fListeners.RemoveItem(listener);
91 }
92 
93 
94 void
95 TableModel::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 
105 void
106 TableModel::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 
116 void
117 TableModel::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 
127 void
128 TableModel::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 
140 TableSelectionModel::TableSelectionModel(Table* table)
141 	:
142 	fTable(table),
143 	fRows(NULL),
144 	fRowCount(-1)
145 {
146 }
147 
148 
149 TableSelectionModel::~TableSelectionModel()
150 {
151 	delete[] fRows;
152 }
153 
154 
155 int32
156 TableSelectionModel::CountRows()
157 {
158 	_Update();
159 
160 	return fRowCount;
161 }
162 
163 
164 int32
165 TableSelectionModel::RowAt(int32 index)
166 {
167 	_Update();
168 
169 	return index >= 0 && index < fRowCount ? fRows[index] : -1;
170 }
171 
172 
173 void
174 TableSelectionModel::_SelectionChanged()
175 {
176 	if (fRowCount >= 0) {
177 		fRowCount = -1;
178 		delete[] fRows;
179 		fRows = NULL;
180 	}
181 }
182 
183 
184 void
185 TableSelectionModel::_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 
217 TableToolTipProvider::~TableToolTipProvider()
218 {
219 }
220 
221 
222 // #pragma mark - TableListener
223 
224 
225 TableListener::~TableListener()
226 {
227 }
228 
229 
230 void
231 TableListener::TableSelectionChanged(Table* table)
232 {
233 }
234 
235 
236 void
237 TableListener::TableRowInvoked(Table* table, int32 rowIndex)
238 {
239 }
240 
241 
242 void
243 TableListener::TableCellMouseDown(Table* table, int32 rowIndex,
244 	int32 columnIndex, BPoint screenWhere, uint32 buttons)
245 {
246 }
247 
248 
249 void
250 TableListener::TableCellMouseUp(Table* table, int32 rowIndex, int32 columnIndex,
251 	BPoint screenWhere, uint32 buttons)
252 {
253 }
254 
255 
256 // #pragma mark - Column
257 
258 
259 class Table::Column : public AbstractColumn {
260 public:
261 								Column(TableModel* model,
262 									TableColumn* tableColumn);
263 	virtual						~Column();
264 
265 	virtual	void				SetModel(AbstractTableModelBase* model);
266 
267 protected:
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 
277 private:
278 			TableModel*			fModel;
279 };
280 
281 
282 Table::Column::Column(TableModel* model, TableColumn* tableColumn)
283 	:
284 	AbstractColumn(tableColumn),
285 	fModel(model)
286 {
287 }
288 
289 
290 Table::Column::~Column()
291 {
292 }
293 
294 
295 void
296 Table::Column::SetModel(AbstractTableModelBase* model)
297 {
298 	fModel = dynamic_cast<TableModel*>(model);
299 }
300 
301 
302 void
303 Table::Column::DrawTitle(BRect rect, BView* targetView)
304 {
305 	fTableColumn->DrawTitle(rect, targetView);
306 }
307 
308 
309 void
310 Table::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 
324 int
325 Table::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 
353 void
354 Table::Column::GetColumnName(BString* into) const
355 {
356 	fTableColumn->GetColumnName(into);
357 }
358 
359 
360 float
361 Table::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 
378 Table::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 
390 Table::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 
403 Table::~Table()
404 {
405 	for (int32 i = CountColumns() - 1; i >= 0; i--)
406 		RemoveColumn(ColumnAt(i));
407 
408 	// rows are deleted by the BColumnListView destructor automatically
409 }
410 
411 
412 void
413 Table::SetTableModel(TableModel* model)
414 {
415 	if (model == fModel)
416 		return;
417 
418 	if (fModel != NULL) {
419 		fModel->RemoveListener(this);
420 
421 		fRows.MakeEmpty();
422 		Clear();
423 
424 		for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
425 			column->SetModel(NULL);
426 	}
427 
428 	fModel = model;
429 
430 	if (fModel == NULL)
431 		return;
432 
433 	fModel->AddListener(this);
434 
435 	for (int32 i = 0; AbstractColumn* column = fColumns.ItemAt(i); i++)
436 		column->SetModel(fModel);
437 
438 	TableRowsAdded(fModel, 0, fModel->CountRows());
439 }
440 
441 
442 void
443 Table::SetToolTipProvider(TableToolTipProvider* toolTipProvider)
444 {
445 	fToolTipProvider = toolTipProvider;
446 }
447 
448 
449 TableSelectionModel*
450 Table::SelectionModel()
451 {
452 	return &fSelectionModel;
453 }
454 
455 
456 void
457 Table::SelectRow(int32 rowIndex, bool extendSelection)
458 {
459 	BRow* row = fRows.ItemAt(rowIndex);
460 	if (row == NULL)
461 		return;
462 
463 	if (!extendSelection) {
464 		fIgnoreSelectionChange++;
465 		DeselectAll();
466 		fIgnoreSelectionChange--;
467 	}
468 
469 	AddToSelection(row);
470 }
471 
472 
473 void
474 Table::DeselectRow(int32 rowIndex)
475 {
476 	if (BRow* row = fRows.ItemAt(rowIndex))
477 		Deselect(row);
478 }
479 
480 
481 void
482 Table::DeselectAllRows()
483 {
484 	DeselectAll();
485 }
486 
487 
488 bool
489 Table::AddTableListener(TableListener* listener)
490 {
491 	return fListeners.AddItem(listener);
492 }
493 
494 
495 void
496 Table::RemoveTableListener(TableListener* listener)
497 {
498 	fListeners.RemoveItem(listener);
499 }
500 
501 
502 status_t
503 Table::GetCellRectAt(int32 rowIndex, int32 colIndex, BRect& _output) const
504 {
505 	BRow* row = fRows.ItemAt(rowIndex);
506 	if (row == NULL)
507 		return B_ENTRY_NOT_FOUND;
508 
509 	AbstractColumn* column = fColumns.ItemAt(colIndex);
510 	if (column == NULL)
511 		return B_ENTRY_NOT_FOUND;
512 
513 	_output = GetFieldRect(row, column);
514 
515 	return B_OK;
516 }
517 
518 
519 bool
520 Table::GetToolTipAt(BPoint point, BToolTip** _tip)
521 {
522 	if (fToolTipProvider == NULL)
523 		return AbstractTable::GetToolTipAt(point, _tip);
524 
525 	// get the table row
526 	BRow* row = RowAt(point);
527 	int32 rowIndex = row != NULL ? _ModelIndexOfRow(row) : -1;
528 	if (rowIndex < 0)
529 		return AbstractTable::GetToolTipAt(point, _tip);
530 
531 	// get the table column
532 	BColumn* column = ColumnAt(point);
533 	int32 columnIndex = column != NULL ? column->LogicalFieldNum() : -1;
534 
535 	return fToolTipProvider->GetToolTipForTableCell(rowIndex, columnIndex,
536 		_tip);
537 }
538 
539 
540 void
541 Table::SelectionChanged()
542 {
543 	if (fIgnoreSelectionChange > 0)
544 		return;
545 
546 	fSelectionModel._SelectionChanged();
547 
548 	if (!fListeners.IsEmpty()) {
549 		int32 listenerCount = fListeners.CountItems();
550 		for (int32 i = listenerCount - 1; i >= 0; i--)
551 			fListeners.ItemAt(i)->TableSelectionChanged(this);
552 	}
553 }
554 
555 
556 AbstractTable::AbstractColumn*
557 Table::CreateColumn(TableColumn* column)
558 {
559 	return new Column(fModel, column);
560 }
561 
562 
563 void
564 Table::ColumnMouseDown(AbstractColumn* column, BRow* row, BField* field,
565 	BPoint screenWhere, uint32 buttons)
566 {
567 	if (!fListeners.IsEmpty()) {
568 		// get the table row and column indices
569 		int32 rowIndex = _ModelIndexOfRow(row);
570 		int32 columnIndex = column->LogicalFieldNum();
571 		if (rowIndex < 0 || columnIndex < 0)
572 			return;
573 
574 		// notify listeners
575 		int32 listenerCount = fListeners.CountItems();
576 		for (int32 i = listenerCount - 1; i >= 0; i--) {
577 			fListeners.ItemAt(i)->TableCellMouseDown(this, rowIndex,
578 				columnIndex, screenWhere, buttons);
579 		}
580 	}
581 }
582 
583 
584 void
585 Table::ColumnMouseUp(AbstractColumn* column, BRow* row, BField* field,
586 	BPoint screenWhere, uint32 buttons)
587 {
588 	if (!fListeners.IsEmpty()) {
589 		// get the table row and column indices
590 		int32 rowIndex = _ModelIndexOfRow(row);
591 		int32 columnIndex = column->LogicalFieldNum();
592 		if (rowIndex < 0 || columnIndex < 0)
593 			return;
594 
595 		// notify listeners
596 		int32 listenerCount = fListeners.CountItems();
597 		for (int32 i = listenerCount - 1; i >= 0; i--) {
598 			fListeners.ItemAt(i)->TableCellMouseUp(this, rowIndex, columnIndex,
599 				screenWhere, buttons);
600 		}
601 	}
602 }
603 
604 
605 void
606 Table::TableRowsAdded(TableModel* model, int32 rowIndex, int32 count)
607 {
608 	// create the rows
609 	int32 endRow = rowIndex + count;
610 	int32 columnCount = fModel->CountColumns();
611 
612 	for (int32 i = rowIndex; i < endRow; i++) {
613 		// create row
614 		BRow* row = new(std::nothrow) BRow();
615 		if (row == NULL) {
616 			// TODO: Report error!
617 			return;
618 		}
619 
620 		// add dummy fields to row
621 		for (int32 columnIndex = 0; columnIndex < columnCount; columnIndex++) {
622 			// It would be nice to create only a single field and set it for all
623 			// columns, but the row takes ultimate ownership, so it have to be
624 			// individual objects.
625 			TableField* field = new(std::nothrow) TableField(i);
626 			if (field == NULL) {
627 				// TODO: Report error!
628 				delete row;
629 				return;
630 			}
631 
632 			row->SetField(field, columnIndex);
633 		}
634 
635 		// add row
636 		if (!fRows.AddItem(row, i)) {
637 			// TODO: Report error!
638 			delete row;
639 			return;
640 		}
641 
642 		AddRow(row, i);
643 	}
644 
645 	// re-index the subsequent rows
646 	_UpdateRowIndices(endRow);
647 }
648 
649 
650 void
651 Table::TableRowsRemoved(TableModel* model, int32 rowIndex, int32 count)
652 {
653 	if (rowIndex == 0 && count == fRows.CountItems()) {
654 		fRows.MakeEmpty();
655 		Clear();
656 		return;
657 	}
658 
659 	for (int32 i = rowIndex + count - 1; i >= rowIndex; i--) {
660 		if (BRow* row = fRows.RemoveItemAt(i)) {
661 			RemoveRow(row);
662 			delete row;
663 		}
664 	}
665 
666 	// re-index the subsequent rows
667 	_UpdateRowIndices(rowIndex);
668 }
669 
670 
671 void
672 Table::TableRowsChanged(TableModel* model, int32 rowIndex, int32 count)
673 {
674 	int32 endIndex = rowIndex + count;
675 	for (int32 i = rowIndex; i < endIndex; i++) {
676 		if (BRow* row = fRows.ItemAt(i))
677 			UpdateRow(row);
678 	}
679 }
680 
681 
682 void
683 Table::TableModelReset(TableModel* model)
684 {
685 	Clear();
686 	TableRowsAdded(model, 0, model->CountRows());
687 }
688 
689 
690 void
691 Table::ItemInvoked()
692 {
693 	if (fListeners.IsEmpty())
694 		return;
695 
696 	int32 index = _ModelIndexOfRow(CurrentSelection());
697 	if (index < 0)
698 		return;
699 
700 	int32 listenerCount = fListeners.CountItems();
701 	for (int32 i = listenerCount - 1; i >= 0; i--)
702 		fListeners.ItemAt(i)->TableRowInvoked(this, index);
703 }
704 
705 
706 void
707 Table::_UpdateRowIndices(int32 fromIndex)
708 {
709 	for (int32 i = fromIndex; BRow* row = fRows.ItemAt(i); i++) {
710 		for (int32 k = 0;
711 			TableField* field = dynamic_cast<TableField*>(row->GetField(k));
712 			k++) {
713 			field->SetRowIndex(i);
714 		}
715 	}
716 }
717 
718 
719 int32
720 Table::_ModelIndexOfRow(BRow* row)
721 {
722 	if (row == NULL)
723 		return -1;
724 
725 	TableField* field = dynamic_cast<TableField*>(row->GetField(0));
726 	if (field == NULL)
727 		return -1;
728 
729 	return field->RowIndex();
730 }
731