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