xref: /haiku/src/apps/debuganalyzer/gui/table/Table.cpp (revision b57470a2179ca208ea910422db1ab2881575b69d)
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 bool
503 Table::GetToolTipAt(BPoint point, BToolTip** _tip)
504 {
505 	if (fToolTipProvider == NULL)
506 		return AbstractTable::GetToolTipAt(point, _tip);
507 
508 	// get the table row
509 	BRow* row = RowAt(point);
510 	int32 rowIndex = row != NULL ? _ModelIndexOfRow(row) : -1;
511 	if (rowIndex < 0)
512 		return AbstractTable::GetToolTipAt(point, _tip);
513 
514 	// get the table column
515 	BColumn* column = ColumnAt(point);
516 	int32 columnIndex = column != NULL ? column->LogicalFieldNum() : -1;
517 
518 	return fToolTipProvider->GetToolTipForTableCell(rowIndex, columnIndex,
519 		_tip);
520 }
521 
522 
523 void
524 Table::SelectionChanged()
525 {
526 	if (fIgnoreSelectionChange > 0)
527 		return;
528 
529 	fSelectionModel._SelectionChanged();
530 
531 	if (!fListeners.IsEmpty()) {
532 		int32 listenerCount = fListeners.CountItems();
533 		for (int32 i = listenerCount - 1; i >= 0; i--)
534 			fListeners.ItemAt(i)->TableSelectionChanged(this);
535 	}
536 }
537 
538 
539 AbstractTable::AbstractColumn*
540 Table::CreateColumn(TableColumn* column)
541 {
542 	return new Column(fModel, column);
543 }
544 
545 
546 void
547 Table::ColumnMouseDown(AbstractColumn* column, BRow* row, BField* field,
548 	BPoint screenWhere, uint32 buttons)
549 {
550 	if (!fListeners.IsEmpty()) {
551 		// get the table row and column indices
552 		int32 rowIndex = _ModelIndexOfRow(row);
553 		int32 columnIndex = column->LogicalFieldNum();
554 		if (rowIndex < 0 || columnIndex < 0)
555 			return;
556 
557 		// notify listeners
558 		int32 listenerCount = fListeners.CountItems();
559 		for (int32 i = listenerCount - 1; i >= 0; i--) {
560 			fListeners.ItemAt(i)->TableCellMouseDown(this, rowIndex,
561 				columnIndex, screenWhere, buttons);
562 		}
563 	}
564 }
565 
566 
567 void
568 Table::ColumnMouseUp(AbstractColumn* column, BRow* row, BField* field,
569 	BPoint screenWhere, uint32 buttons)
570 {
571 	if (!fListeners.IsEmpty()) {
572 		// get the table row and column indices
573 		int32 rowIndex = _ModelIndexOfRow(row);
574 		int32 columnIndex = column->LogicalFieldNum();
575 		if (rowIndex < 0 || columnIndex < 0)
576 			return;
577 
578 		// notify listeners
579 		int32 listenerCount = fListeners.CountItems();
580 		for (int32 i = listenerCount - 1; i >= 0; i--) {
581 			fListeners.ItemAt(i)->TableCellMouseUp(this, rowIndex, columnIndex,
582 				screenWhere, buttons);
583 		}
584 	}
585 }
586 
587 
588 void
589 Table::TableRowsAdded(TableModel* model, int32 rowIndex, int32 count)
590 {
591 	// create the rows
592 	int32 endRow = rowIndex + count;
593 	int32 columnCount = fModel->CountColumns();
594 
595 	for (int32 i = rowIndex; i < endRow; i++) {
596 		// create row
597 		BRow* row = new(std::nothrow) BRow();
598 		if (row == NULL) {
599 			// TODO: Report error!
600 			return;
601 		}
602 
603 		// add dummy fields to row
604 		for (int32 columnIndex = 0; columnIndex < columnCount; columnIndex++) {
605 			// It would be nice to create only a single field and set it for all
606 			// columns, but the row takes ultimate ownership, so it have to be
607 			// individual objects.
608 			TableField* field = new(std::nothrow) TableField(i);
609 			if (field == NULL) {
610 				// TODO: Report error!
611 				delete row;
612 				return;
613 			}
614 
615 			row->SetField(field, columnIndex);
616 		}
617 
618 		// add row
619 		if (!fRows.AddItem(row, i)) {
620 			// TODO: Report error!
621 			delete row;
622 			return;
623 		}
624 
625 		AddRow(row, i);
626 	}
627 
628 	// re-index the subsequent rows
629 	_UpdateRowIndices(endRow);
630 }
631 
632 
633 void
634 Table::TableRowsRemoved(TableModel* model, int32 rowIndex, int32 count)
635 {
636 	if (rowIndex == 0 && count == fRows.CountItems()) {
637 		fRows.MakeEmpty();
638 		Clear();
639 		return;
640 	}
641 
642 	for (int32 i = rowIndex + count - 1; i >= rowIndex; i--) {
643 		if (BRow* row = fRows.RemoveItemAt(i)) {
644 			RemoveRow(row);
645 			delete row;
646 		}
647 	}
648 
649 	// re-index the subsequent rows
650 	_UpdateRowIndices(rowIndex);
651 }
652 
653 
654 void
655 Table::TableRowsChanged(TableModel* model, int32 rowIndex, int32 count)
656 {
657 	int32 endIndex = rowIndex + count;
658 	for (int32 i = rowIndex; i < endIndex; i++) {
659 		if (BRow* row = fRows.ItemAt(i))
660 			UpdateRow(row);
661 	}
662 }
663 
664 
665 void
666 Table::TableModelReset(TableModel* model)
667 {
668 	Clear();
669 	TableRowsAdded(model, 0, model->CountRows());
670 }
671 
672 
673 void
674 Table::ItemInvoked()
675 {
676 	if (fListeners.IsEmpty())
677 		return;
678 
679 	int32 index = _ModelIndexOfRow(CurrentSelection());
680 	if (index < 0)
681 		return;
682 
683 	int32 listenerCount = fListeners.CountItems();
684 	for (int32 i = listenerCount - 1; i >= 0; i--)
685 		fListeners.ItemAt(i)->TableRowInvoked(this, index);
686 }
687 
688 
689 void
690 Table::_UpdateRowIndices(int32 fromIndex)
691 {
692 	for (int32 i = fromIndex; BRow* row = fRows.ItemAt(i); i++) {
693 		for (int32 k = 0;
694 			TableField* field = dynamic_cast<TableField*>(row->GetField(k));
695 			k++) {
696 			field->SetRowIndex(i);
697 		}
698 	}
699 }
700 
701 
702 int32
703 Table::_ModelIndexOfRow(BRow* row)
704 {
705 	if (row == NULL)
706 		return -1;
707 
708 	TableField* field = dynamic_cast<TableField*>(row->GetField(0));
709 	if (field == NULL)
710 		return -1;
711 
712 	return field->RowIndex();
713 }
714