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