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