xref: /haiku/src/apps/debuganalyzer/gui/table/Table.cpp (revision 830f67ef991407f287dbc1238aa5f5906d90c991)
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 	// rows are deleted by the BColumnListView destructor automatically
406 }
407 
408 
409 void
410 Table::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 
439 void
440 Table::SetToolTipProvider(TableToolTipProvider* toolTipProvider)
441 {
442 	fToolTipProvider = toolTipProvider;
443 }
444 
445 
446 TableSelectionModel*
447 Table::SelectionModel()
448 {
449 	return &fSelectionModel;
450 }
451 
452 
453 void
454 Table::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 
470 void
471 Table::DeselectRow(int32 rowIndex)
472 {
473 	if (BRow* row = fRows.ItemAt(rowIndex))
474 		Deselect(row);
475 }
476 
477 
478 void
479 Table::DeselectAllRows()
480 {
481 	DeselectAll();
482 }
483 
484 
485 bool
486 Table::AddTableListener(TableListener* listener)
487 {
488 	return fListeners.AddItem(listener);
489 }
490 
491 
492 void
493 Table::RemoveTableListener(TableListener* listener)
494 {
495 	fListeners.RemoveItem(listener);
496 }
497 
498 
499 status_t
500 Table::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 
516 bool
517 Table::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 
537 void
538 Table::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 
553 AbstractTable::AbstractColumn*
554 Table::CreateColumn(TableColumn* column)
555 {
556 	return new Column(fModel, column);
557 }
558 
559 
560 void
561 Table::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 
581 void
582 Table::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 
602 void
603 Table::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 
647 void
648 Table::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 
668 void
669 Table::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 
679 void
680 Table::TableModelReset(TableModel* model)
681 {
682 	Clear();
683 	TableRowsAdded(model, 0, model->CountRows());
684 }
685 
686 
687 void
688 Table::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 
703 void
704 Table::_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 
716 int32
717 Table::_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