1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_svtools.hxx" 26 27 #include "svtools/table/tablecontrol.hxx" 28 #include "svtools/table/defaultinputhandler.hxx" 29 #include "svtools/table/tablemodel.hxx" 30 31 #include "tabledatawindow.hxx" 32 #include "tablecontrol_impl.hxx" 33 #include "tablegeometry.hxx" 34 35 /** === begin UNO includes === **/ 36 #include <com/sun/star/accessibility/XAccessible.hpp> 37 #include <com/sun/star/accessibility/AccessibleTableModelChange.hpp> 38 #include <com/sun/star/accessibility/AccessibleEventId.hpp> 39 #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp> 40 /** === end UNO includes === **/ 41 42 #include <comphelper/flagguard.hxx> 43 #include <vcl/scrbar.hxx> 44 #include <vcl/seleng.hxx> 45 #include <rtl/ref.hxx> 46 #include <vcl/image.hxx> 47 #include <tools/diagnose_ex.h> 48 49 #include <functional> 50 #include <numeric> 51 52 #define MIN_COLUMN_WIDTH_PIXEL 4 53 54 //...................................................................................................................... 55 namespace svt { namespace table 56 { 57 //...................................................................................................................... 58 59 /** === begin UNO using === **/ 60 using ::com::sun::star::accessibility::AccessibleTableModelChange; 61 using ::com::sun::star::uno::makeAny; 62 using ::com::sun::star::uno::Any; 63 using ::com::sun::star::accessibility::XAccessible; 64 using ::com::sun::star::uno::Reference; 65 /** === end UNO using === **/ 66 namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId; 67 namespace AccessibleTableModelChangeType = ::com::sun::star::accessibility::AccessibleTableModelChangeType; 68 69 //================================================================================================================== 70 //= SuppressCursor 71 //================================================================================================================== 72 class SuppressCursor 73 { 74 private: 75 ITableControl& m_rTable; 76 77 public: 78 SuppressCursor( ITableControl& _rTable ) 79 :m_rTable( _rTable ) 80 { 81 m_rTable.hideCursor(); 82 } 83 ~SuppressCursor() 84 { 85 m_rTable.showCursor(); 86 } 87 }; 88 89 //==================================================================== 90 //= EmptyTableModel 91 //==================================================================== 92 /** default implementation of an ->ITableModel, used as fallback when no 93 real model is present 94 95 Instances of this class are static in any way, and provide the least 96 necessary default functionality for a table model. 97 */ 98 class EmptyTableModel : public ITableModel 99 { 100 public: 101 EmptyTableModel() 102 { 103 } 104 105 // ITableModel overridables 106 virtual TableSize getColumnCount() const 107 { 108 return 0; 109 } 110 virtual TableSize getRowCount() const 111 { 112 return 0; 113 } 114 virtual bool hasColumnHeaders() const 115 { 116 return false; 117 } 118 virtual bool hasRowHeaders() const 119 { 120 return false; 121 } 122 virtual bool isCellEditable( ColPos col, RowPos row ) const 123 { 124 (void)col; 125 (void)row; 126 return false; 127 } 128 virtual PColumnModel getColumnModel( ColPos column ) 129 { 130 DBG_ERROR( "EmptyTableModel::getColumnModel: invalid call!" ); 131 (void)column; 132 return PColumnModel(); 133 } 134 virtual PTableRenderer getRenderer() const 135 { 136 return PTableRenderer(); 137 } 138 virtual PTableInputHandler getInputHandler() const 139 { 140 return PTableInputHandler(); 141 } 142 virtual TableMetrics getRowHeight() const 143 { 144 return 5 * 100; 145 } 146 virtual void setRowHeight(TableMetrics _nRowHeight) 147 { 148 (void)_nRowHeight; 149 } 150 virtual TableMetrics getColumnHeaderHeight() const 151 { 152 return 0; 153 } 154 virtual TableMetrics getRowHeaderWidth() const 155 { 156 return 0; 157 } 158 virtual ScrollbarVisibility getVerticalScrollbarVisibility() const 159 { 160 return ScrollbarShowNever; 161 } 162 virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const 163 { 164 return ScrollbarShowNever; 165 } 166 virtual void addTableModelListener( const PTableModelListener& i_listener ) 167 { 168 (void)i_listener; 169 } 170 virtual void removeTableModelListener( const PTableModelListener& i_listener ) 171 { 172 (void)i_listener; 173 } 174 virtual ::boost::optional< ::Color > getLineColor() const 175 { 176 return ::boost::optional< ::Color >(); 177 } 178 virtual ::boost::optional< ::Color > getHeaderBackgroundColor() const 179 { 180 return ::boost::optional< ::Color >(); 181 } 182 virtual ::boost::optional< ::Color > getHeaderTextColor() const 183 { 184 return ::boost::optional< ::Color >(); 185 } 186 virtual ::boost::optional< ::Color > getActiveSelectionBackColor() const 187 { 188 return ::boost::optional< ::Color >(); 189 } 190 virtual ::boost::optional< ::Color > getInactiveSelectionBackColor() const 191 { 192 return ::boost::optional< ::Color >(); 193 } 194 virtual ::boost::optional< ::Color > getActiveSelectionTextColor() const 195 { 196 return ::boost::optional< ::Color >(); 197 } 198 virtual ::boost::optional< ::Color > getInactiveSelectionTextColor() const 199 { 200 return ::boost::optional< ::Color >(); 201 } 202 virtual ::boost::optional< ::Color > getTextColor() const 203 { 204 return ::boost::optional< ::Color >(); 205 } 206 virtual ::boost::optional< ::Color > getTextLineColor() const 207 { 208 return ::boost::optional< ::Color >(); 209 } 210 virtual ::boost::optional< ::std::vector< ::Color > > getRowBackgroundColors() const 211 { 212 return ::boost::optional< ::std::vector< ::Color > >(); 213 } 214 virtual ::com::sun::star::style::VerticalAlignment getVerticalAlign() const 215 { 216 return com::sun::star::style::VerticalAlignment(0); 217 } 218 virtual ITableDataSort* getSortAdapter() 219 { 220 return NULL; 221 } 222 virtual void getCellContent( ColPos const i_col, RowPos const i_row, ::com::sun::star::uno::Any& o_cellContent ) 223 { 224 (void)i_row; 225 (void)i_col; 226 o_cellContent.clear(); 227 } 228 virtual void getCellToolTip( ColPos const, RowPos const, ::com::sun::star::uno::Any& ) 229 { 230 } 231 virtual Any getRowHeading( RowPos const i_rowPos ) const 232 { 233 (void)i_rowPos; 234 return Any(); 235 } 236 }; 237 238 239 //==================================================================== 240 //= TableControl_Impl 241 //==================================================================== 242 DBG_NAME( TableControl_Impl ) 243 244 #if DBG_UTIL 245 //==================================================================== 246 //= SuspendInvariants 247 //==================================================================== 248 class SuspendInvariants 249 { 250 private: 251 const TableControl_Impl& m_rTable; 252 sal_Int32 m_nSuspendFlags; 253 254 public: 255 SuspendInvariants( const TableControl_Impl& _rTable, sal_Int32 _nSuspendFlags ) 256 :m_rTable( _rTable ) 257 ,m_nSuspendFlags( _nSuspendFlags ) 258 { 259 //DBG_ASSERT( ( m_rTable.m_nRequiredInvariants & m_nSuspendFlags ) == m_nSuspendFlags, 260 // "SuspendInvariants: cannot suspend what is already suspended!" ); 261 const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants &= ~m_nSuspendFlags; 262 } 263 ~SuspendInvariants() 264 { 265 const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants |= m_nSuspendFlags; 266 } 267 }; 268 #define DBG_SUSPEND_INV( flags ) \ 269 SuspendInvariants aSuspendInv( *this, flags ); 270 #else 271 #define DBG_SUSPEND_INV( flags ) 272 #endif 273 274 #if DBG_UTIL 275 //==================================================================== 276 const char* TableControl_Impl_checkInvariants( const void* _pInstance ) 277 { 278 return static_cast< const TableControl_Impl* >( _pInstance )->impl_checkInvariants(); 279 } 280 281 namespace 282 { 283 template< typename SCALAR_TYPE > 284 bool lcl_checkLimitsExclusive( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax ) 285 { 286 return ( _nValue > _nMin ) && ( _nValue < _nMax ); 287 } 288 289 template< typename SCALAR_TYPE > 290 bool lcl_checkLimitsExclusive_OrDefault_OrFallback( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax, 291 PTableModel _pModel, SCALAR_TYPE _nDefaultOrFallback ) 292 { 293 if ( !_pModel ) 294 return _nValue == _nDefaultOrFallback; 295 if ( _nMax <= _nMin ) 296 return _nDefaultOrFallback == _nValue; 297 return lcl_checkLimitsExclusive( _nValue, _nMin, _nMax ); 298 } 299 } 300 301 //------------------------------------------------------------------------------------------------------------------ 302 const sal_Char* TableControl_Impl::impl_checkInvariants() const 303 { 304 if ( !m_pModel ) 305 return "no model, not even an EmptyTableModel"; 306 307 if ( !m_pDataWindow ) 308 return "invalid data window!"; 309 310 if ( m_pModel->getColumnCount() != m_nColumnCount ) 311 return "column counts are inconsistent!"; 312 313 if ( m_pModel->getRowCount() != m_nRowCount ) 314 return "row counts are inconsistent!"; 315 316 if ( ( m_nCurColumn != COL_INVALID ) && !m_aColumnWidths.empty() && ( m_nCurColumn < 0 ) || ( m_nCurColumn >= (ColPos)m_aColumnWidths.size() ) ) 317 return "current column is invalid!"; 318 319 if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nTopRow, (RowPos)-1, m_nRowCount, getModel(), (RowPos)0 ) ) 320 return "invalid top row value!"; 321 322 if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurRow, (RowPos)-1, m_nRowCount, getModel(), ROW_INVALID ) ) 323 return "invalid current row value!"; 324 325 if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nLeftColumn, (ColPos)-1, m_nColumnCount, getModel(), (ColPos)0 ) ) 326 return "invalid current column value!"; 327 328 if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurColumn, (ColPos)-1, m_nColumnCount, getModel(), COL_INVALID ) ) 329 return "invalid current column value!"; 330 331 if ( m_pInputHandler != m_pModel->getInputHandler() ) 332 return "input handler is not the model-provided one!"; 333 334 // m_aSelectedRows should have reasonable content 335 { 336 if ( m_aSelectedRows.size() > size_t( m_pModel->getRowCount() ) ) 337 return "there are more rows selected than actually exist"; 338 for ( ::std::vector< RowPos >::const_iterator selRow = m_aSelectedRows.begin(); 339 selRow != m_aSelectedRows.end(); 340 ++selRow 341 ) 342 { 343 if ( ( *selRow < 0 ) || ( *selRow >= m_pModel->getRowCount() ) ) 344 return "a non-existent row is selected"; 345 } 346 } 347 348 // m_nColHeaderHeightPixel consistent with the model's value? 349 { 350 TableMetrics nHeaderHeight = m_pModel->hasColumnHeaders() ? m_pModel->getColumnHeaderHeight() : 0; 351 nHeaderHeight = m_rAntiImpl.LogicToPixel( Size( 0, nHeaderHeight ), MAP_APPFONT ).Height(); 352 if ( nHeaderHeight != m_nColHeaderHeightPixel ) 353 return "column header heights are inconsistent!"; 354 } 355 356 bool isDummyModel = dynamic_cast< const EmptyTableModel* >( m_pModel.get() ) != NULL; 357 if ( !isDummyModel ) 358 { 359 TableMetrics nRowHeight = m_pModel->getRowHeight(); 360 nRowHeight = m_rAntiImpl.LogicToPixel( Size( 0, nRowHeight ), MAP_APPFONT).Height(); 361 if ( nRowHeight != m_nRowHeightPixel ) 362 return "row heights are inconsistent!"; 363 } 364 365 // m_nRowHeaderWidthPixel consistent with the model's value? 366 { 367 TableMetrics nHeaderWidth = m_pModel->hasRowHeaders() ? m_pModel->getRowHeaderWidth() : 0; 368 nHeaderWidth = m_rAntiImpl.LogicToPixel( Size( nHeaderWidth, 0 ), MAP_APPFONT ).Width(); 369 if ( nHeaderWidth != m_nRowHeaderWidthPixel ) 370 return "row header widths are inconsistent!"; 371 } 372 373 // m_aColumnWidths consistency 374 if ( size_t( m_nColumnCount ) != m_aColumnWidths.size() ) 375 return "wrong number of cached column widths"; 376 377 for ( ColumnPositions::const_iterator col = m_aColumnWidths.begin(); 378 col != m_aColumnWidths.end(); 379 ) 380 { 381 if ( col->getEnd() < col->getStart() ) 382 return "column widths: 'end' is expected to not be smaller than start"; 383 384 ColumnPositions::const_iterator nextCol = col + 1; 385 if ( nextCol != m_aColumnWidths.end() ) 386 if ( col->getEnd() != nextCol->getStart() ) 387 return "column widths: one column's end should be the next column's start"; 388 col = nextCol; 389 } 390 391 if ( m_nLeftColumn < m_nColumnCount ) 392 if ( m_aColumnWidths[ m_nLeftColumn ].getStart() != m_nRowHeaderWidthPixel ) 393 return "the left-most column should start immediately after the row header"; 394 395 if ( m_nCursorHidden < 0 ) 396 return "invalid hidden count for the cursor!"; 397 398 if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pVScroll ) 399 { 400 DBG_SUSPEND_INV( INV_SCROLL_POSITION ); 401 // prevent infinite recursion 402 403 if ( m_nLeftColumn < 0 ) 404 return "invalid left-most column index"; 405 if ( m_pVScroll->GetThumbPos() != m_nTopRow ) 406 return "vertical scroll bar |position| is incorrect!"; 407 if ( m_pVScroll->GetRange().Max() != m_nRowCount ) 408 return "vertical scroll bar |range| is incorrect!"; 409 if ( m_pVScroll->GetVisibleSize() != impl_getVisibleRows( false ) ) 410 return "vertical scroll bar |visible size| is incorrect!"; 411 } 412 413 if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pHScroll ) 414 { 415 DBG_SUSPEND_INV( INV_SCROLL_POSITION ); 416 // prevent infinite recursion 417 418 if ( m_pHScroll->GetThumbPos() != m_nLeftColumn ) 419 return "horizontal scroll bar |position| is incorrect!"; 420 if ( m_pHScroll->GetRange().Max() != m_nColumnCount ) 421 return "horizontal scroll bar |range| is incorrect!"; 422 if ( m_pHScroll->GetVisibleSize() != impl_getVisibleColumns( false ) ) 423 return "horizontal scroll bar |visible size| is incorrect!"; 424 } 425 426 return NULL; 427 } 428 #endif 429 430 #define DBG_CHECK_ME() \ 431 DBG_CHKTHIS( TableControl_Impl, TableControl_Impl_checkInvariants ) 432 433 //------------------------------------------------------------------------------------------------------------------ 434 TableControl_Impl::TableControl_Impl( TableControl& _rAntiImpl ) 435 :m_rAntiImpl ( _rAntiImpl ) 436 ,m_pModel ( new EmptyTableModel ) 437 ,m_pInputHandler ( ) 438 ,m_nRowHeightPixel ( 15 ) 439 ,m_nColHeaderHeightPixel( 0 ) 440 ,m_nRowHeaderWidthPixel ( 0 ) 441 ,m_nColumnCount ( 0 ) 442 ,m_nRowCount ( 0 ) 443 ,m_bColumnsFit ( true ) 444 ,m_nCurColumn ( COL_INVALID ) 445 ,m_nCurRow ( ROW_INVALID ) 446 ,m_nLeftColumn ( 0 ) 447 ,m_nTopRow ( 0 ) 448 ,m_nCursorHidden ( 1 ) 449 ,m_pDataWindow ( new TableDataWindow( *this ) ) 450 ,m_pVScroll ( NULL ) 451 ,m_pHScroll ( NULL ) 452 ,m_pScrollCorner ( NULL ) 453 ,m_pSelEngine ( ) 454 ,m_aSelectedRows ( ) 455 ,m_pTableFunctionSet ( new TableFunctionSet( this ) ) 456 ,m_nAnchor ( -1 ) 457 ,m_bUpdatingColWidths ( false ) 458 ,m_pAccessibleTable ( NULL ) 459 #if DBG_UTIL 460 ,m_nRequiredInvariants ( INV_SCROLL_POSITION ) 461 #endif 462 { 463 DBG_CTOR( TableControl_Impl, TableControl_Impl_checkInvariants ); 464 m_pSelEngine = new SelectionEngine( m_pDataWindow.get(), m_pTableFunctionSet ); 465 m_pSelEngine->SetSelectionMode(SINGLE_SELECTION); 466 m_pDataWindow->SetPosPixel( Point( 0, 0 ) ); 467 m_pDataWindow->Show(); 468 } 469 470 //------------------------------------------------------------------------------------------------------------------ 471 TableControl_Impl::~TableControl_Impl() 472 { 473 DBG_DTOR( TableControl_Impl, TableControl_Impl_checkInvariants ); 474 475 DELETEZ( m_pVScroll ); 476 DELETEZ( m_pHScroll ); 477 DELETEZ( m_pScrollCorner ); 478 DELETEZ( m_pTableFunctionSet ); 479 DELETEZ( m_pSelEngine ); 480 } 481 482 //------------------------------------------------------------------------------------------------------------------ 483 void TableControl_Impl::setModel( PTableModel _pModel ) 484 { 485 DBG_CHECK_ME(); 486 487 SuppressCursor aHideCursor( *this ); 488 489 if ( !!m_pModel ) 490 m_pModel->removeTableModelListener( shared_from_this() ); 491 492 m_pModel = _pModel; 493 if ( !m_pModel) 494 m_pModel.reset( new EmptyTableModel ); 495 496 m_pModel->addTableModelListener( shared_from_this() ); 497 498 m_nCurRow = ROW_INVALID; 499 m_nCurColumn = COL_INVALID; 500 501 // recalc some model-dependent cached info 502 impl_ni_updateCachedModelValues(); 503 impl_ni_relayout(); 504 505 // completely invalidate 506 m_rAntiImpl.Invalidate(); 507 508 // reset cursor to (0,0) 509 if ( m_nRowCount ) m_nCurRow = 0; 510 if ( m_nColumnCount ) m_nCurColumn = 0; 511 } 512 513 //------------------------------------------------------------------------------------------------------------------ 514 namespace 515 { 516 bool lcl_adjustSelectedRows( ::std::vector< RowPos >& io_selectionIndexes, RowPos const i_firstAffectedRowIndex, TableSize const i_offset ) 517 { 518 bool didChanges = false; 519 for ( ::std::vector< RowPos >::iterator selPos = io_selectionIndexes.begin(); 520 selPos != io_selectionIndexes.end(); 521 ++selPos 522 ) 523 { 524 if ( *selPos < i_firstAffectedRowIndex ) 525 continue; 526 *selPos += i_offset; 527 didChanges = true; 528 } 529 return didChanges; 530 } 531 } 532 533 //------------------------------------------------------------------------------------------------------------------ 534 void TableControl_Impl::rowsInserted( RowPos i_first, RowPos i_last ) 535 { 536 DBG_CHECK_ME(); 537 OSL_PRECOND( i_last >= i_first, "TableControl_Impl::rowsInserted: invalid row indexes!" ); 538 539 TableSize const insertedRows = i_last - i_first + 1; 540 541 // adjust selection, if necessary 542 bool const selectionChanged = lcl_adjustSelectedRows( m_aSelectedRows, i_first, insertedRows ); 543 544 // adjust our cached row count 545 m_nRowCount = m_pModel->getRowCount(); 546 547 // if the rows have been inserted before the current row, adjust this 548 if ( i_first <= m_nCurRow ) 549 goTo( m_nCurColumn, m_nCurRow + insertedRows ); 550 551 // relayout, since the scrollbar need might have changed 552 impl_ni_relayout(); 553 554 // notify A1YY events 555 if ( impl_isAccessibleAlive() ) 556 { 557 impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED, 558 makeAny( AccessibleTableModelChange( AccessibleTableModelChangeType::INSERT, i_first, i_last, 0, m_pModel->getColumnCount() ) ), 559 Any() 560 ); 561 } 562 563 // schedule repaint 564 invalidateRowRange( i_first, ROW_INVALID ); 565 566 // call selection handlers, if necessary 567 if ( selectionChanged ) 568 m_rAntiImpl.Select(); 569 } 570 571 //------------------------------------------------------------------------------------------------------------------ 572 void TableControl_Impl::rowsRemoved( RowPos i_first, RowPos i_last ) 573 { 574 sal_Int32 firstRemovedRow = i_first; 575 sal_Int32 lastRemovedRow = i_last; 576 577 // adjust selection, if necessary 578 bool selectionChanged = false; 579 if ( i_first == -1 ) 580 { 581 selectionChanged = markAllRowsAsDeselected(); 582 583 firstRemovedRow = 0; 584 lastRemovedRow = m_nRowCount - 1; 585 } 586 else 587 { 588 ENSURE_OR_RETURN_VOID( i_last >= i_first, "TableControl_Impl::rowsRemoved: illegal indexes!" ); 589 590 for ( sal_Int32 row = i_first; row <= i_last; ++row ) 591 { 592 if ( markRowAsDeselected( row ) ) 593 selectionChanged = true; 594 } 595 596 if ( lcl_adjustSelectedRows( m_aSelectedRows, i_last + 1, i_first - i_last - 1 ) ) 597 selectionChanged = true; 598 } 599 600 // adjust cached row count 601 m_nRowCount = m_pModel->getRowCount(); 602 603 // adjust the current row, if it is larger than the row count now 604 if ( m_nCurRow >= m_nRowCount ) 605 { 606 if ( m_nRowCount > 0 ) 607 goTo( m_nCurColumn, m_nRowCount - 1 ); 608 else 609 { 610 m_nCurRow = ROW_INVALID; 611 m_nTopRow = 0; 612 } 613 } 614 else if ( m_nRowCount == 0 ) 615 { 616 m_nTopRow = 0; 617 } 618 619 620 // relayout, since the scrollbar need might have changed 621 impl_ni_relayout(); 622 623 // notify A11Y events 624 if ( impl_isAccessibleAlive() ) 625 { 626 commitTableEvent( 627 AccessibleEventId::TABLE_MODEL_CHANGED, 628 makeAny( AccessibleTableModelChange( 629 AccessibleTableModelChangeType::DELETE, 630 firstRemovedRow, 631 lastRemovedRow, 632 0, 633 m_pModel->getColumnCount() 634 ) ), 635 Any() 636 ); 637 } 638 639 // schedule a repaint 640 invalidateRowRange( firstRemovedRow, ROW_INVALID ); 641 642 // call selection handlers, if necessary 643 if ( selectionChanged ) 644 m_rAntiImpl.Select(); 645 } 646 647 //------------------------------------------------------------------------------------------------------------------ 648 void TableControl_Impl::columnInserted( ColPos const i_colIndex ) 649 { 650 m_nColumnCount = m_pModel->getColumnCount(); 651 impl_ni_relayout(); 652 653 m_rAntiImpl.Invalidate(); 654 655 OSL_UNUSED( i_colIndex ); 656 } 657 658 //------------------------------------------------------------------------------------------------------------------ 659 void TableControl_Impl::columnRemoved( ColPos const i_colIndex ) 660 { 661 m_nColumnCount = m_pModel->getColumnCount(); 662 663 // adjust the current column, if it is larger than the column count now 664 if ( m_nCurColumn >= m_nColumnCount ) 665 { 666 if ( m_nColumnCount > 0 ) 667 goTo( m_nCurColumn - 1, m_nCurRow ); 668 else 669 m_nCurColumn = COL_INVALID; 670 } 671 672 impl_ni_relayout(); 673 674 m_rAntiImpl.Invalidate(); 675 676 OSL_UNUSED( i_colIndex ); 677 } 678 679 //------------------------------------------------------------------------------------------------------------------ 680 void TableControl_Impl::allColumnsRemoved() 681 { 682 m_nColumnCount = m_pModel->getColumnCount(); 683 impl_ni_relayout(); 684 685 m_rAntiImpl.Invalidate(); 686 } 687 688 //------------------------------------------------------------------------------------------------------------------ 689 void TableControl_Impl::cellsUpdated( ColPos const i_firstCol, ColPos i_lastCol, RowPos const i_firstRow, RowPos const i_lastRow ) 690 { 691 invalidateRowRange( i_firstRow, i_lastRow ); 692 693 OSL_UNUSED( i_firstCol ); 694 OSL_UNUSED( i_lastCol ); 695 } 696 697 //------------------------------------------------------------------------------------------------------------------ 698 void TableControl_Impl::tableMetricsChanged() 699 { 700 impl_ni_updateCachedTableMetrics(); 701 impl_ni_relayout(); 702 m_rAntiImpl.Invalidate(); 703 } 704 705 //------------------------------------------------------------------------------------------------------------------ 706 void TableControl_Impl::impl_invalidateColumn( ColPos const i_column ) 707 { 708 DBG_CHECK_ME(); 709 710 Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() ); 711 712 const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column ); 713 if ( aColumn.isValid() ) 714 m_rAntiImpl.Invalidate( aColumn.getRect() ); 715 } 716 717 //------------------------------------------------------------------------------------------------------------------ 718 void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ) 719 { 720 ColumnAttributeGroup nGroup( i_attributeGroup ); 721 if ( nGroup & COL_ATTRS_APPEARANCE ) 722 { 723 impl_invalidateColumn( i_column ); 724 nGroup &= ~COL_ATTRS_APPEARANCE; 725 } 726 727 if ( nGroup & COL_ATTRS_WIDTH ) 728 { 729 if ( !m_bUpdatingColWidths ) 730 { 731 impl_ni_relayout( i_column ); 732 invalidate( TableAreaAll ); 733 } 734 735 nGroup &= ~COL_ATTRS_WIDTH; 736 } 737 738 OSL_ENSURE( ( nGroup == COL_ATTRS_NONE ) || ( i_attributeGroup == COL_ATTRS_ALL ), 739 "TableControl_Impl::columnChanged: don't know how to handle this change!" ); 740 } 741 742 //------------------------------------------------------------------------------------------------------------------ 743 Rectangle TableControl_Impl::impl_getAllVisibleCellsArea() const 744 { 745 DBG_CHECK_ME(); 746 747 Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) ); 748 749 // determine the right-most border of the last column which is 750 // at least partially visible 751 aArea.Right() = m_nRowHeaderWidthPixel; 752 if ( !m_aColumnWidths.empty() ) 753 { 754 // the number of pixels which are scrolled out of the left hand 755 // side of the window 756 const long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd(); 757 758 ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin(); 759 do 760 { 761 aArea.Right() = loop->getEnd() - nScrolledOutLeft + m_nRowHeaderWidthPixel; 762 ++loop; 763 } 764 while ( ( loop != m_aColumnWidths.rend() ) 765 && ( loop->getEnd() - nScrolledOutLeft >= aArea.Right() ) 766 ); 767 } 768 // so far, aArea.Right() denotes the first pixel *after* the cell area 769 --aArea.Right(); 770 771 // determine the last row which is at least partially visible 772 aArea.Bottom() = 773 m_nColHeaderHeightPixel 774 + impl_getVisibleRows( true ) * m_nRowHeightPixel 775 - 1; 776 777 return aArea; 778 } 779 780 //------------------------------------------------------------------------------------------------------------------ 781 Rectangle TableControl_Impl::impl_getAllVisibleDataCellArea() const 782 { 783 DBG_CHECK_ME(); 784 785 Rectangle aArea( impl_getAllVisibleCellsArea() ); 786 aArea.Left() = m_nRowHeaderWidthPixel; 787 aArea.Top() = m_nColHeaderHeightPixel; 788 return aArea; 789 } 790 791 //------------------------------------------------------------------------------------------------------------------ 792 void TableControl_Impl::impl_ni_updateCachedTableMetrics() 793 { 794 m_nRowHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getRowHeight() ), MAP_APPFONT ).Height(); 795 796 m_nColHeaderHeightPixel = 0; 797 if ( m_pModel->hasColumnHeaders() ) 798 m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getColumnHeaderHeight() ), MAP_APPFONT ).Height(); 799 800 m_nRowHeaderWidthPixel = 0; 801 if ( m_pModel->hasRowHeaders() ) 802 m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel( Size( m_pModel->getRowHeaderWidth(), 0 ), MAP_APPFONT).Width(); 803 } 804 805 //------------------------------------------------------------------------------------------------------------------ 806 void TableControl_Impl::impl_ni_updateCachedModelValues() 807 { 808 m_pInputHandler = m_pModel->getInputHandler(); 809 if ( !m_pInputHandler ) 810 m_pInputHandler.reset( new DefaultInputHandler ); 811 812 m_nColumnCount = m_pModel->getColumnCount(); 813 if ( m_nLeftColumn >= m_nColumnCount ) 814 m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0; 815 816 m_nRowCount = m_pModel->getRowCount(); 817 if ( m_nTopRow >= m_nRowCount ) 818 m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0; 819 820 impl_ni_updateCachedTableMetrics(); 821 } 822 823 //------------------------------------------------------------------------------------------------------------------ 824 namespace 825 { 826 //.............................................................................................................. 827 /// determines whether a scrollbar is needed for the given values 828 bool lcl_determineScrollbarNeed( long const i_position, ScrollbarVisibility const i_visibility, 829 long const i_availableSpace, long const i_neededSpace ) 830 { 831 if ( i_visibility == ScrollbarShowNever ) 832 return false; 833 if ( i_visibility == ScrollbarShowAlways ) 834 return true; 835 if ( i_position > 0 ) 836 return true; 837 if ( i_availableSpace >= i_neededSpace ) 838 return false; 839 return true; 840 } 841 842 //.............................................................................................................. 843 void lcl_setButtonRepeat( Window& _rWindow, sal_uLong _nDelay ) 844 { 845 AllSettings aSettings = _rWindow.GetSettings(); 846 MouseSettings aMouseSettings = aSettings.GetMouseSettings(); 847 848 aMouseSettings.SetButtonRepeat( _nDelay ); 849 aSettings.SetMouseSettings( aMouseSettings ); 850 851 _rWindow.SetSettings( aSettings, sal_True ); 852 } 853 854 //.............................................................................................................. 855 bool lcl_updateScrollbar( Window& _rParent, ScrollBar*& _rpBar, 856 bool const i_needBar, long _nVisibleUnits, 857 long _nPosition, long _nLineSize, long _nRange, 858 bool _bHorizontal, const Link& _rScrollHandler ) 859 { 860 // do we currently have the scrollbar? 861 bool bHaveBar = _rpBar != NULL; 862 863 // do we need to correct the scrollbar visibility? 864 if ( bHaveBar && !i_needBar ) 865 { 866 if ( _rpBar->IsTracking() ) 867 _rpBar->EndTracking(); 868 DELETEZ( _rpBar ); 869 } 870 else if ( !bHaveBar && i_needBar ) 871 { 872 _rpBar = new ScrollBar( 873 &_rParent, 874 WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL ) 875 ); 876 _rpBar->SetScrollHdl( _rScrollHandler ); 877 // get some speed into the scrolling .... 878 lcl_setButtonRepeat( *_rpBar, 0 ); 879 } 880 881 if ( _rpBar ) 882 { 883 _rpBar->SetRange( Range( 0, _nRange ) ); 884 _rpBar->SetVisibleSize( _nVisibleUnits ); 885 _rpBar->SetPageSize( _nVisibleUnits ); 886 _rpBar->SetLineSize( _nLineSize ); 887 _rpBar->SetThumbPos( _nPosition ); 888 _rpBar->Show(); 889 } 890 891 return ( bHaveBar != i_needBar ); 892 } 893 894 //.............................................................................................................. 895 /** returns the number of rows fitting into the given range, 896 for the given row height. Partially fitting rows are counted, too, if the 897 respective parameter says so. 898 */ 899 TableSize lcl_getRowsFittingInto( long _nOverallHeight, long _nRowHeightPixel, bool _bAcceptPartialRow = false ) 900 { 901 return _bAcceptPartialRow 902 ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel 903 : _nOverallHeight / _nRowHeightPixel; 904 } 905 906 //.............................................................................................................. 907 /** returns the number of columns fitting into the given area, 908 with the first visible column as given. Partially fitting columns are counted, too, 909 if the respective parameter says so. 910 */ 911 TableSize lcl_getColumnsVisibleWithin( const Rectangle& _rArea, ColPos _nFirstVisibleColumn, 912 const TableControl_Impl& _rControl, bool _bAcceptPartialRow ) 913 { 914 TableSize visibleColumns = 0; 915 TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn ); 916 while ( aColumn.isValid() ) 917 { 918 if ( !_bAcceptPartialRow ) 919 if ( aColumn.getRect().Right() > _rArea.Right() ) 920 // this column is only partially visible, and this is not allowed 921 break; 922 923 aColumn.moveRight(); 924 ++visibleColumns; 925 } 926 return visibleColumns; 927 } 928 929 } 930 931 //------------------------------------------------------------------------------------------------------------------ 932 long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding, 933 bool const i_assumeVerticalScrollbar, ::std::vector< long >& o_newColWidthsPixel ) const 934 { 935 // the available horizontal space 936 long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width(); 937 ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel ); 938 if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) ) 939 { 940 gridWidthPixel -= m_nRowHeaderWidthPixel; 941 } 942 943 if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) ) 944 { 945 long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); 946 gridWidthPixel -= nScrollbarMetrics; 947 } 948 949 // no need to do anything without columns 950 TableSize const colCount = m_pModel->getColumnCount(); 951 if ( colCount == 0 ) 952 return gridWidthPixel; 953 954 // collect some meta data for our columns: 955 // - their current (pixel) metrics 956 long accumulatedCurrentWidth = 0; 957 ::std::vector< long > currentColWidths; 958 currentColWidths.reserve( colCount ); 959 typedef ::std::vector< ::std::pair< long, long > > ColumnLimits; 960 ColumnLimits effectiveColumnLimits; 961 effectiveColumnLimits.reserve( colCount ); 962 long accumulatedMinWidth = 0; 963 long accumulatedMaxWidth = 0; 964 // - their relative flexibility 965 ::std::vector< ::sal_Int32 > columnFlexibilities; 966 columnFlexibilities.reserve( colCount ); 967 long flexibilityDenominator = 0; 968 size_t flexibleColumnCount = 0; 969 for ( ColPos col = 0; col < colCount; ++col ) 970 { 971 PColumnModel const pColumn = m_pModel->getColumnModel( col ); 972 ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); 973 974 // current width 975 long const currentWidth = appFontWidthToPixel( pColumn->getWidth() ); 976 currentColWidths.push_back( currentWidth ); 977 978 // accumulated width 979 accumulatedCurrentWidth += currentWidth; 980 981 // flexibility 982 ::sal_Int32 flexibility = pColumn->getFlexibility(); 983 OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." ); 984 if ( ( flexibility < 0 ) // normalization 985 || ( !pColumn->isResizable() ) // column not resizeable => no auto-resize 986 || ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respec this 987 ) 988 flexibility = 0; 989 990 // min/max width 991 long effectiveMin = currentWidth, effectiveMax = currentWidth; 992 // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then 993 if ( flexibility > 0 ) 994 { 995 long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() ); 996 if ( minWidth > 0 ) 997 effectiveMin = minWidth; 998 else 999 effectiveMin = MIN_COLUMN_WIDTH_PIXEL; 1000 1001 long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() ); 1002 OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" ); 1003 if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) ) 1004 effectiveMax = maxWidth; 1005 else 1006 effectiveMax = gridWidthPixel; // TODO: any better guess here? 1007 1008 if ( effectiveMin == effectiveMax ) 1009 // if the min and the max are identical, this implies no flexibility at all 1010 flexibility = 0; 1011 } 1012 1013 columnFlexibilities.push_back( flexibility ); 1014 flexibilityDenominator += flexibility; 1015 if ( flexibility > 0 ) 1016 ++flexibleColumnCount; 1017 1018 effectiveColumnLimits.push_back( ::std::pair< long, long >( effectiveMin, effectiveMax ) ); 1019 accumulatedMinWidth += effectiveMin; 1020 accumulatedMaxWidth += effectiveMax; 1021 } 1022 1023 o_newColWidthsPixel = currentColWidths; 1024 if ( flexibilityDenominator == 0 ) 1025 { 1026 // no column is flexible => don't adjust anything 1027 } 1028 else if ( gridWidthPixel > accumulatedCurrentWidth ) 1029 { // we have space to give away ... 1030 long distributePixel = gridWidthPixel - accumulatedCurrentWidth; 1031 if ( gridWidthPixel > accumulatedMaxWidth ) 1032 { 1033 // ... but the column's maximal widths are still less than we have 1034 // => set them all to max 1035 for ( size_t i = 0; i < size_t( colCount ); ++i ) 1036 { 1037 o_newColWidthsPixel[i] = effectiveColumnLimits[i].second; 1038 } 1039 } 1040 else 1041 { 1042 bool startOver = false; 1043 do 1044 { 1045 startOver = false; 1046 // distribute the remaining space amongst all columns with a positive flexibility 1047 for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i ) 1048 { 1049 long const columnFlexibility = columnFlexibilities[i]; 1050 if ( columnFlexibility == 0 ) 1051 continue; 1052 1053 long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator; 1054 1055 if ( newColWidth > effectiveColumnLimits[i].second ) 1056 { // that was too much, we hit the col's maximum 1057 // set the new width to exactly this maximum 1058 newColWidth = effectiveColumnLimits[i].second; 1059 // adjust the flexibility denominator ... 1060 flexibilityDenominator -= columnFlexibility; 1061 columnFlexibilities[i] = 0; 1062 --flexibleColumnCount; 1063 // ... and the remaining width ... 1064 long const difference = newColWidth - currentColWidths[i]; 1065 distributePixel -= difference; 1066 // ... this way, we ensure that the width not taken up by this column is consumed by the other 1067 // flexible ones (if there are some) 1068 1069 // and start over with the first column, since there might be earlier columns which need 1070 // to be recalculated now 1071 startOver = true; 1072 } 1073 1074 o_newColWidthsPixel[i] = newColWidth; 1075 } 1076 } 1077 while ( startOver ); 1078 1079 // are there pixels left (might be caused by rounding errors)? 1080 distributePixel = gridWidthPixel - ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ); 1081 while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) ) 1082 { 1083 // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible 1084 // columns which did not yet reach their maximum. 1085 for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i ) 1086 { 1087 if ( columnFlexibilities[i] == 0 ) 1088 continue; 1089 1090 OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second, 1091 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" ); 1092 if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first ) 1093 { 1094 columnFlexibilities[i] = 0; 1095 --flexibleColumnCount; 1096 continue; 1097 } 1098 1099 ++o_newColWidthsPixel[i]; 1100 --distributePixel; 1101 } 1102 } 1103 } 1104 } 1105 else if ( gridWidthPixel < accumulatedCurrentWidth ) 1106 { // we need to take away some space from the columns which allow it ... 1107 long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel; 1108 if ( gridWidthPixel < accumulatedMinWidth ) 1109 { 1110 // ... but the column's minimal widths are still more than we have 1111 // => set them all to min 1112 for ( size_t i = 0; i < size_t( colCount ); ++i ) 1113 { 1114 o_newColWidthsPixel[i] = effectiveColumnLimits[i].first; 1115 } 1116 } 1117 else 1118 { 1119 bool startOver = false; 1120 do 1121 { 1122 startOver = false; 1123 // take away the space we need from the columns with a positive flexibility 1124 for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i ) 1125 { 1126 long const columnFlexibility = columnFlexibilities[i]; 1127 if ( columnFlexibility == 0 ) 1128 continue; 1129 1130 long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator; 1131 1132 if ( newColWidth < effectiveColumnLimits[i].first ) 1133 { // that was too much, we hit the col's minimum 1134 // set the new width to exactly this minimum 1135 newColWidth = effectiveColumnLimits[i].first; 1136 // adjust the flexibility denominator ... 1137 flexibilityDenominator -= columnFlexibility; 1138 columnFlexibilities[i] = 0; 1139 --flexibleColumnCount; 1140 // ... and the remaining width ... 1141 long const difference = currentColWidths[i] - newColWidth; 1142 takeAwayPixel -= difference; 1143 1144 // and start over with the first column, since there might be earlier columns which need 1145 // to be recalculated now 1146 startOver = true; 1147 } 1148 1149 o_newColWidthsPixel[i] = newColWidth; 1150 } 1151 } 1152 while ( startOver ); 1153 1154 // are there pixels left (might be caused by rounding errors)? 1155 takeAwayPixel = ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ) - gridWidthPixel; 1156 while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) ) 1157 { 1158 // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible 1159 // columns which did not yet reach their minimum. 1160 for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i ) 1161 { 1162 if ( columnFlexibilities[i] == 0 ) 1163 continue; 1164 1165 OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first, 1166 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" ); 1167 if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first ) 1168 { 1169 columnFlexibilities[i] = 0; 1170 --flexibleColumnCount; 1171 continue; 1172 } 1173 1174 --o_newColWidthsPixel[i]; 1175 --takeAwayPixel; 1176 } 1177 } 1178 } 1179 } 1180 1181 return gridWidthPixel; 1182 } 1183 1184 //------------------------------------------------------------------------------------------------------------------ 1185 void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding ) 1186 { 1187 ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" ); 1188 1189 m_aColumnWidths.resize( 0 ); 1190 if ( !m_pModel ) 1191 return; 1192 1193 ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true ); 1194 SuppressCursor aHideCursor( *this ); 1195 1196 // layouting steps: 1197 // 1198 // 1. adjust column widths, leaving space for a vertical scrollbar 1199 // 2. determine need for a vertical scrollbar 1200 // - V-YES: all fine, result from 1. is still valid 1201 // - V-NO: result from 1. is still under consideration 1202 // 1203 // 3. determine need for a horizontal scrollbar 1204 // - H-NO: all fine, result from 2. is still valid 1205 // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO 1206 // - V-YES: all fine, result from 1. is still valid 1207 // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it 1208 1209 ::std::vector< long > newWidthsPixel; 1210 long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel ); 1211 1212 // the width/height of a scrollbar, needed several times below 1213 long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); 1214 1215 // determine the playground for the data cells (excluding headers) 1216 // TODO: what if the control is smaller than needed for the headers/scrollbars? 1217 Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() ); 1218 aDataCellPlayground.Left() = m_nRowHeaderWidthPixel; 1219 aDataCellPlayground.Top() = m_nColHeaderHeightPixel; 1220 1221 OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ), 1222 "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" ); 1223 long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 ); 1224 1225 ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility(); 1226 ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility(); 1227 1228 // do we need a vertical scrollbar? 1229 bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed( 1230 m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); 1231 bool bFirstRoundVScrollNeed = false; 1232 if ( bNeedVerticalScrollbar ) 1233 { 1234 aDataCellPlayground.Right() -= nScrollbarMetrics; 1235 bFirstRoundVScrollNeed = true; 1236 } 1237 1238 // do we need a horizontal scrollbar? 1239 bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed( 1240 m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth ); 1241 if ( bNeedHorizontalScrollbar ) 1242 { 1243 aDataCellPlayground.Bottom() -= nScrollbarMetrics; 1244 1245 // now that we just found that we need a horizontal scrollbar, 1246 // the need for a vertical one may have changed, since the horizontal 1247 // SB might just occupy enough space so that not all rows do fit 1248 // anymore 1249 if ( !bFirstRoundVScrollNeed ) 1250 { 1251 bNeedVerticalScrollbar = lcl_determineScrollbarNeed( 1252 m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); 1253 if ( bNeedVerticalScrollbar ) 1254 { 1255 aDataCellPlayground.Right() -= nScrollbarMetrics; 1256 } 1257 } 1258 } 1259 1260 // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now, 1261 // we know that this is not the case, re-calculate the column widths. 1262 if ( !bNeedVerticalScrollbar ) 1263 gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel ); 1264 1265 // update the column objects with the new widths we finally calculated 1266 TableSize const colCount = m_pModel->getColumnCount(); 1267 m_aColumnWidths.reserve( colCount ); 1268 long accumulatedWidthPixel = m_nRowHeaderWidthPixel; 1269 bool anyColumnWidthChanged = false; 1270 for ( ColPos col = 0; col < colCount; ++col ) 1271 { 1272 const long columnStart = accumulatedWidthPixel; 1273 const long columnEnd = columnStart + newWidthsPixel[col]; 1274 m_aColumnWidths.push_back( MutableColumnMetrics( columnStart, columnEnd ) ); 1275 accumulatedWidthPixel = columnEnd; 1276 1277 // and don't forget to forward this to the column models 1278 PColumnModel const pColumn = m_pModel->getColumnModel( col ); 1279 ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); 1280 1281 long const oldColumnWidthAppFont = pColumn->getWidth(); 1282 long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] ); 1283 pColumn->setWidth( newColumnWidthAppFont ); 1284 1285 anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont ); 1286 } 1287 1288 // if the column widths changed, ensure everything is repainted 1289 if ( anyColumnWidthChanged ) 1290 invalidate( TableAreaAll ); 1291 1292 // if the column resizing happened to leave some space at the right, but there are columns 1293 // scrolled out to the left, scroll them in 1294 while ( ( m_nLeftColumn > 0 ) 1295 && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel ) 1296 ) 1297 { 1298 --m_nLeftColumn; 1299 } 1300 1301 // now adjust the column metrics, since they currently ignore the horizontal scroll position 1302 if ( m_nLeftColumn > 0 ) 1303 { 1304 const long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart(); 1305 for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin(); 1306 colPos != m_aColumnWidths.end(); 1307 ++colPos 1308 ) 1309 { 1310 colPos->move( offsetPixel ); 1311 } 1312 } 1313 1314 // show or hide the scrollbars as needed, and position the data window 1315 impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar ); 1316 } 1317 1318 //------------------------------------------------------------------------------------------------------------------ 1319 void TableControl_Impl::impl_ni_positionChildWindows( Rectangle const & i_dataCellPlayground, 1320 bool const i_verticalScrollbar, bool const i_horizontalScrollbar ) 1321 { 1322 long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); 1323 1324 // create or destroy the vertical scrollbar, as needed 1325 lcl_updateScrollbar( 1326 m_rAntiImpl, 1327 m_pVScroll, 1328 i_verticalScrollbar, 1329 lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel ), 1330 // visible units 1331 m_nTopRow, // current position 1332 1, // line size 1333 m_nRowCount, // range 1334 false, // vertical 1335 LINK( this, TableControl_Impl, OnScroll ) // scroll handler 1336 ); 1337 1338 // position it 1339 if ( m_pVScroll ) 1340 { 1341 Rectangle aScrollbarArea( 1342 Point( i_dataCellPlayground.Right() + 1, 0 ), 1343 Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 ) 1344 ); 1345 m_pVScroll->SetPosSizePixel( 1346 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); 1347 } 1348 1349 // create or destroy the horizontal scrollbar, as needed 1350 lcl_updateScrollbar( 1351 m_rAntiImpl, 1352 m_pHScroll, 1353 i_horizontalScrollbar, 1354 lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ), 1355 // visible units 1356 m_nLeftColumn, // current position 1357 1, // line size 1358 m_nColumnCount, // range 1359 true, // horizontal 1360 LINK( this, TableControl_Impl, OnScroll ) // scroll handler 1361 ); 1362 1363 // position it 1364 if ( m_pHScroll ) 1365 { 1366 TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ); 1367 TableMetrics const nRange = m_nColumnCount; 1368 if( m_nLeftColumn + nVisibleUnits == nRange - 1 ) 1369 { 1370 if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() ) 1371 { 1372 m_pHScroll->SetVisibleSize( nVisibleUnits -1 ); 1373 m_pHScroll->SetPageSize( nVisibleUnits - 1 ); 1374 } 1375 } 1376 Rectangle aScrollbarArea( 1377 Point( 0, i_dataCellPlayground.Bottom() + 1 ), 1378 Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics ) 1379 ); 1380 m_pHScroll->SetPosSizePixel( 1381 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); 1382 } 1383 1384 // the corner window connecting the two scrollbars in the lower right corner 1385 bool bHaveScrollCorner = NULL != m_pScrollCorner; 1386 bool bNeedScrollCorner = ( NULL != m_pHScroll ) && ( NULL != m_pVScroll ); 1387 if ( bHaveScrollCorner && !bNeedScrollCorner ) 1388 { 1389 DELETEZ( m_pScrollCorner ); 1390 } 1391 else if ( !bHaveScrollCorner && bNeedScrollCorner ) 1392 { 1393 m_pScrollCorner = new ScrollBarBox( &m_rAntiImpl ); 1394 m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) ); 1395 m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) ); 1396 m_pScrollCorner->Show(); 1397 } 1398 else if(bHaveScrollCorner && bNeedScrollCorner) 1399 { 1400 m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) ); 1401 m_pScrollCorner->Show(); 1402 } 1403 1404 // resize the data window 1405 m_pDataWindow->SetSizePixel( Size( 1406 i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel, 1407 i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel 1408 ) ); 1409 } 1410 1411 //------------------------------------------------------------------------------------------------------------------ 1412 void TableControl_Impl::onResize() 1413 { 1414 DBG_CHECK_ME(); 1415 1416 impl_ni_relayout(); 1417 checkCursorPosition(); 1418 } 1419 1420 //------------------------------------------------------------------------------------------------------------------ 1421 void TableControl_Impl::doPaintContent( const Rectangle& _rUpdateRect ) 1422 { 1423 DBG_CHECK_ME(); 1424 1425 if ( !getModel() ) 1426 return; 1427 PTableRenderer pRenderer = getModel()->getRenderer(); 1428 DBG_ASSERT( !!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!" ); 1429 if ( !pRenderer ) 1430 return; 1431 1432 // our current style settings, to be passed to the renderer 1433 const StyleSettings& rStyle = m_rAntiImpl.GetSettings().GetStyleSettings(); 1434 m_nRowCount = m_pModel->getRowCount(); 1435 // the area occupied by all (at least partially) visible cells, including 1436 // headers 1437 Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() ); 1438 1439 // ............................ 1440 // draw the header column area 1441 if ( m_pModel->hasColumnHeaders() ) 1442 { 1443 TableRowGeometry const aHeaderRow( *this, Rectangle( Point( 0, 0 ), 1444 aAllCellsWithHeaders.BottomRight() ), ROW_COL_HEADERS ); 1445 Rectangle const aColRect(aHeaderRow.getRect()); 1446 pRenderer->PaintHeaderArea( 1447 *m_pDataWindow, aColRect, true, false, rStyle 1448 ); 1449 // Note that strictly, aHeaderRow.getRect() also contains the intersection between column 1450 // and row header area. However, below we go to paint this intersection, again, 1451 // so this hopefully doesn't hurt if we already paint it here. 1452 1453 for ( TableCellGeometry aCell( aHeaderRow, m_nLeftColumn ); 1454 aCell.isValid(); 1455 aCell.moveRight() 1456 ) 1457 { 1458 if ( _rUpdateRect.GetIntersection( aCell.getRect() ).IsEmpty() ) 1459 continue; 1460 1461 bool isActiveColumn = ( aCell.getColumn() == getCurrentColumn() ); 1462 bool isSelectedColumn = false; 1463 pRenderer->PaintColumnHeader( aCell.getColumn(), isActiveColumn, isSelectedColumn, 1464 *m_pDataWindow, aCell.getRect(), rStyle ); 1465 } 1466 } 1467 // the area occupied by the row header, if any 1468 Rectangle aRowHeaderArea; 1469 if ( m_pModel->hasRowHeaders() ) 1470 { 1471 aRowHeaderArea = aAllCellsWithHeaders; 1472 aRowHeaderArea.Right() = m_nRowHeaderWidthPixel - 1; 1473 1474 TableSize const nVisibleRows = impl_getVisibleRows( true ); 1475 TableSize nActualRows = nVisibleRows; 1476 if ( m_nTopRow + nActualRows > m_nRowCount ) 1477 nActualRows = m_nRowCount - m_nTopRow; 1478 aRowHeaderArea.Bottom() = m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1; 1479 1480 pRenderer->PaintHeaderArea( *m_pDataWindow, aRowHeaderArea, false, true, rStyle ); 1481 // Note that strictly, aRowHeaderArea also contains the intersection between column 1482 // and row header area. However, below we go to paint this intersection, again, 1483 // so this hopefully doesn't hurt if we already paint it here. 1484 1485 if ( m_pModel->hasColumnHeaders() ) 1486 { 1487 TableCellGeometry const aIntersection( *this, Rectangle( Point( 0, 0 ), 1488 aAllCellsWithHeaders.BottomRight() ), COL_ROW_HEADERS, ROW_COL_HEADERS ); 1489 Rectangle const aInters( aIntersection.getRect() ); 1490 pRenderer->PaintHeaderArea( 1491 *m_pDataWindow, aInters, true, true, rStyle 1492 ); 1493 } 1494 } 1495 1496 // ............................ 1497 // draw the table content row by row 1498 1499 TableSize colCount = getModel()->getColumnCount(); 1500 1501 // paint all rows 1502 Rectangle const aAllDataCellsArea( impl_getAllVisibleDataCellArea() ); 1503 for ( TableRowGeometry aRowIterator( *this, aAllCellsWithHeaders, getTopRow() ); 1504 aRowIterator.isValid(); 1505 aRowIterator.moveDown() ) 1506 { 1507 if ( _rUpdateRect.GetIntersection( aRowIterator.getRect() ).IsEmpty() ) 1508 continue; 1509 1510 bool const isControlFocused = m_rAntiImpl.HasControlFocus(); 1511 bool const isSelectedRow = isRowSelected( aRowIterator.getRow() ); 1512 1513 Rectangle const aRect = aRowIterator.getRect().GetIntersection( aAllDataCellsArea ); 1514 1515 // give the redenderer a chance to prepare the row 1516 pRenderer->PrepareRow( 1517 aRowIterator.getRow(), isControlFocused, isSelectedRow, 1518 *m_pDataWindow, aRect, rStyle 1519 ); 1520 1521 // paint the row header 1522 if ( m_pModel->hasRowHeaders() ) 1523 { 1524 const Rectangle aCurrentRowHeader( aRowHeaderArea.GetIntersection( aRowIterator.getRect() ) ); 1525 pRenderer->PaintRowHeader( isControlFocused, isSelectedRow, *m_pDataWindow, aCurrentRowHeader, 1526 rStyle ); 1527 } 1528 1529 if ( !colCount ) 1530 continue; 1531 1532 // paint all cells in this row 1533 for ( TableCellGeometry aCell( aRowIterator, m_nLeftColumn ); 1534 aCell.isValid(); 1535 aCell.moveRight() 1536 ) 1537 { 1538 bool isSelectedColumn = false; 1539 pRenderer->PaintCell( aCell.getColumn(), isSelectedRow || isSelectedColumn, isControlFocused, 1540 *m_pDataWindow, aCell.getRect(), rStyle ); 1541 } 1542 } 1543 } 1544 //------------------------------------------------------------------------------------------------------------------ 1545 void TableControl_Impl::hideCursor() 1546 { 1547 DBG_CHECK_ME(); 1548 1549 if ( ++m_nCursorHidden == 1 ) 1550 impl_ni_doSwitchCursor( false ); 1551 } 1552 1553 //------------------------------------------------------------------------------------------------------------------ 1554 void TableControl_Impl::showCursor() 1555 { 1556 DBG_CHECK_ME(); 1557 1558 DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" ); 1559 if ( --m_nCursorHidden == 0 ) 1560 impl_ni_doSwitchCursor( true ); 1561 } 1562 1563 //------------------------------------------------------------------------------------------------------------------ 1564 bool TableControl_Impl::dispatchAction( TableControlAction _eAction ) 1565 { 1566 DBG_CHECK_ME(); 1567 1568 bool bSuccess = false; 1569 bool selectionChanged = false; 1570 1571 switch ( _eAction ) 1572 { 1573 case cursorDown: 1574 if ( m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION ) 1575 { 1576 //if other rows already selected, deselect them 1577 if ( m_aSelectedRows.size()>0 ) 1578 { 1579 invalidateSelectedRows(); 1580 m_aSelectedRows.clear(); 1581 } 1582 if ( m_nCurRow < m_nRowCount-1 ) 1583 { 1584 ++m_nCurRow; 1585 m_aSelectedRows.push_back(m_nCurRow); 1586 } 1587 else 1588 m_aSelectedRows.push_back(m_nCurRow); 1589 invalidateRow( m_nCurRow ); 1590 ensureVisible(m_nCurColumn,m_nCurRow,false); 1591 selectionChanged = true; 1592 bSuccess = true; 1593 } 1594 else 1595 { 1596 if ( m_nCurRow < m_nRowCount - 1 ) 1597 bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 ); 1598 } 1599 break; 1600 1601 case cursorUp: 1602 if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1603 { 1604 if(m_aSelectedRows.size()>0) 1605 { 1606 invalidateSelectedRows(); 1607 m_aSelectedRows.clear(); 1608 } 1609 if(m_nCurRow>0) 1610 { 1611 --m_nCurRow; 1612 m_aSelectedRows.push_back(m_nCurRow); 1613 invalidateRow( m_nCurRow ); 1614 } 1615 else 1616 { 1617 m_aSelectedRows.push_back(m_nCurRow); 1618 invalidateRow( m_nCurRow ); 1619 } 1620 ensureVisible(m_nCurColumn,m_nCurRow,false); 1621 selectionChanged = true; 1622 bSuccess = true; 1623 } 1624 else 1625 { 1626 if ( m_nCurRow > 0 ) 1627 bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 ); 1628 } 1629 break; 1630 case cursorLeft: 1631 if ( m_nCurColumn > 0 ) 1632 bSuccess = goTo( m_nCurColumn - 1, m_nCurRow ); 1633 else 1634 if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) ) 1635 bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 ); 1636 break; 1637 1638 case cursorRight: 1639 if ( m_nCurColumn < m_nColumnCount - 1 ) 1640 bSuccess = goTo( m_nCurColumn + 1, m_nCurRow ); 1641 else 1642 if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) ) 1643 bSuccess = goTo( 0, m_nCurRow + 1 ); 1644 break; 1645 1646 case cursorToLineStart: 1647 bSuccess = goTo( 0, m_nCurRow ); 1648 break; 1649 1650 case cursorToLineEnd: 1651 bSuccess = goTo( m_nColumnCount - 1, m_nCurRow ); 1652 break; 1653 1654 case cursorToFirstLine: 1655 bSuccess = goTo( m_nCurColumn, 0 ); 1656 break; 1657 1658 case cursorToLastLine: 1659 bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 ); 1660 break; 1661 1662 case cursorPageUp: 1663 { 1664 RowPos nNewRow = ::std::max( (RowPos)0, m_nCurRow - impl_getVisibleRows( false ) ); 1665 bSuccess = goTo( m_nCurColumn, nNewRow ); 1666 } 1667 break; 1668 1669 case cursorPageDown: 1670 { 1671 RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) ); 1672 bSuccess = goTo( m_nCurColumn, nNewRow ); 1673 } 1674 break; 1675 1676 case cursorTopLeft: 1677 bSuccess = goTo( 0, 0 ); 1678 break; 1679 1680 case cursorBottomRight: 1681 bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 ); 1682 break; 1683 1684 case cursorSelectRow: 1685 { 1686 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1687 return bSuccess = false; 1688 //pos is the position of the current row in the vector of selected rows, if current row is selected 1689 int pos = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); 1690 //if current row is selected, it should be deselected, when ALT+SPACE are pressed 1691 if(pos>-1) 1692 { 1693 m_aSelectedRows.erase(m_aSelectedRows.begin()+pos); 1694 if(m_aSelectedRows.empty() && m_nAnchor != -1) 1695 m_nAnchor = -1; 1696 } 1697 //else select the row->put it in the vector 1698 else 1699 m_aSelectedRows.push_back(m_nCurRow); 1700 invalidateRow( m_nCurRow ); 1701 selectionChanged = true; 1702 bSuccess = true; 1703 } 1704 break; 1705 case cursorSelectRowUp: 1706 { 1707 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1708 return bSuccess = false; 1709 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1710 { 1711 //if there are other selected rows, deselect them 1712 return false; 1713 } 1714 else 1715 { 1716 //there are other selected rows 1717 if(m_aSelectedRows.size()>0) 1718 { 1719 //the anchor wasn't set -> a region is not selected, that's why clear all selection 1720 //and select the current row 1721 if(m_nAnchor==-1) 1722 { 1723 invalidateSelectedRows(); 1724 m_aSelectedRows.clear(); 1725 m_aSelectedRows.push_back(m_nCurRow); 1726 invalidateRow( m_nCurRow ); 1727 } 1728 else 1729 { 1730 //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected 1731 int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); 1732 int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow-1); 1733 if(prevRow>-1) 1734 { 1735 //if m_nCurRow isn't the upper one, can move up, otherwise not 1736 if(m_nCurRow>0) 1737 m_nCurRow--; 1738 else 1739 return bSuccess = true; 1740 //if nextRow already selected, deselect it, otherwise select it 1741 if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) 1742 { 1743 m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); 1744 invalidateRow( m_nCurRow + 1 ); 1745 } 1746 else 1747 { 1748 m_aSelectedRows.push_back(m_nCurRow); 1749 invalidateRow( m_nCurRow ); 1750 } 1751 } 1752 else 1753 { 1754 if(m_nCurRow>0) 1755 { 1756 m_aSelectedRows.push_back(m_nCurRow); 1757 m_nCurRow--; 1758 m_aSelectedRows.push_back(m_nCurRow); 1759 invalidateSelectedRegion( m_nCurRow+1, m_nCurRow ); 1760 } 1761 } 1762 } 1763 } 1764 else 1765 { 1766 //if nothing is selected and the current row isn't the upper one 1767 //select the current and one row above 1768 //otherwise select only the upper row 1769 if(m_nCurRow>0) 1770 { 1771 m_aSelectedRows.push_back(m_nCurRow); 1772 m_nCurRow--; 1773 m_aSelectedRows.push_back(m_nCurRow); 1774 invalidateSelectedRegion( m_nCurRow+1, m_nCurRow ); 1775 } 1776 else 1777 { 1778 m_aSelectedRows.push_back(m_nCurRow); 1779 invalidateRow( m_nCurRow ); 1780 } 1781 } 1782 m_pSelEngine->SetAnchor(sal_True); 1783 m_nAnchor = m_nCurRow; 1784 ensureVisible(m_nCurColumn, m_nCurRow, false); 1785 selectionChanged = true; 1786 bSuccess = true; 1787 } 1788 } 1789 break; 1790 case cursorSelectRowDown: 1791 { 1792 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1793 bSuccess = false; 1794 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1795 { 1796 bSuccess = false; 1797 } 1798 else 1799 { 1800 if(m_aSelectedRows.size()>0) 1801 { 1802 //the anchor wasn't set -> a region is not selected, that's why clear all selection 1803 //and select the current row 1804 if(m_nAnchor==-1) 1805 { 1806 invalidateSelectedRows(); 1807 m_aSelectedRows.clear(); 1808 m_aSelectedRows.push_back(m_nCurRow); 1809 invalidateRow( m_nCurRow ); 1810 } 1811 else 1812 { 1813 //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected 1814 int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); 1815 int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow+1); 1816 if(prevRow>-1) 1817 { 1818 //if m_nCurRow isn't the last one, can move down, otherwise not 1819 if(m_nCurRow<m_nRowCount-1) 1820 m_nCurRow++; 1821 else 1822 return bSuccess = true; 1823 //if next row already selected, deselect it, otherwise select it 1824 if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) 1825 { 1826 m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); 1827 invalidateRow( m_nCurRow - 1 ); 1828 } 1829 else 1830 { 1831 m_aSelectedRows.push_back(m_nCurRow); 1832 invalidateRow( m_nCurRow ); 1833 } 1834 } 1835 else 1836 { 1837 if(m_nCurRow<m_nRowCount-1) 1838 { 1839 m_aSelectedRows.push_back(m_nCurRow); 1840 m_nCurRow++; 1841 m_aSelectedRows.push_back(m_nCurRow); 1842 invalidateSelectedRegion( m_nCurRow-1, m_nCurRow ); 1843 } 1844 } 1845 } 1846 } 1847 else 1848 { 1849 //there wasn't any selection, select current and row beneath, otherwise only row beneath 1850 if(m_nCurRow<m_nRowCount-1) 1851 { 1852 m_aSelectedRows.push_back(m_nCurRow); 1853 m_nCurRow++; 1854 m_aSelectedRows.push_back(m_nCurRow); 1855 invalidateSelectedRegion( m_nCurRow-1, m_nCurRow ); 1856 } 1857 else 1858 { 1859 m_aSelectedRows.push_back(m_nCurRow); 1860 invalidateRow( m_nCurRow ); 1861 } 1862 } 1863 m_pSelEngine->SetAnchor(sal_True); 1864 m_nAnchor = m_nCurRow; 1865 ensureVisible(m_nCurColumn, m_nCurRow, false); 1866 selectionChanged = true; 1867 bSuccess = true; 1868 } 1869 } 1870 break; 1871 1872 case cursorSelectRowAreaTop: 1873 { 1874 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1875 bSuccess = false; 1876 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1877 bSuccess = false; 1878 else 1879 { 1880 //select the region between the current and the upper row 1881 RowPos iter = m_nCurRow; 1882 invalidateSelectedRegion( m_nCurRow, 0 ); 1883 //put the rows in vector 1884 while(iter>=0) 1885 { 1886 if ( !isRowSelected( iter ) ) 1887 m_aSelectedRows.push_back(iter); 1888 --iter; 1889 } 1890 m_nCurRow = 0; 1891 m_nAnchor = m_nCurRow; 1892 m_pSelEngine->SetAnchor(sal_True); 1893 ensureVisible(m_nCurColumn, 0, false); 1894 selectionChanged = true; 1895 bSuccess = true; 1896 } 1897 } 1898 break; 1899 1900 case cursorSelectRowAreaBottom: 1901 { 1902 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1903 return bSuccess = false; 1904 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1905 return bSuccess = false; 1906 //select the region between the current and the last row 1907 RowPos iter = m_nCurRow; 1908 invalidateSelectedRegion( m_nCurRow, m_nRowCount-1 ); 1909 //put the rows in the vector 1910 while(iter<=m_nRowCount) 1911 { 1912 if ( !isRowSelected( iter ) ) 1913 m_aSelectedRows.push_back(iter); 1914 ++iter; 1915 } 1916 m_nCurRow = m_nRowCount-1; 1917 m_nAnchor = m_nCurRow; 1918 m_pSelEngine->SetAnchor(sal_True); 1919 ensureVisible(m_nCurColumn, m_nRowCount-1, false); 1920 selectionChanged = true; 1921 bSuccess = true; 1922 } 1923 break; 1924 default: 1925 DBG_ERROR( "TableControl_Impl::dispatchAction: unsupported action!" ); 1926 break; 1927 } 1928 1929 if ( bSuccess && selectionChanged ) 1930 { 1931 m_rAntiImpl.Select(); 1932 } 1933 1934 return bSuccess; 1935 } 1936 1937 //------------------------------------------------------------------------------------------------------------------ 1938 void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow ) 1939 { 1940 PTableRenderer pRenderer = !!m_pModel ? m_pModel->getRenderer() : PTableRenderer(); 1941 if ( !!pRenderer ) 1942 { 1943 Rectangle aCellRect; 1944 impl_getCellRect( m_nCurColumn, m_nCurRow, aCellRect ); 1945 if ( _bShow ) 1946 pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect ); 1947 else 1948 pRenderer->HideCellCursor( *m_pDataWindow, aCellRect ); 1949 } 1950 } 1951 1952 //------------------------------------------------------------------------------------------------------------------ 1953 void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, Rectangle& _rCellRect ) const 1954 { 1955 DBG_CHECK_ME(); 1956 1957 if ( !m_pModel 1958 || ( COL_INVALID == _nColumn ) 1959 || ( ROW_INVALID == _nRow ) 1960 ) 1961 { 1962 _rCellRect.SetEmpty(); 1963 return; 1964 } 1965 1966 TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow ); 1967 _rCellRect = aCell.getRect(); 1968 } 1969 1970 //------------------------------------------------------------------------------------------------------------------ 1971 RowPos TableControl_Impl::getRowAtPoint( const Point& rPoint ) const 1972 { 1973 DBG_CHECK_ME(); 1974 return impl_getRowForAbscissa( rPoint.Y() ); 1975 } 1976 1977 //------------------------------------------------------------------------------------------------------------------ 1978 ColPos TableControl_Impl::getColAtPoint( const Point& rPoint ) const 1979 { 1980 DBG_CHECK_ME(); 1981 return impl_getColumnForOrdinate( rPoint.X() ); 1982 } 1983 1984 //------------------------------------------------------------------------------------------------------------------ 1985 TableCell TableControl_Impl::hitTest( Point const & i_point ) const 1986 { 1987 TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) ); 1988 if ( aCell.nColumn > COL_ROW_HEADERS ) 1989 { 1990 PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn ); 1991 MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] ); 1992 if ( ( rColInfo.getEnd() - 3 <= i_point.X() ) 1993 && ( rColInfo.getEnd() >= i_point.X() ) 1994 && pColumn->isResizable() 1995 ) 1996 { 1997 aCell.eArea = ColumnDivider; 1998 } 1999 } 2000 return aCell; 2001 } 2002 2003 //------------------------------------------------------------------------------------------------------------------ 2004 ColumnMetrics TableControl_Impl::getColumnMetrics( ColPos const i_column ) const 2005 { 2006 DBG_CHECK_ME(); 2007 2008 ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ), 2009 "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() ); 2010 return (ColumnMetrics const &)m_aColumnWidths[ i_column ]; 2011 } 2012 2013 //------------------------------------------------------------------------------------------------------------------ 2014 PTableModel TableControl_Impl::getModel() const 2015 { 2016 return m_pModel; 2017 } 2018 2019 //------------------------------------------------------------------------------------------------------------------ 2020 RowPos TableControl_Impl::getCurrentColumn() const 2021 { 2022 return m_nCurColumn; 2023 } 2024 2025 //------------------------------------------------------------------------------------------------------------------ 2026 RowPos TableControl_Impl::getCurrentRow() const 2027 { 2028 return m_nCurRow; 2029 } 2030 2031 //------------------------------------------------------------------------------------------------------------------ 2032 ::Size TableControl_Impl::getTableSizePixel() const 2033 { 2034 return m_pDataWindow->GetOutputSizePixel(); 2035 } 2036 2037 //------------------------------------------------------------------------------------------------------------------ 2038 void TableControl_Impl::setPointer( Pointer const & i_pointer ) 2039 { 2040 DBG_CHECK_ME(); 2041 m_pDataWindow->SetPointer( i_pointer ); 2042 } 2043 2044 //------------------------------------------------------------------------------------------------------------------ 2045 void TableControl_Impl::captureMouse() 2046 { 2047 m_pDataWindow->CaptureMouse(); 2048 } 2049 2050 //------------------------------------------------------------------------------------------------------------------ 2051 void TableControl_Impl::releaseMouse() 2052 { 2053 m_pDataWindow->ReleaseMouse(); 2054 } 2055 2056 //------------------------------------------------------------------------------------------------------------------ 2057 void TableControl_Impl::invalidate( TableArea const i_what ) 2058 { 2059 switch ( i_what ) 2060 { 2061 case TableAreaColumnHeaders: 2062 m_pDataWindow->Invalidate( calcHeaderRect( true ) ); 2063 break; 2064 2065 case TableAreaRowHeaders: 2066 m_pDataWindow->Invalidate( calcHeaderRect( false ) ); 2067 break; 2068 2069 case TableAreaDataArea: 2070 m_pDataWindow->Invalidate( impl_getAllVisibleDataCellArea() ); 2071 break; 2072 2073 case TableAreaAll: 2074 m_pDataWindow->Invalidate(); 2075 m_pDataWindow->GetParent()->Invalidate( INVALIDATE_TRANSPARENT ); 2076 break; 2077 } 2078 } 2079 2080 //------------------------------------------------------------------------------------------------------------------ 2081 long TableControl_Impl::pixelWidthToAppFont( long const i_pixels ) const 2082 { 2083 return m_pDataWindow->PixelToLogic( Size( i_pixels, 0 ), MAP_APPFONT ).Width(); 2084 } 2085 2086 //------------------------------------------------------------------------------------------------------------------ 2087 long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits ) const 2088 { 2089 return m_pDataWindow->LogicToPixel( Size( i_appFontUnits, 0 ), MAP_APPFONT ).Width(); 2090 } 2091 2092 //------------------------------------------------------------------------------------------------------------------ 2093 void TableControl_Impl::hideTracking() 2094 { 2095 m_pDataWindow->HideTracking(); 2096 } 2097 2098 //------------------------------------------------------------------------------------------------------------------ 2099 void TableControl_Impl::showTracking( Rectangle const & i_location, sal_uInt16 const i_flags ) 2100 { 2101 m_pDataWindow->ShowTracking( i_location, i_flags ); 2102 } 2103 2104 //------------------------------------------------------------------------------------------------------------------ 2105 bool TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row ) 2106 { 2107 DBG_CHECK_ME(); 2108 return goTo( i_col, i_row ); 2109 } 2110 2111 //------------------------------------------------------------------------------------------------------------------ 2112 void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow ) 2113 { 2114 DBG_CHECK_ME(); 2115 // get the visible area of the table control and set the Left and right border of the region to be repainted 2116 Rectangle const aAllCells( impl_getAllVisibleCellsArea() ); 2117 2118 Rectangle aInvalidateRect; 2119 aInvalidateRect.Left() = aAllCells.Left(); 2120 aInvalidateRect.Right() = aAllCells.Right(); 2121 // if only one row is selected 2122 if ( _nPrevRow == _nCurRow ) 2123 { 2124 Rectangle aCellRect; 2125 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2126 aInvalidateRect.Top() = aCellRect.Top(); 2127 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2128 } 2129 //if the region is above the current row 2130 else if(_nPrevRow < _nCurRow ) 2131 { 2132 Rectangle aCellRect; 2133 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); 2134 aInvalidateRect.Top() = aCellRect.Top(); 2135 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2136 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2137 } 2138 //if the region is beneath the current row 2139 else 2140 { 2141 Rectangle aCellRect; 2142 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2143 aInvalidateRect.Top() = aCellRect.Top(); 2144 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); 2145 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2146 } 2147 m_pDataWindow->Invalidate( aInvalidateRect ); 2148 } 2149 2150 //------------------------------------------------------------------------------------------------------------------ 2151 void TableControl_Impl::invalidateSelectedRows() 2152 { 2153 for ( ::std::vector< RowPos >::iterator selRow = m_aSelectedRows.begin(); 2154 selRow != m_aSelectedRows.end(); 2155 ++selRow 2156 ) 2157 { 2158 invalidateRow( *selRow ); 2159 } 2160 } 2161 2162 //------------------------------------------------------------------------------------------------------------------ 2163 void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ) 2164 { 2165 RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow; 2166 RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1; 2167 RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow; 2168 2169 Rectangle aInvalidateRect; 2170 2171 Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() ); 2172 TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true ); 2173 while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) ) 2174 { 2175 aInvalidateRect.Union( aRow.getRect() ); 2176 aRow.moveDown(); 2177 } 2178 2179 if ( i_lastRow == ROW_INVALID ) 2180 aInvalidateRect.Bottom() = m_pDataWindow->GetOutputSizePixel().Height(); 2181 2182 m_pDataWindow->Invalidate( aInvalidateRect, 2183 m_pDataWindow->GetControlBackground().GetTransparency() ? INVALIDATE_TRANSPARENT : 0 ); 2184 } 2185 2186 //------------------------------------------------------------------------------ 2187 void TableControl_Impl::checkCursorPosition() 2188 { 2189 DBG_CHECK_ME(); 2190 2191 TableSize nVisibleRows = impl_getVisibleRows(true); 2192 TableSize nVisibleCols = impl_getVisibleColumns(true); 2193 if ( ( m_nTopRow + nVisibleRows > m_nRowCount ) 2194 && ( m_nRowCount >= nVisibleRows ) 2195 ) 2196 { 2197 --m_nTopRow; 2198 } 2199 else 2200 { 2201 m_nTopRow = 0; 2202 } 2203 2204 if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount ) 2205 && ( m_nColumnCount >= nVisibleCols ) 2206 ) 2207 { 2208 --m_nLeftColumn; 2209 } 2210 else 2211 { 2212 m_nLeftColumn = 0; 2213 } 2214 2215 m_pDataWindow->Invalidate(); 2216 } 2217 2218 //-------------------------------------------------------------------- 2219 TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const 2220 { 2221 DBG_CHECK_ME(); 2222 2223 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" ); 2224 2225 return lcl_getRowsFittingInto( 2226 m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel, 2227 m_nRowHeightPixel, 2228 _bAcceptPartialRow 2229 ); 2230 } 2231 2232 //-------------------------------------------------------------------- 2233 TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const 2234 { 2235 DBG_CHECK_ME(); 2236 2237 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" ); 2238 2239 return lcl_getColumnsVisibleWithin( 2240 Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ), 2241 m_nLeftColumn, 2242 *this, 2243 _bAcceptPartialCol 2244 ); 2245 } 2246 2247 //-------------------------------------------------------------------- 2248 bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow ) 2249 { 2250 DBG_CHECK_ME(); 2251 2252 // TODO: give veto listeners a chance 2253 2254 if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount ) 2255 || ( _nRow < 0 ) || ( _nRow >= m_nRowCount ) 2256 ) 2257 { 2258 OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" ); 2259 return false; 2260 } 2261 2262 SuppressCursor aHideCursor( *this ); 2263 m_nCurColumn = _nColumn; 2264 m_nCurRow = _nRow; 2265 2266 // ensure that the new cell is visible 2267 ensureVisible( m_nCurColumn, m_nCurRow, false ); 2268 return true; 2269 } 2270 2271 //-------------------------------------------------------------------- 2272 void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility ) 2273 { 2274 DBG_CHECK_ME(); 2275 DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount ) 2276 && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ), 2277 "TableControl_Impl::ensureVisible: invalid coordinates!" ); 2278 2279 SuppressCursor aHideCursor( *this ); 2280 2281 if ( _nColumn < m_nLeftColumn ) 2282 impl_scrollColumns( _nColumn - m_nLeftColumn ); 2283 else 2284 { 2285 TableSize nVisibleColumns = impl_getVisibleColumns( _bAcceptPartialVisibility ); 2286 if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 ) 2287 { 2288 impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) ); 2289 // TODO: since not all columns have the same width, this might in theory result 2290 // in the column still not being visible. 2291 } 2292 } 2293 2294 if ( _nRow < m_nTopRow ) 2295 impl_scrollRows( _nRow - m_nTopRow ); 2296 else 2297 { 2298 TableSize nVisibleRows = impl_getVisibleRows( _bAcceptPartialVisibility ); 2299 if ( _nRow > m_nTopRow + nVisibleRows - 1 ) 2300 impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) ); 2301 } 2302 } 2303 2304 //-------------------------------------------------------------------- 2305 ::rtl::OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col ) 2306 { 2307 Any aCellValue; 2308 m_pModel->getCellContent( i_col, i_row, aCellValue ); 2309 2310 ::rtl::OUString sCellStringContent; 2311 m_pModel->getRenderer()->GetFormattedCellString( aCellValue, i_col, i_row, sCellStringContent ); 2312 2313 return sCellStringContent; 2314 } 2315 2316 //-------------------------------------------------------------------- 2317 TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta ) 2318 { 2319 // compute new top row 2320 RowPos nNewTopRow = 2321 ::std::max( 2322 ::std::min( (RowPos)( m_nTopRow + _nRowDelta ), (RowPos)( m_nRowCount - 1 ) ), 2323 (RowPos)0 2324 ); 2325 2326 RowPos nOldTopRow = m_nTopRow; 2327 m_nTopRow = nNewTopRow; 2328 2329 // if updates are enabled currently, scroll the viewport 2330 if ( m_nTopRow != nOldTopRow ) 2331 { 2332 DBG_SUSPEND_INV( INV_SCROLL_POSITION ); 2333 SuppressCursor aHideCursor( *this ); 2334 // TODO: call a onStartScroll at our listener (or better an own onStartScroll, 2335 // which hides the cursor and then calls the listener) 2336 // Same for onEndScroll 2337 2338 // scroll the view port, if possible 2339 long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow ); 2340 2341 Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() ); 2342 2343 if ( m_pDataWindow->GetBackground().IsScrollable() 2344 && abs( nPixelDelta ) < aDataArea.GetHeight() 2345 ) 2346 { 2347 m_pDataWindow->Scroll( 0, (long)-nPixelDelta, aDataArea, SCROLL_CLIP | SCROLL_UPDATE | SCROLL_CHILDREN); 2348 } 2349 else 2350 { 2351 m_pDataWindow->Invalidate( INVALIDATE_UPDATE ); 2352 m_pDataWindow->GetParent()->Invalidate( INVALIDATE_TRANSPARENT ); 2353 } 2354 2355 // update the position at the vertical scrollbar 2356 if ( m_pVScroll != NULL ) 2357 m_pVScroll->SetThumbPos( m_nTopRow ); 2358 } 2359 2360 // The scroll bar availaility might change when we scrolled. 2361 // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10. 2362 // Now let 2363 // - the user scroll to row number 6, so the last 5 rows are visible 2364 // - somebody remove the last 4 rows 2365 // - the user scroll to row number 5 being the top row, so the last two rows are visible 2366 // - somebody remove row number 6 2367 // - the user scroll to row number 1 2368 // => in this case, the need for the scrollbar vanishes immediately. 2369 if ( m_nTopRow == 0 ) 2370 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); 2371 2372 return (TableSize)( m_nTopRow - nOldTopRow ); 2373 } 2374 2375 //-------------------------------------------------------------------- 2376 TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta ) 2377 { 2378 DBG_CHECK_ME(); 2379 return impl_ni_ScrollRows( i_rowDelta ); 2380 } 2381 2382 //-------------------------------------------------------------------- 2383 TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta ) 2384 { 2385 // compute new left column 2386 const ColPos nNewLeftColumn = 2387 ::std::max( 2388 ::std::min( (ColPos)( m_nLeftColumn + _nColumnDelta ), (ColPos)( m_nColumnCount - 1 ) ), 2389 (ColPos)0 2390 ); 2391 2392 const ColPos nOldLeftColumn = m_nLeftColumn; 2393 m_nLeftColumn = nNewLeftColumn; 2394 2395 // if updates are enabled currently, scroll the viewport 2396 if ( m_nLeftColumn != nOldLeftColumn ) 2397 { 2398 DBG_SUSPEND_INV( INV_SCROLL_POSITION ); 2399 SuppressCursor aHideCursor( *this ); 2400 // TODO: call a onStartScroll at our listener (or better an own onStartScroll, 2401 // which hides the cursor and then calls the listener) 2402 // Same for onEndScroll 2403 2404 // scroll the view port, if possible 2405 const Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() ); 2406 2407 long nPixelDelta = 2408 m_aColumnWidths[ nOldLeftColumn ].getStart() 2409 - m_aColumnWidths[ m_nLeftColumn ].getStart(); 2410 2411 // update our column positions 2412 // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct 2413 // information in m_aColumnWidths 2414 for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin(); 2415 colPos != m_aColumnWidths.end(); 2416 ++colPos 2417 ) 2418 { 2419 colPos->move( nPixelDelta ); 2420 } 2421 2422 // scroll the window content (if supported and possible), or invalidate the complete window 2423 if ( m_pDataWindow->GetBackground().IsScrollable() 2424 && abs( nPixelDelta ) < aDataArea.GetWidth() 2425 ) 2426 { 2427 m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, SCROLL_CLIP | SCROLL_UPDATE ); 2428 } 2429 else 2430 { 2431 m_pDataWindow->Invalidate( INVALIDATE_UPDATE ); 2432 m_pDataWindow->GetParent()->Invalidate( INVALIDATE_TRANSPARENT ); 2433 } 2434 2435 // update the position at the horizontal scrollbar 2436 if ( m_pHScroll != NULL ) 2437 m_pHScroll->SetThumbPos( m_nLeftColumn ); 2438 } 2439 2440 // The scroll bar availaility might change when we scrolled. This is because we do not hide 2441 // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will 2442 // be auto-hidden when it's scrolled back to pos 0. 2443 if ( m_nLeftColumn == 0 ) 2444 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); 2445 2446 return (TableSize)( m_nLeftColumn - nOldLeftColumn ); 2447 } 2448 2449 //------------------------------------------------------------------------------------------------------------------ 2450 TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta ) 2451 { 2452 DBG_CHECK_ME(); 2453 return impl_ni_ScrollColumns( i_columnDelta ); 2454 } 2455 2456 //------------------------------------------------------------------------------------------------------------------ 2457 SelectionEngine* TableControl_Impl::getSelEngine() 2458 { 2459 return m_pSelEngine; 2460 } 2461 2462 //------------------------------------------------------------------------------------------------------------------ 2463 ScrollBar* TableControl_Impl::getHorzScrollbar() 2464 { 2465 return m_pHScroll; 2466 } 2467 2468 //------------------------------------------------------------------------------------------------------------------ 2469 ScrollBar* TableControl_Impl::getVertScrollbar() 2470 { 2471 return m_pVScroll; 2472 } 2473 2474 //------------------------------------------------------------------------------------------------------------------ 2475 bool TableControl_Impl::isRowSelected( RowPos i_row ) const 2476 { 2477 return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end(); 2478 } 2479 2480 //------------------------------------------------------------------------------------------------------------------ 2481 RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const 2482 { 2483 if ( i_selectionIndex < m_aSelectedRows.size() ) 2484 return m_aSelectedRows[ i_selectionIndex ]; 2485 return ROW_INVALID; 2486 } 2487 2488 //------------------------------------------------------------------------------------------------------------------ 2489 int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current) 2490 { 2491 std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current); 2492 if ( it != selectedRows.end() ) 2493 { 2494 return it - selectedRows.begin(); 2495 } 2496 return -1; 2497 } 2498 2499 //-------------------------------------------------------------------- 2500 ColPos TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate ) const 2501 { 2502 DBG_CHECK_ME(); 2503 2504 if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) ) 2505 return COL_INVALID; 2506 2507 if ( i_ordinate < m_nRowHeaderWidthPixel ) 2508 return COL_ROW_HEADERS; 2509 2510 ColumnPositions::const_iterator lowerBound = ::std::lower_bound( 2511 m_aColumnWidths.begin(), 2512 m_aColumnWidths.end(), 2513 i_ordinate + 1, 2514 ColumnInfoPositionLess() 2515 ); 2516 if ( lowerBound == m_aColumnWidths.end() ) 2517 { 2518 // point is *behind* the start of the last column ... 2519 if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() ) 2520 // ... but still before its end 2521 return m_nColumnCount - 1; 2522 return COL_INVALID; 2523 } 2524 return lowerBound - m_aColumnWidths.begin(); 2525 } 2526 2527 //-------------------------------------------------------------------- 2528 RowPos TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa ) const 2529 { 2530 DBG_CHECK_ME(); 2531 2532 if ( i_abscissa < 0 ) 2533 return ROW_INVALID; 2534 2535 if ( i_abscissa < m_nColHeaderHeightPixel ) 2536 return ROW_COL_HEADERS; 2537 2538 long const abscissa = i_abscissa - m_nColHeaderHeightPixel; 2539 long const row = m_nTopRow + abscissa / m_nRowHeightPixel; 2540 return row < m_pModel->getRowCount() ? row : ROW_INVALID; 2541 } 2542 2543 //-------------------------------------------------------------------- 2544 bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex ) 2545 { 2546 DBG_CHECK_ME(); 2547 2548 ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex ); 2549 if ( selPos == m_aSelectedRows.end() ) 2550 return false; 2551 2552 m_aSelectedRows.erase( selPos ); 2553 return true; 2554 } 2555 2556 //-------------------------------------------------------------------- 2557 bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex ) 2558 { 2559 DBG_CHECK_ME(); 2560 2561 if ( isRowSelected( i_rowIndex ) ) 2562 return false; 2563 2564 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); 2565 switch ( eSelMode ) 2566 { 2567 case SINGLE_SELECTION: 2568 if ( !m_aSelectedRows.empty() ) 2569 { 2570 OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" ); 2571 m_aSelectedRows[0] = i_rowIndex; 2572 break; 2573 } 2574 // fall through 2575 2576 case MULTIPLE_SELECTION: 2577 m_aSelectedRows.push_back( i_rowIndex ); 2578 break; 2579 2580 default: 2581 OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" ); 2582 return false; 2583 } 2584 2585 return true; 2586 } 2587 2588 //-------------------------------------------------------------------- 2589 bool TableControl_Impl::markAllRowsAsDeselected() 2590 { 2591 if ( m_aSelectedRows.empty() ) 2592 return false; 2593 2594 m_aSelectedRows.clear(); 2595 return true; 2596 } 2597 2598 //-------------------------------------------------------------------- 2599 bool TableControl_Impl::markAllRowsAsSelected() 2600 { 2601 DBG_CHECK_ME(); 2602 2603 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); 2604 ENSURE_OR_RETURN_FALSE( eSelMode == MULTIPLE_SELECTION, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" ); 2605 2606 if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) ) 2607 { 2608 #if OSL_DEBUG_LEVEL > 0 2609 for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row ) 2610 { 2611 OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" ); 2612 } 2613 #endif 2614 // already all rows marked as selected 2615 return false; 2616 } 2617 2618 m_aSelectedRows.clear(); 2619 for ( RowPos i=0; i < m_pModel->getRowCount(); ++i ) 2620 m_aSelectedRows.push_back(i); 2621 2622 return true; 2623 } 2624 2625 //-------------------------------------------------------------------- 2626 void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) 2627 { 2628 impl_commitAccessibleEvent( i_eventID, i_newValue, i_oldValue ); 2629 } 2630 2631 //-------------------------------------------------------------------- 2632 void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) 2633 { 2634 DBG_CHECK_ME(); 2635 if ( impl_isAccessibleAlive() ) 2636 m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue ); 2637 } 2638 2639 //-------------------------------------------------------------------- 2640 void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) 2641 { 2642 DBG_CHECK_ME(); 2643 if ( impl_isAccessibleAlive() ) 2644 m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue ); 2645 } 2646 2647 //-------------------------------------------------------------------- 2648 Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader) 2649 { 2650 Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() ); 2651 Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() ); 2652 if ( bColHeader ) 2653 return Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) ); 2654 else 2655 return Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) ); 2656 } 2657 2658 //-------------------------------------------------------------------- 2659 Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos ) 2660 { 2661 Rectangle const aHeaderRect = calcHeaderRect( bColHeader ); 2662 TableCellGeometry const aGeometry( 2663 *this, aHeaderRect, 2664 bColHeader ? nPos : COL_ROW_HEADERS, 2665 bColHeader ? ROW_COL_HEADERS : nPos 2666 ); 2667 return aGeometry.getRect(); 2668 } 2669 2670 //-------------------------------------------------------------------- 2671 Rectangle TableControl_Impl::calcTableRect() 2672 { 2673 return impl_getAllVisibleDataCellArea(); 2674 } 2675 2676 //-------------------------------------------------------------------- 2677 Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol ) 2678 { 2679 Rectangle aCellRect; 2680 impl_getCellRect( nRow, nCol, aCellRect ); 2681 return aCellRect; 2682 } 2683 2684 //-------------------------------------------------------------------- 2685 IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ ) 2686 { 2687 DBG_CHECK_ME(); 2688 // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of 2689 // doing a complete re-layout? 2690 impl_ni_relayout(); 2691 return 1L; 2692 } 2693 2694 //-------------------------------------------------------------------- 2695 IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar ) 2696 { 2697 DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ), 2698 "TableControl_Impl::OnScroll: where did this come from?" ); 2699 2700 if ( _pScrollbar == m_pVScroll ) 2701 impl_ni_ScrollRows( _pScrollbar->GetDelta() ); 2702 else 2703 impl_ni_ScrollColumns( _pScrollbar->GetDelta() ); 2704 2705 return 0L; 2706 } 2707 2708 //------------------------------------------------------------------------------------------------------------------ 2709 Reference< XAccessible > TableControl_Impl::getAccessible( Window& i_parentWindow ) 2710 { 2711 DBG_TESTSOLARMUTEX(); 2712 if ( m_pAccessibleTable == NULL ) 2713 { 2714 Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible(); 2715 if ( xAccParent.is() ) 2716 { 2717 m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl( 2718 xAccParent, m_rAntiImpl 2719 ); 2720 } 2721 } 2722 2723 Reference< XAccessible > xAccessible; 2724 if ( m_pAccessibleTable ) 2725 xAccessible = m_pAccessibleTable->getMyself(); 2726 return xAccessible; 2727 } 2728 2729 //------------------------------------------------------------------------------------------------------------------ 2730 void TableControl_Impl::disposeAccessible() 2731 { 2732 if ( m_pAccessibleTable ) 2733 m_pAccessibleTable->dispose(); 2734 m_pAccessibleTable = NULL; 2735 } 2736 2737 //------------------------------------------------------------------------------------------------------------------ 2738 bool TableControl_Impl::impl_isAccessibleAlive() const 2739 { 2740 DBG_CHECK_ME(); 2741 return ( NULL != m_pAccessibleTable ) && m_pAccessibleTable->isAlive(); 2742 } 2743 2744 //------------------------------------------------------------------------------------------------------------------ 2745 void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue, Any const & i_oldValue ) 2746 { 2747 DBG_CHECK_ME(); 2748 if ( impl_isAccessibleAlive() ) 2749 m_pAccessibleTable->commitEvent( i_eventID, i_newValue, i_oldValue ); 2750 } 2751 2752 //================================================================================================================== 2753 //= TableFunctionSet 2754 //================================================================================================================== 2755 //------------------------------------------------------------------------------------------------------------------ 2756 TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl) 2757 :m_pTableControl( _pTableControl) 2758 ,m_nCurrentRow( ROW_INVALID ) 2759 { 2760 } 2761 //------------------------------------------------------------------------------------------------------------------ 2762 TableFunctionSet::~TableFunctionSet() 2763 { 2764 } 2765 //------------------------------------------------------------------------------------------------------------------ 2766 void TableFunctionSet::BeginDrag() 2767 { 2768 } 2769 //------------------------------------------------------------------------------------------------------------------ 2770 void TableFunctionSet::CreateAnchor() 2771 { 2772 m_pTableControl->setAnchor( m_pTableControl->getCurRow() ); 2773 } 2774 2775 //------------------------------------------------------------------------------------------------------------------ 2776 void TableFunctionSet::DestroyAnchor() 2777 { 2778 m_pTableControl->setAnchor( ROW_INVALID ); 2779 } 2780 2781 //------------------------------------------------------------------------------------------------------------------ 2782 sal_Bool TableFunctionSet::SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor) 2783 { 2784 sal_Bool bHandled = sal_False; 2785 // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click 2786 RowPos newRow = m_pTableControl->getRowAtPoint( rPoint ); 2787 if ( newRow == ROW_COL_HEADERS ) 2788 newRow = m_pTableControl->getTopRow(); 2789 2790 ColPos newCol = m_pTableControl->getColAtPoint( rPoint ); 2791 if ( newCol == COL_ROW_HEADERS ) 2792 newCol = m_pTableControl->getLeftColumn(); 2793 2794 if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) ) 2795 return sal_False; 2796 2797 if ( bDontSelectAtCursor ) 2798 { 2799 if ( m_pTableControl->getSelectedRowCount() > 1 ) 2800 m_pTableControl->getSelEngine()->AddAlways(sal_True); 2801 bHandled = sal_True; 2802 } 2803 else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() ) 2804 { 2805 //selecting region, 2806 int diff = m_pTableControl->getCurRow() - newRow; 2807 //selected region lies above the last selection 2808 if( diff >= 0) 2809 { 2810 //put selected rows in vector 2811 while ( m_pTableControl->getAnchor() >= newRow ) 2812 { 2813 m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); 2814 m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); 2815 diff--; 2816 } 2817 m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); 2818 } 2819 //selected region lies beneath the last selected row 2820 else 2821 { 2822 while ( m_pTableControl->getAnchor() <= newRow ) 2823 { 2824 m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); 2825 m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); 2826 diff++; 2827 } 2828 m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); 2829 } 2830 m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow ); 2831 bHandled = sal_True; 2832 } 2833 //no region selected 2834 else 2835 { 2836 if ( !m_pTableControl->hasRowSelection() ) 2837 m_pTableControl->markRowAsSelected( newRow ); 2838 else 2839 { 2840 if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION ) 2841 { 2842 DeselectAll(); 2843 m_pTableControl->markRowAsSelected( newRow ); 2844 } 2845 else 2846 { 2847 m_pTableControl->markRowAsSelected( newRow ); 2848 } 2849 } 2850 if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION ) 2851 m_pTableControl->getSelEngine()->AddAlways(sal_True); 2852 2853 m_pTableControl->invalidateRow( newRow ); 2854 bHandled = sal_True; 2855 } 2856 m_pTableControl->goTo( newCol, newRow ); 2857 return bHandled; 2858 } 2859 //------------------------------------------------------------------------------------------------------------------ 2860 sal_Bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint ) 2861 { 2862 m_pTableControl->getSelEngine()->AddAlways(sal_False); 2863 if ( !m_pTableControl->hasRowSelection() ) 2864 return sal_False; 2865 else 2866 { 2867 RowPos curRow = m_pTableControl->getRowAtPoint( rPoint ); 2868 m_pTableControl->setAnchor( ROW_INVALID ); 2869 bool selected = m_pTableControl->isRowSelected( curRow ); 2870 m_nCurrentRow = curRow; 2871 return selected; 2872 } 2873 } 2874 //------------------------------------------------------------------------------------------------------------------ 2875 void TableFunctionSet::DeselectAtPoint( const Point& rPoint ) 2876 { 2877 (void)rPoint; 2878 m_pTableControl->invalidateRow( m_nCurrentRow ); 2879 m_pTableControl->markRowAsDeselected( m_nCurrentRow ); 2880 } 2881 2882 //------------------------------------------------------------------------------------------------------------------ 2883 void TableFunctionSet::DeselectAll() 2884 { 2885 if ( m_pTableControl->hasRowSelection() ) 2886 { 2887 for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i ) 2888 { 2889 RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i); 2890 m_pTableControl->invalidateRow( rowIndex ); 2891 } 2892 2893 m_pTableControl->markAllRowsAsDeselected(); 2894 } 2895 } 2896 2897 //...................................................................................................................... 2898 } } // namespace svt::table 2899 //...................................................................................................................... 2900