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 break; 2076 } 2077 } 2078 2079 //------------------------------------------------------------------------------------------------------------------ 2080 long TableControl_Impl::pixelWidthToAppFont( long const i_pixels ) const 2081 { 2082 return m_pDataWindow->PixelToLogic( Size( i_pixels, 0 ), MAP_APPFONT ).Width(); 2083 } 2084 2085 //------------------------------------------------------------------------------------------------------------------ 2086 long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits ) const 2087 { 2088 return m_pDataWindow->LogicToPixel( Size( i_appFontUnits, 0 ), MAP_APPFONT ).Width(); 2089 } 2090 2091 //------------------------------------------------------------------------------------------------------------------ 2092 void TableControl_Impl::hideTracking() 2093 { 2094 m_pDataWindow->HideTracking(); 2095 } 2096 2097 //------------------------------------------------------------------------------------------------------------------ 2098 void TableControl_Impl::showTracking( Rectangle const & i_location, sal_uInt16 const i_flags ) 2099 { 2100 m_pDataWindow->ShowTracking( i_location, i_flags ); 2101 } 2102 2103 //------------------------------------------------------------------------------------------------------------------ 2104 bool TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row ) 2105 { 2106 DBG_CHECK_ME(); 2107 return goTo( i_col, i_row ); 2108 } 2109 2110 //------------------------------------------------------------------------------------------------------------------ 2111 void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow ) 2112 { 2113 DBG_CHECK_ME(); 2114 // get the visible area of the table control and set the Left and right border of the region to be repainted 2115 Rectangle const aAllCells( impl_getAllVisibleCellsArea() ); 2116 2117 Rectangle aInvalidateRect; 2118 aInvalidateRect.Left() = aAllCells.Left(); 2119 aInvalidateRect.Right() = aAllCells.Right(); 2120 // if only one row is selected 2121 if ( _nPrevRow == _nCurRow ) 2122 { 2123 Rectangle aCellRect; 2124 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2125 aInvalidateRect.Top() = aCellRect.Top(); 2126 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2127 } 2128 //if the region is above the current row 2129 else if(_nPrevRow < _nCurRow ) 2130 { 2131 Rectangle aCellRect; 2132 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); 2133 aInvalidateRect.Top() = aCellRect.Top(); 2134 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2135 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2136 } 2137 //if the region is beneath the current row 2138 else 2139 { 2140 Rectangle aCellRect; 2141 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2142 aInvalidateRect.Top() = aCellRect.Top(); 2143 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); 2144 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2145 } 2146 m_pDataWindow->Invalidate( aInvalidateRect ); 2147 } 2148 2149 //------------------------------------------------------------------------------------------------------------------ 2150 void TableControl_Impl::invalidateSelectedRows() 2151 { 2152 for ( ::std::vector< RowPos >::iterator selRow = m_aSelectedRows.begin(); 2153 selRow != m_aSelectedRows.end(); 2154 ++selRow 2155 ) 2156 { 2157 invalidateRow( *selRow ); 2158 } 2159 } 2160 2161 //------------------------------------------------------------------------------------------------------------------ 2162 void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ) 2163 { 2164 RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow; 2165 RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1; 2166 RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow; 2167 2168 Rectangle aInvalidateRect; 2169 2170 Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() ); 2171 TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true ); 2172 while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) ) 2173 { 2174 aInvalidateRect.Union( aRow.getRect() ); 2175 aRow.moveDown(); 2176 } 2177 2178 if ( i_lastRow == ROW_INVALID ) 2179 aInvalidateRect.Bottom() = m_pDataWindow->GetOutputSizePixel().Height(); 2180 2181 m_pDataWindow->Invalidate( aInvalidateRect, 2182 m_pDataWindow->GetControlBackground().GetTransparency() ? INVALIDATE_TRANSPARENT : 0 ); 2183 } 2184 2185 //------------------------------------------------------------------------------ 2186 void TableControl_Impl::checkCursorPosition() 2187 { 2188 DBG_CHECK_ME(); 2189 2190 TableSize nVisibleRows = impl_getVisibleRows(true); 2191 TableSize nVisibleCols = impl_getVisibleColumns(true); 2192 if ( ( m_nTopRow + nVisibleRows > m_nRowCount ) 2193 && ( m_nRowCount >= nVisibleRows ) 2194 ) 2195 { 2196 --m_nTopRow; 2197 } 2198 else 2199 { 2200 m_nTopRow = 0; 2201 } 2202 2203 if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount ) 2204 && ( m_nColumnCount >= nVisibleCols ) 2205 ) 2206 { 2207 --m_nLeftColumn; 2208 } 2209 else 2210 { 2211 m_nLeftColumn = 0; 2212 } 2213 2214 m_pDataWindow->Invalidate(); 2215 } 2216 2217 //-------------------------------------------------------------------- 2218 TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const 2219 { 2220 DBG_CHECK_ME(); 2221 2222 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" ); 2223 2224 return lcl_getRowsFittingInto( 2225 m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel, 2226 m_nRowHeightPixel, 2227 _bAcceptPartialRow 2228 ); 2229 } 2230 2231 //-------------------------------------------------------------------- 2232 TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const 2233 { 2234 DBG_CHECK_ME(); 2235 2236 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" ); 2237 2238 return lcl_getColumnsVisibleWithin( 2239 Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ), 2240 m_nLeftColumn, 2241 *this, 2242 _bAcceptPartialCol 2243 ); 2244 } 2245 2246 //-------------------------------------------------------------------- 2247 bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow ) 2248 { 2249 DBG_CHECK_ME(); 2250 2251 // TODO: give veto listeners a chance 2252 2253 if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount ) 2254 || ( _nRow < 0 ) || ( _nRow >= m_nRowCount ) 2255 ) 2256 { 2257 OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" ); 2258 return false; 2259 } 2260 2261 SuppressCursor aHideCursor( *this ); 2262 m_nCurColumn = _nColumn; 2263 m_nCurRow = _nRow; 2264 2265 // ensure that the new cell is visible 2266 ensureVisible( m_nCurColumn, m_nCurRow, false ); 2267 return true; 2268 } 2269 2270 //-------------------------------------------------------------------- 2271 void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility ) 2272 { 2273 DBG_CHECK_ME(); 2274 DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount ) 2275 && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ), 2276 "TableControl_Impl::ensureVisible: invalid coordinates!" ); 2277 2278 SuppressCursor aHideCursor( *this ); 2279 2280 if ( _nColumn < m_nLeftColumn ) 2281 impl_scrollColumns( _nColumn - m_nLeftColumn ); 2282 else 2283 { 2284 TableSize nVisibleColumns = impl_getVisibleColumns( _bAcceptPartialVisibility ); 2285 if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 ) 2286 { 2287 impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) ); 2288 // TODO: since not all columns have the same width, this might in theory result 2289 // in the column still not being visible. 2290 } 2291 } 2292 2293 if ( _nRow < m_nTopRow ) 2294 impl_scrollRows( _nRow - m_nTopRow ); 2295 else 2296 { 2297 TableSize nVisibleRows = impl_getVisibleRows( _bAcceptPartialVisibility ); 2298 if ( _nRow > m_nTopRow + nVisibleRows - 1 ) 2299 impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) ); 2300 } 2301 } 2302 2303 //-------------------------------------------------------------------- 2304 ::rtl::OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col ) 2305 { 2306 Any aCellValue; 2307 m_pModel->getCellContent( i_col, i_row, aCellValue ); 2308 2309 ::rtl::OUString sCellStringContent; 2310 m_pModel->getRenderer()->GetFormattedCellString( aCellValue, i_col, i_row, sCellStringContent ); 2311 2312 return sCellStringContent; 2313 } 2314 2315 //-------------------------------------------------------------------- 2316 TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta ) 2317 { 2318 // compute new top row 2319 RowPos nNewTopRow = 2320 ::std::max( 2321 ::std::min( (RowPos)( m_nTopRow + _nRowDelta ), (RowPos)( m_nRowCount - 1 ) ), 2322 (RowPos)0 2323 ); 2324 2325 RowPos nOldTopRow = m_nTopRow; 2326 m_nTopRow = nNewTopRow; 2327 2328 // if updates are enabled currently, scroll the viewport 2329 if ( m_nTopRow != nOldTopRow ) 2330 { 2331 DBG_SUSPEND_INV( INV_SCROLL_POSITION ); 2332 SuppressCursor aHideCursor( *this ); 2333 // TODO: call a onStartScroll at our listener (or better an own onStartScroll, 2334 // which hides the cursor and then calls the listener) 2335 // Same for onEndScroll 2336 2337 // scroll the view port, if possible 2338 long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow ); 2339 2340 Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() ); 2341 2342 if ( m_pDataWindow->GetBackground().IsScrollable() 2343 && abs( nPixelDelta ) < aDataArea.GetHeight() 2344 ) 2345 { 2346 m_pDataWindow->Scroll( 0, (long)-nPixelDelta, aDataArea, SCROLL_CLIP | SCROLL_UPDATE | SCROLL_CHILDREN); 2347 } 2348 else 2349 m_pDataWindow->Invalidate( INVALIDATE_UPDATE ); 2350 2351 // update the position at the vertical scrollbar 2352 if ( m_pVScroll != NULL ) 2353 m_pVScroll->SetThumbPos( m_nTopRow ); 2354 } 2355 2356 // The scroll bar availaility might change when we scrolled. 2357 // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10. 2358 // Now let 2359 // - the user scroll to row number 6, so the last 5 rows are visible 2360 // - somebody remove the last 4 rows 2361 // - the user scroll to row number 5 being the top row, so the last two rows are visible 2362 // - somebody remove row number 6 2363 // - the user scroll to row number 1 2364 // => in this case, the need for the scrollbar vanishes immediately. 2365 if ( m_nTopRow == 0 ) 2366 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); 2367 2368 return (TableSize)( m_nTopRow - nOldTopRow ); 2369 } 2370 2371 //-------------------------------------------------------------------- 2372 TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta ) 2373 { 2374 DBG_CHECK_ME(); 2375 return impl_ni_ScrollRows( i_rowDelta ); 2376 } 2377 2378 //-------------------------------------------------------------------- 2379 TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta ) 2380 { 2381 // compute new left column 2382 const ColPos nNewLeftColumn = 2383 ::std::max( 2384 ::std::min( (ColPos)( m_nLeftColumn + _nColumnDelta ), (ColPos)( m_nColumnCount - 1 ) ), 2385 (ColPos)0 2386 ); 2387 2388 const ColPos nOldLeftColumn = m_nLeftColumn; 2389 m_nLeftColumn = nNewLeftColumn; 2390 2391 // if updates are enabled currently, scroll the viewport 2392 if ( m_nLeftColumn != nOldLeftColumn ) 2393 { 2394 DBG_SUSPEND_INV( INV_SCROLL_POSITION ); 2395 SuppressCursor aHideCursor( *this ); 2396 // TODO: call a onStartScroll at our listener (or better an own onStartScroll, 2397 // which hides the cursor and then calls the listener) 2398 // Same for onEndScroll 2399 2400 // scroll the view port, if possible 2401 const Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() ); 2402 2403 long nPixelDelta = 2404 m_aColumnWidths[ nOldLeftColumn ].getStart() 2405 - m_aColumnWidths[ m_nLeftColumn ].getStart(); 2406 2407 // update our column positions 2408 // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct 2409 // information in m_aColumnWidths 2410 for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin(); 2411 colPos != m_aColumnWidths.end(); 2412 ++colPos 2413 ) 2414 { 2415 colPos->move( nPixelDelta ); 2416 } 2417 2418 // scroll the window content (if supported and possible), or invalidate the complete window 2419 if ( m_pDataWindow->GetBackground().IsScrollable() 2420 && abs( nPixelDelta ) < aDataArea.GetWidth() 2421 ) 2422 { 2423 m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, SCROLL_CLIP | SCROLL_UPDATE ); 2424 } 2425 else 2426 m_pDataWindow->Invalidate( INVALIDATE_UPDATE ); 2427 2428 // update the position at the horizontal scrollbar 2429 if ( m_pHScroll != NULL ) 2430 m_pHScroll->SetThumbPos( m_nLeftColumn ); 2431 } 2432 2433 // The scroll bar availaility might change when we scrolled. This is because we do not hide 2434 // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will 2435 // be auto-hidden when it's scrolled back to pos 0. 2436 if ( m_nLeftColumn == 0 ) 2437 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); 2438 2439 return (TableSize)( m_nLeftColumn - nOldLeftColumn ); 2440 } 2441 2442 //------------------------------------------------------------------------------------------------------------------ 2443 TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta ) 2444 { 2445 DBG_CHECK_ME(); 2446 return impl_ni_ScrollColumns( i_columnDelta ); 2447 } 2448 2449 //------------------------------------------------------------------------------------------------------------------ 2450 SelectionEngine* TableControl_Impl::getSelEngine() 2451 { 2452 return m_pSelEngine; 2453 } 2454 2455 //------------------------------------------------------------------------------------------------------------------ 2456 ScrollBar* TableControl_Impl::getHorzScrollbar() 2457 { 2458 return m_pHScroll; 2459 } 2460 2461 //------------------------------------------------------------------------------------------------------------------ 2462 ScrollBar* TableControl_Impl::getVertScrollbar() 2463 { 2464 return m_pVScroll; 2465 } 2466 2467 //------------------------------------------------------------------------------------------------------------------ 2468 bool TableControl_Impl::isRowSelected( RowPos i_row ) const 2469 { 2470 return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end(); 2471 } 2472 2473 //------------------------------------------------------------------------------------------------------------------ 2474 RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const 2475 { 2476 if ( i_selectionIndex < m_aSelectedRows.size() ) 2477 return m_aSelectedRows[ i_selectionIndex ]; 2478 return ROW_INVALID; 2479 } 2480 2481 //------------------------------------------------------------------------------------------------------------------ 2482 int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current) 2483 { 2484 std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current); 2485 if ( it != selectedRows.end() ) 2486 { 2487 return it - selectedRows.begin(); 2488 } 2489 return -1; 2490 } 2491 2492 //-------------------------------------------------------------------- 2493 ColPos TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate ) const 2494 { 2495 DBG_CHECK_ME(); 2496 2497 if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) ) 2498 return COL_INVALID; 2499 2500 if ( i_ordinate < m_nRowHeaderWidthPixel ) 2501 return COL_ROW_HEADERS; 2502 2503 ColumnPositions::const_iterator lowerBound = ::std::lower_bound( 2504 m_aColumnWidths.begin(), 2505 m_aColumnWidths.end(), 2506 i_ordinate + 1, 2507 ColumnInfoPositionLess() 2508 ); 2509 if ( lowerBound == m_aColumnWidths.end() ) 2510 { 2511 // point is *behind* the start of the last column ... 2512 if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() ) 2513 // ... but still before its end 2514 return m_nColumnCount - 1; 2515 return COL_INVALID; 2516 } 2517 return lowerBound - m_aColumnWidths.begin(); 2518 } 2519 2520 //-------------------------------------------------------------------- 2521 RowPos TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa ) const 2522 { 2523 DBG_CHECK_ME(); 2524 2525 if ( i_abscissa < 0 ) 2526 return ROW_INVALID; 2527 2528 if ( i_abscissa < m_nColHeaderHeightPixel ) 2529 return ROW_COL_HEADERS; 2530 2531 long const abscissa = i_abscissa - m_nColHeaderHeightPixel; 2532 long const row = m_nTopRow + abscissa / m_nRowHeightPixel; 2533 return row < m_pModel->getRowCount() ? row : ROW_INVALID; 2534 } 2535 2536 //-------------------------------------------------------------------- 2537 bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex ) 2538 { 2539 DBG_CHECK_ME(); 2540 2541 ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex ); 2542 if ( selPos == m_aSelectedRows.end() ) 2543 return false; 2544 2545 m_aSelectedRows.erase( selPos ); 2546 return true; 2547 } 2548 2549 //-------------------------------------------------------------------- 2550 bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex ) 2551 { 2552 DBG_CHECK_ME(); 2553 2554 if ( isRowSelected( i_rowIndex ) ) 2555 return false; 2556 2557 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); 2558 switch ( eSelMode ) 2559 { 2560 case SINGLE_SELECTION: 2561 if ( !m_aSelectedRows.empty() ) 2562 { 2563 OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" ); 2564 m_aSelectedRows[0] = i_rowIndex; 2565 break; 2566 } 2567 // fall through 2568 2569 case MULTIPLE_SELECTION: 2570 m_aSelectedRows.push_back( i_rowIndex ); 2571 break; 2572 2573 default: 2574 OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" ); 2575 return false; 2576 } 2577 2578 return true; 2579 } 2580 2581 //-------------------------------------------------------------------- 2582 bool TableControl_Impl::markAllRowsAsDeselected() 2583 { 2584 if ( m_aSelectedRows.empty() ) 2585 return false; 2586 2587 m_aSelectedRows.clear(); 2588 return true; 2589 } 2590 2591 //-------------------------------------------------------------------- 2592 bool TableControl_Impl::markAllRowsAsSelected() 2593 { 2594 DBG_CHECK_ME(); 2595 2596 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); 2597 ENSURE_OR_RETURN_FALSE( eSelMode == MULTIPLE_SELECTION, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" ); 2598 2599 if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) ) 2600 { 2601 #if OSL_DEBUG_LEVEL > 0 2602 for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row ) 2603 { 2604 OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" ); 2605 } 2606 #endif 2607 // already all rows marked as selected 2608 return false; 2609 } 2610 2611 m_aSelectedRows.clear(); 2612 for ( RowPos i=0; i < m_pModel->getRowCount(); ++i ) 2613 m_aSelectedRows.push_back(i); 2614 2615 return true; 2616 } 2617 2618 //-------------------------------------------------------------------- 2619 void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) 2620 { 2621 impl_commitAccessibleEvent( i_eventID, i_newValue, i_oldValue ); 2622 } 2623 2624 //-------------------------------------------------------------------- 2625 void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) 2626 { 2627 DBG_CHECK_ME(); 2628 if ( impl_isAccessibleAlive() ) 2629 m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue ); 2630 } 2631 2632 //-------------------------------------------------------------------- 2633 void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) 2634 { 2635 DBG_CHECK_ME(); 2636 if ( impl_isAccessibleAlive() ) 2637 m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue ); 2638 } 2639 2640 //-------------------------------------------------------------------- 2641 Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader) 2642 { 2643 Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() ); 2644 Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() ); 2645 if ( bColHeader ) 2646 return Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) ); 2647 else 2648 return Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) ); 2649 } 2650 2651 //-------------------------------------------------------------------- 2652 Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos ) 2653 { 2654 Rectangle const aHeaderRect = calcHeaderRect( bColHeader ); 2655 TableCellGeometry const aGeometry( 2656 *this, aHeaderRect, 2657 bColHeader ? nPos : COL_ROW_HEADERS, 2658 bColHeader ? ROW_COL_HEADERS : nPos 2659 ); 2660 return aGeometry.getRect(); 2661 } 2662 2663 //-------------------------------------------------------------------- 2664 Rectangle TableControl_Impl::calcTableRect() 2665 { 2666 return impl_getAllVisibleDataCellArea(); 2667 } 2668 2669 //-------------------------------------------------------------------- 2670 Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol ) 2671 { 2672 Rectangle aCellRect; 2673 impl_getCellRect( nRow, nCol, aCellRect ); 2674 return aCellRect; 2675 } 2676 2677 //-------------------------------------------------------------------- 2678 IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ ) 2679 { 2680 DBG_CHECK_ME(); 2681 // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of 2682 // doing a complete re-layout? 2683 impl_ni_relayout(); 2684 return 1L; 2685 } 2686 2687 //-------------------------------------------------------------------- 2688 IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar ) 2689 { 2690 DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ), 2691 "TableControl_Impl::OnScroll: where did this come from?" ); 2692 2693 if ( _pScrollbar == m_pVScroll ) 2694 impl_ni_ScrollRows( _pScrollbar->GetDelta() ); 2695 else 2696 impl_ni_ScrollColumns( _pScrollbar->GetDelta() ); 2697 2698 return 0L; 2699 } 2700 2701 //------------------------------------------------------------------------------------------------------------------ 2702 Reference< XAccessible > TableControl_Impl::getAccessible( Window& i_parentWindow ) 2703 { 2704 DBG_TESTSOLARMUTEX(); 2705 if ( m_pAccessibleTable == NULL ) 2706 { 2707 Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible(); 2708 if ( xAccParent.is() ) 2709 { 2710 m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl( 2711 xAccParent, m_rAntiImpl 2712 ); 2713 } 2714 } 2715 2716 Reference< XAccessible > xAccessible; 2717 if ( m_pAccessibleTable ) 2718 xAccessible = m_pAccessibleTable->getMyself(); 2719 return xAccessible; 2720 } 2721 2722 //------------------------------------------------------------------------------------------------------------------ 2723 void TableControl_Impl::disposeAccessible() 2724 { 2725 if ( m_pAccessibleTable ) 2726 m_pAccessibleTable->dispose(); 2727 m_pAccessibleTable = NULL; 2728 } 2729 2730 //------------------------------------------------------------------------------------------------------------------ 2731 bool TableControl_Impl::impl_isAccessibleAlive() const 2732 { 2733 DBG_CHECK_ME(); 2734 return ( NULL != m_pAccessibleTable ) && m_pAccessibleTable->isAlive(); 2735 } 2736 2737 //------------------------------------------------------------------------------------------------------------------ 2738 void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue, Any const & i_oldValue ) 2739 { 2740 DBG_CHECK_ME(); 2741 if ( impl_isAccessibleAlive() ) 2742 m_pAccessibleTable->commitEvent( i_eventID, i_newValue, i_oldValue ); 2743 } 2744 2745 //================================================================================================================== 2746 //= TableFunctionSet 2747 //================================================================================================================== 2748 //------------------------------------------------------------------------------------------------------------------ 2749 TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl) 2750 :m_pTableControl( _pTableControl) 2751 ,m_nCurrentRow( ROW_INVALID ) 2752 { 2753 } 2754 //------------------------------------------------------------------------------------------------------------------ 2755 TableFunctionSet::~TableFunctionSet() 2756 { 2757 } 2758 //------------------------------------------------------------------------------------------------------------------ 2759 void TableFunctionSet::BeginDrag() 2760 { 2761 } 2762 //------------------------------------------------------------------------------------------------------------------ 2763 void TableFunctionSet::CreateAnchor() 2764 { 2765 m_pTableControl->setAnchor( m_pTableControl->getCurRow() ); 2766 } 2767 2768 //------------------------------------------------------------------------------------------------------------------ 2769 void TableFunctionSet::DestroyAnchor() 2770 { 2771 m_pTableControl->setAnchor( ROW_INVALID ); 2772 } 2773 2774 //------------------------------------------------------------------------------------------------------------------ 2775 sal_Bool TableFunctionSet::SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor) 2776 { 2777 sal_Bool bHandled = sal_False; 2778 // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click 2779 RowPos newRow = m_pTableControl->getRowAtPoint( rPoint ); 2780 if ( newRow == ROW_COL_HEADERS ) 2781 newRow = m_pTableControl->getTopRow(); 2782 2783 ColPos newCol = m_pTableControl->getColAtPoint( rPoint ); 2784 if ( newCol == COL_ROW_HEADERS ) 2785 newCol = m_pTableControl->getLeftColumn(); 2786 2787 if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) ) 2788 return sal_False; 2789 2790 if ( bDontSelectAtCursor ) 2791 { 2792 if ( m_pTableControl->getSelectedRowCount() > 1 ) 2793 m_pTableControl->getSelEngine()->AddAlways(sal_True); 2794 bHandled = sal_True; 2795 } 2796 else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() ) 2797 { 2798 //selecting region, 2799 int diff = m_pTableControl->getCurRow() - newRow; 2800 //selected region lies above the last selection 2801 if( diff >= 0) 2802 { 2803 //put selected rows in vector 2804 while ( m_pTableControl->getAnchor() >= newRow ) 2805 { 2806 m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); 2807 m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); 2808 diff--; 2809 } 2810 m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); 2811 } 2812 //selected region lies beneath the last selected row 2813 else 2814 { 2815 while ( m_pTableControl->getAnchor() <= newRow ) 2816 { 2817 m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); 2818 m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); 2819 diff++; 2820 } 2821 m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); 2822 } 2823 m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow ); 2824 bHandled = sal_True; 2825 } 2826 //no region selected 2827 else 2828 { 2829 if ( !m_pTableControl->hasRowSelection() ) 2830 m_pTableControl->markRowAsSelected( newRow ); 2831 else 2832 { 2833 if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION ) 2834 { 2835 DeselectAll(); 2836 m_pTableControl->markRowAsSelected( newRow ); 2837 } 2838 else 2839 { 2840 m_pTableControl->markRowAsSelected( newRow ); 2841 } 2842 } 2843 if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION ) 2844 m_pTableControl->getSelEngine()->AddAlways(sal_True); 2845 2846 m_pTableControl->invalidateRow( newRow ); 2847 bHandled = sal_True; 2848 } 2849 m_pTableControl->goTo( newCol, newRow ); 2850 return bHandled; 2851 } 2852 //------------------------------------------------------------------------------------------------------------------ 2853 sal_Bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint ) 2854 { 2855 m_pTableControl->getSelEngine()->AddAlways(sal_False); 2856 if ( !m_pTableControl->hasRowSelection() ) 2857 return sal_False; 2858 else 2859 { 2860 RowPos curRow = m_pTableControl->getRowAtPoint( rPoint ); 2861 m_pTableControl->setAnchor( ROW_INVALID ); 2862 bool selected = m_pTableControl->isRowSelected( curRow ); 2863 m_nCurrentRow = curRow; 2864 return selected; 2865 } 2866 } 2867 //------------------------------------------------------------------------------------------------------------------ 2868 void TableFunctionSet::DeselectAtPoint( const Point& rPoint ) 2869 { 2870 (void)rPoint; 2871 m_pTableControl->invalidateRow( m_nCurrentRow ); 2872 m_pTableControl->markRowAsDeselected( m_nCurrentRow ); 2873 } 2874 2875 //------------------------------------------------------------------------------------------------------------------ 2876 void TableFunctionSet::DeselectAll() 2877 { 2878 if ( m_pTableControl->hasRowSelection() ) 2879 { 2880 for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i ) 2881 { 2882 RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i); 2883 m_pTableControl->invalidateRow( rowIndex ); 2884 } 2885 2886 m_pTableControl->markAllRowsAsDeselected(); 2887 } 2888 } 2889 2890 //...................................................................................................................... 2891 } } // namespace svt::table 2892 //...................................................................................................................... 2893