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 m_nCurRow = ROW_INVALID; 610 } 611 612 // relayout, since the scrollbar need might have changed 613 impl_ni_relayout(); 614 615 // notify A11Y events 616 if ( impl_isAccessibleAlive() ) 617 { 618 commitTableEvent( 619 AccessibleEventId::TABLE_MODEL_CHANGED, 620 makeAny( AccessibleTableModelChange( 621 AccessibleTableModelChangeType::DELETE, 622 firstRemovedRow, 623 lastRemovedRow, 624 0, 625 m_pModel->getColumnCount() 626 ) ), 627 Any() 628 ); 629 } 630 631 // schedule a repaint 632 invalidateRowRange( firstRemovedRow, ROW_INVALID ); 633 634 // call selection handlers, if necessary 635 if ( selectionChanged ) 636 m_rAntiImpl.Select(); 637 } 638 639 //------------------------------------------------------------------------------------------------------------------ 640 void TableControl_Impl::columnInserted( ColPos const i_colIndex ) 641 { 642 m_nColumnCount = m_pModel->getColumnCount(); 643 impl_ni_relayout(); 644 645 m_rAntiImpl.Invalidate(); 646 647 OSL_UNUSED( i_colIndex ); 648 } 649 650 //------------------------------------------------------------------------------------------------------------------ 651 void TableControl_Impl::columnRemoved( ColPos const i_colIndex ) 652 { 653 m_nColumnCount = m_pModel->getColumnCount(); 654 655 // adjust the current column, if it is larger than the column count now 656 if ( m_nCurColumn >= m_nColumnCount ) 657 { 658 if ( m_nColumnCount > 0 ) 659 goTo( m_nCurColumn - 1, m_nCurRow ); 660 else 661 m_nCurColumn = COL_INVALID; 662 } 663 664 impl_ni_relayout(); 665 666 m_rAntiImpl.Invalidate(); 667 668 OSL_UNUSED( i_colIndex ); 669 } 670 671 //------------------------------------------------------------------------------------------------------------------ 672 void TableControl_Impl::allColumnsRemoved() 673 { 674 m_nColumnCount = m_pModel->getColumnCount(); 675 impl_ni_relayout(); 676 677 m_rAntiImpl.Invalidate(); 678 } 679 680 //------------------------------------------------------------------------------------------------------------------ 681 void TableControl_Impl::cellsUpdated( ColPos const i_firstCol, ColPos i_lastCol, RowPos const i_firstRow, RowPos const i_lastRow ) 682 { 683 invalidateRowRange( i_firstRow, i_lastRow ); 684 685 OSL_UNUSED( i_firstCol ); 686 OSL_UNUSED( i_lastCol ); 687 } 688 689 //------------------------------------------------------------------------------------------------------------------ 690 void TableControl_Impl::tableMetricsChanged() 691 { 692 impl_ni_updateCachedTableMetrics(); 693 impl_ni_relayout(); 694 m_rAntiImpl.Invalidate(); 695 } 696 697 //------------------------------------------------------------------------------------------------------------------ 698 void TableControl_Impl::impl_invalidateColumn( ColPos const i_column ) 699 { 700 DBG_CHECK_ME(); 701 702 Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() ); 703 704 const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column ); 705 if ( aColumn.isValid() ) 706 m_rAntiImpl.Invalidate( aColumn.getRect() ); 707 } 708 709 //------------------------------------------------------------------------------------------------------------------ 710 void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup ) 711 { 712 ColumnAttributeGroup nGroup( i_attributeGroup ); 713 if ( nGroup & COL_ATTRS_APPEARANCE ) 714 { 715 impl_invalidateColumn( i_column ); 716 nGroup &= ~COL_ATTRS_APPEARANCE; 717 } 718 719 if ( nGroup & COL_ATTRS_WIDTH ) 720 { 721 if ( !m_bUpdatingColWidths ) 722 { 723 impl_ni_relayout( i_column ); 724 invalidate( TableAreaAll ); 725 } 726 727 nGroup &= ~COL_ATTRS_WIDTH; 728 } 729 730 OSL_ENSURE( ( nGroup == COL_ATTRS_NONE ) || ( i_attributeGroup == COL_ATTRS_ALL ), 731 "TableControl_Impl::columnChanged: don't know how to handle this change!" ); 732 } 733 734 //------------------------------------------------------------------------------------------------------------------ 735 Rectangle TableControl_Impl::impl_getAllVisibleCellsArea() const 736 { 737 DBG_CHECK_ME(); 738 739 Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) ); 740 741 // determine the right-most border of the last column which is 742 // at least partially visible 743 aArea.Right() = m_nRowHeaderWidthPixel; 744 if ( !m_aColumnWidths.empty() ) 745 { 746 // the number of pixels which are scrolled out of the left hand 747 // side of the window 748 const long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd(); 749 750 ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin(); 751 do 752 { 753 aArea.Right() = loop->getEnd() - nScrolledOutLeft + m_nRowHeaderWidthPixel; 754 ++loop; 755 } 756 while ( ( loop != m_aColumnWidths.rend() ) 757 && ( loop->getEnd() - nScrolledOutLeft >= aArea.Right() ) 758 ); 759 } 760 // so far, aArea.Right() denotes the first pixel *after* the cell area 761 --aArea.Right(); 762 763 // determine the last row which is at least partially visible 764 aArea.Bottom() = 765 m_nColHeaderHeightPixel 766 + impl_getVisibleRows( true ) * m_nRowHeightPixel 767 - 1; 768 769 return aArea; 770 } 771 772 //------------------------------------------------------------------------------------------------------------------ 773 Rectangle TableControl_Impl::impl_getAllVisibleDataCellArea() const 774 { 775 DBG_CHECK_ME(); 776 777 Rectangle aArea( impl_getAllVisibleCellsArea() ); 778 aArea.Left() = m_nRowHeaderWidthPixel; 779 aArea.Top() = m_nColHeaderHeightPixel; 780 return aArea; 781 } 782 783 //------------------------------------------------------------------------------------------------------------------ 784 void TableControl_Impl::impl_ni_updateCachedTableMetrics() 785 { 786 m_nRowHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getRowHeight() ), MAP_APPFONT ).Height(); 787 788 m_nColHeaderHeightPixel = 0; 789 if ( m_pModel->hasColumnHeaders() ) 790 m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getColumnHeaderHeight() ), MAP_APPFONT ).Height(); 791 792 m_nRowHeaderWidthPixel = 0; 793 if ( m_pModel->hasRowHeaders() ) 794 m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel( Size( m_pModel->getRowHeaderWidth(), 0 ), MAP_APPFONT).Width(); 795 } 796 797 //------------------------------------------------------------------------------------------------------------------ 798 void TableControl_Impl::impl_ni_updateCachedModelValues() 799 { 800 m_pInputHandler = m_pModel->getInputHandler(); 801 if ( !m_pInputHandler ) 802 m_pInputHandler.reset( new DefaultInputHandler ); 803 804 m_nColumnCount = m_pModel->getColumnCount(); 805 if ( m_nLeftColumn >= m_nColumnCount ) 806 m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0; 807 808 m_nRowCount = m_pModel->getRowCount(); 809 if ( m_nTopRow >= m_nRowCount ) 810 m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0; 811 812 impl_ni_updateCachedTableMetrics(); 813 } 814 815 //------------------------------------------------------------------------------------------------------------------ 816 namespace 817 { 818 //.............................................................................................................. 819 /// determines whether a scrollbar is needed for the given values 820 bool lcl_determineScrollbarNeed( long const i_position, ScrollbarVisibility const i_visibility, 821 long const i_availableSpace, long const i_neededSpace ) 822 { 823 if ( i_visibility == ScrollbarShowNever ) 824 return false; 825 if ( i_visibility == ScrollbarShowAlways ) 826 return true; 827 if ( i_position > 0 ) 828 return true; 829 if ( i_availableSpace >= i_neededSpace ) 830 return false; 831 return true; 832 } 833 834 //.............................................................................................................. 835 void lcl_setButtonRepeat( Window& _rWindow, sal_uLong _nDelay ) 836 { 837 AllSettings aSettings = _rWindow.GetSettings(); 838 MouseSettings aMouseSettings = aSettings.GetMouseSettings(); 839 840 aMouseSettings.SetButtonRepeat( _nDelay ); 841 aSettings.SetMouseSettings( aMouseSettings ); 842 843 _rWindow.SetSettings( aSettings, sal_True ); 844 } 845 846 //.............................................................................................................. 847 bool lcl_updateScrollbar( Window& _rParent, ScrollBar*& _rpBar, 848 bool const i_needBar, long _nVisibleUnits, 849 long _nPosition, long _nLineSize, long _nRange, 850 bool _bHorizontal, const Link& _rScrollHandler ) 851 { 852 // do we currently have the scrollbar? 853 bool bHaveBar = _rpBar != NULL; 854 855 // do we need to correct the scrollbar visibility? 856 if ( bHaveBar && !i_needBar ) 857 { 858 if ( _rpBar->IsTracking() ) 859 _rpBar->EndTracking(); 860 DELETEZ( _rpBar ); 861 } 862 else if ( !bHaveBar && i_needBar ) 863 { 864 _rpBar = new ScrollBar( 865 &_rParent, 866 WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL ) 867 ); 868 _rpBar->SetScrollHdl( _rScrollHandler ); 869 // get some speed into the scrolling .... 870 lcl_setButtonRepeat( *_rpBar, 0 ); 871 } 872 873 if ( _rpBar ) 874 { 875 _rpBar->SetRange( Range( 0, _nRange ) ); 876 _rpBar->SetVisibleSize( _nVisibleUnits ); 877 _rpBar->SetPageSize( _nVisibleUnits ); 878 _rpBar->SetLineSize( _nLineSize ); 879 _rpBar->SetThumbPos( _nPosition ); 880 _rpBar->Show(); 881 } 882 883 return ( bHaveBar != i_needBar ); 884 } 885 886 //.............................................................................................................. 887 /** returns the number of rows fitting into the given range, 888 for the given row height. Partially fitting rows are counted, too, if the 889 respective parameter says so. 890 */ 891 TableSize lcl_getRowsFittingInto( long _nOverallHeight, long _nRowHeightPixel, bool _bAcceptPartialRow = false ) 892 { 893 return _bAcceptPartialRow 894 ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel 895 : _nOverallHeight / _nRowHeightPixel; 896 } 897 898 //.............................................................................................................. 899 /** returns the number of columns fitting into the given area, 900 with the first visible column as given. Partially fitting columns are counted, too, 901 if the respective parameter says so. 902 */ 903 TableSize lcl_getColumnsVisibleWithin( const Rectangle& _rArea, ColPos _nFirstVisibleColumn, 904 const TableControl_Impl& _rControl, bool _bAcceptPartialRow ) 905 { 906 TableSize visibleColumns = 0; 907 TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn ); 908 while ( aColumn.isValid() ) 909 { 910 if ( !_bAcceptPartialRow ) 911 if ( aColumn.getRect().Right() > _rArea.Right() ) 912 // this column is only partially visible, and this is not allowed 913 break; 914 915 aColumn.moveRight(); 916 ++visibleColumns; 917 } 918 return visibleColumns; 919 } 920 921 } 922 923 //------------------------------------------------------------------------------------------------------------------ 924 long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding, 925 bool const i_assumeVerticalScrollbar, ::std::vector< long >& o_newColWidthsPixel ) const 926 { 927 // the available horizontal space 928 long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width(); 929 ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel ); 930 if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) ) 931 { 932 gridWidthPixel -= m_nRowHeaderWidthPixel; 933 } 934 935 if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) ) 936 { 937 long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); 938 gridWidthPixel -= nScrollbarMetrics; 939 } 940 941 // no need to do anything without columns 942 TableSize const colCount = m_pModel->getColumnCount(); 943 if ( colCount == 0 ) 944 return gridWidthPixel; 945 946 // collect some meta data for our columns: 947 // - their current (pixel) metrics 948 long accumulatedCurrentWidth = 0; 949 ::std::vector< long > currentColWidths; 950 currentColWidths.reserve( colCount ); 951 typedef ::std::vector< ::std::pair< long, long > > ColumnLimits; 952 ColumnLimits effectiveColumnLimits; 953 effectiveColumnLimits.reserve( colCount ); 954 long accumulatedMinWidth = 0; 955 long accumulatedMaxWidth = 0; 956 // - their relative flexibility 957 ::std::vector< ::sal_Int32 > columnFlexibilities; 958 columnFlexibilities.reserve( colCount ); 959 long flexibilityDenominator = 0; 960 size_t flexibleColumnCount = 0; 961 for ( ColPos col = 0; col < colCount; ++col ) 962 { 963 PColumnModel const pColumn = m_pModel->getColumnModel( col ); 964 ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); 965 966 // current width 967 long const currentWidth = appFontWidthToPixel( pColumn->getWidth() ); 968 currentColWidths.push_back( currentWidth ); 969 970 // accumulated width 971 accumulatedCurrentWidth += currentWidth; 972 973 // flexibility 974 ::sal_Int32 flexibility = pColumn->getFlexibility(); 975 OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." ); 976 if ( ( flexibility < 0 ) // normalization 977 || ( !pColumn->isResizable() ) // column not resizeable => no auto-resize 978 || ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respec this 979 ) 980 flexibility = 0; 981 982 // min/max width 983 long effectiveMin = currentWidth, effectiveMax = currentWidth; 984 // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then 985 if ( flexibility > 0 ) 986 { 987 long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() ); 988 if ( minWidth > 0 ) 989 effectiveMin = minWidth; 990 else 991 effectiveMin = MIN_COLUMN_WIDTH_PIXEL; 992 993 long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() ); 994 OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" ); 995 if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) ) 996 effectiveMax = maxWidth; 997 else 998 effectiveMax = gridWidthPixel; // TODO: any better guess here? 999 1000 if ( effectiveMin == effectiveMax ) 1001 // if the min and the max are identical, this implies no flexibility at all 1002 flexibility = 0; 1003 } 1004 1005 columnFlexibilities.push_back( flexibility ); 1006 flexibilityDenominator += flexibility; 1007 if ( flexibility > 0 ) 1008 ++flexibleColumnCount; 1009 1010 effectiveColumnLimits.push_back( ::std::pair< long, long >( effectiveMin, effectiveMax ) ); 1011 accumulatedMinWidth += effectiveMin; 1012 accumulatedMaxWidth += effectiveMax; 1013 } 1014 1015 o_newColWidthsPixel = currentColWidths; 1016 if ( flexibilityDenominator == 0 ) 1017 { 1018 // no column is flexible => don't adjust anything 1019 } 1020 else if ( gridWidthPixel > accumulatedCurrentWidth ) 1021 { // we have space to give away ... 1022 long distributePixel = gridWidthPixel - accumulatedCurrentWidth; 1023 if ( gridWidthPixel > accumulatedMaxWidth ) 1024 { 1025 // ... but the column's maximal widths are still less than we have 1026 // => set them all to max 1027 for ( size_t i = 0; i < size_t( colCount ); ++i ) 1028 { 1029 o_newColWidthsPixel[i] = effectiveColumnLimits[i].second; 1030 } 1031 } 1032 else 1033 { 1034 bool startOver = false; 1035 do 1036 { 1037 startOver = false; 1038 // distribute the remaining space amongst all columns with a positive flexibility 1039 for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i ) 1040 { 1041 long const columnFlexibility = columnFlexibilities[i]; 1042 if ( columnFlexibility == 0 ) 1043 continue; 1044 1045 long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator; 1046 1047 if ( newColWidth > effectiveColumnLimits[i].second ) 1048 { // that was too much, we hit the col's maximum 1049 // set the new width to exactly this maximum 1050 newColWidth = effectiveColumnLimits[i].second; 1051 // adjust the flexibility denominator ... 1052 flexibilityDenominator -= columnFlexibility; 1053 columnFlexibilities[i] = 0; 1054 --flexibleColumnCount; 1055 // ... and the remaining width ... 1056 long const difference = newColWidth - currentColWidths[i]; 1057 distributePixel -= difference; 1058 // ... this way, we ensure that the width not taken up by this column is consumed by the other 1059 // flexible ones (if there are some) 1060 1061 // and start over with the first column, since there might be earlier columns which need 1062 // to be recalculated now 1063 startOver = true; 1064 } 1065 1066 o_newColWidthsPixel[i] = newColWidth; 1067 } 1068 } 1069 while ( startOver ); 1070 1071 // are there pixels left (might be caused by rounding errors)? 1072 distributePixel = gridWidthPixel - ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ); 1073 while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) ) 1074 { 1075 // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible 1076 // columns which did not yet reach their maximum. 1077 for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i ) 1078 { 1079 if ( columnFlexibilities[i] == 0 ) 1080 continue; 1081 1082 OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second, 1083 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" ); 1084 if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first ) 1085 { 1086 columnFlexibilities[i] = 0; 1087 --flexibleColumnCount; 1088 continue; 1089 } 1090 1091 ++o_newColWidthsPixel[i]; 1092 --distributePixel; 1093 } 1094 } 1095 } 1096 } 1097 else if ( gridWidthPixel < accumulatedCurrentWidth ) 1098 { // we need to take away some space from the columns which allow it ... 1099 long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel; 1100 if ( gridWidthPixel < accumulatedMinWidth ) 1101 { 1102 // ... but the column's minimal widths are still more than we have 1103 // => set them all to min 1104 for ( size_t i = 0; i < size_t( colCount ); ++i ) 1105 { 1106 o_newColWidthsPixel[i] = effectiveColumnLimits[i].first; 1107 } 1108 } 1109 else 1110 { 1111 bool startOver = false; 1112 do 1113 { 1114 startOver = false; 1115 // take away the space we need from the columns with a positive flexibility 1116 for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i ) 1117 { 1118 long const columnFlexibility = columnFlexibilities[i]; 1119 if ( columnFlexibility == 0 ) 1120 continue; 1121 1122 long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator; 1123 1124 if ( newColWidth < effectiveColumnLimits[i].first ) 1125 { // that was too much, we hit the col's minimum 1126 // set the new width to exactly this minimum 1127 newColWidth = effectiveColumnLimits[i].first; 1128 // adjust the flexibility denominator ... 1129 flexibilityDenominator -= columnFlexibility; 1130 columnFlexibilities[i] = 0; 1131 --flexibleColumnCount; 1132 // ... and the remaining width ... 1133 long const difference = currentColWidths[i] - newColWidth; 1134 takeAwayPixel -= difference; 1135 1136 // and start over with the first column, since there might be earlier columns which need 1137 // to be recalculated now 1138 startOver = true; 1139 } 1140 1141 o_newColWidthsPixel[i] = newColWidth; 1142 } 1143 } 1144 while ( startOver ); 1145 1146 // are there pixels left (might be caused by rounding errors)? 1147 takeAwayPixel = ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ) - gridWidthPixel; 1148 while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) ) 1149 { 1150 // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible 1151 // columns which did not yet reach their minimum. 1152 for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i ) 1153 { 1154 if ( columnFlexibilities[i] == 0 ) 1155 continue; 1156 1157 OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first, 1158 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" ); 1159 if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first ) 1160 { 1161 columnFlexibilities[i] = 0; 1162 --flexibleColumnCount; 1163 continue; 1164 } 1165 1166 --o_newColWidthsPixel[i]; 1167 --takeAwayPixel; 1168 } 1169 } 1170 } 1171 } 1172 1173 return gridWidthPixel; 1174 } 1175 1176 //------------------------------------------------------------------------------------------------------------------ 1177 void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding ) 1178 { 1179 ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" ); 1180 1181 m_aColumnWidths.resize( 0 ); 1182 if ( !m_pModel ) 1183 return; 1184 1185 ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true ); 1186 SuppressCursor aHideCursor( *this ); 1187 1188 // layouting steps: 1189 // 1190 // 1. adjust column widths, leaving space for a vertical scrollbar 1191 // 2. determine need for a vertical scrollbar 1192 // - V-YES: all fine, result from 1. is still valid 1193 // - V-NO: result from 1. is still under consideration 1194 // 1195 // 3. determine need for a horizontal scrollbar 1196 // - H-NO: all fine, result from 2. is still valid 1197 // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO 1198 // - V-YES: all fine, result from 1. is still valid 1199 // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it 1200 1201 ::std::vector< long > newWidthsPixel; 1202 long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel ); 1203 1204 // the width/height of a scrollbar, needed several times below 1205 long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); 1206 1207 // determine the playground for the data cells (excluding headers) 1208 // TODO: what if the control is smaller than needed for the headers/scrollbars? 1209 Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() ); 1210 aDataCellPlayground.Left() = m_nRowHeaderWidthPixel; 1211 aDataCellPlayground.Top() = m_nColHeaderHeightPixel; 1212 1213 OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ), 1214 "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" ); 1215 long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 ); 1216 1217 ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility(); 1218 ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility(); 1219 1220 // do we need a vertical scrollbar? 1221 bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed( 1222 m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); 1223 bool bFirstRoundVScrollNeed = false; 1224 if ( bNeedVerticalScrollbar ) 1225 { 1226 aDataCellPlayground.Right() -= nScrollbarMetrics; 1227 bFirstRoundVScrollNeed = true; 1228 } 1229 1230 // do we need a horizontal scrollbar? 1231 bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed( 1232 m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth ); 1233 if ( bNeedHorizontalScrollbar ) 1234 { 1235 aDataCellPlayground.Bottom() -= nScrollbarMetrics; 1236 1237 // now that we just found that we need a horizontal scrollbar, 1238 // the need for a vertical one may have changed, since the horizontal 1239 // SB might just occupy enough space so that not all rows do fit 1240 // anymore 1241 if ( !bFirstRoundVScrollNeed ) 1242 { 1243 bNeedVerticalScrollbar = lcl_determineScrollbarNeed( 1244 m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount ); 1245 if ( bNeedVerticalScrollbar ) 1246 { 1247 aDataCellPlayground.Right() -= nScrollbarMetrics; 1248 } 1249 } 1250 } 1251 1252 // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now, 1253 // we know that this is not the case, re-calculate the column widths. 1254 if ( !bNeedVerticalScrollbar ) 1255 gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel ); 1256 1257 // update the column objects with the new widths we finally calculated 1258 TableSize const colCount = m_pModel->getColumnCount(); 1259 m_aColumnWidths.reserve( colCount ); 1260 long accumulatedWidthPixel = m_nRowHeaderWidthPixel; 1261 bool anyColumnWidthChanged = false; 1262 for ( ColPos col = 0; col < colCount; ++col ) 1263 { 1264 const long columnStart = accumulatedWidthPixel; 1265 const long columnEnd = columnStart + newWidthsPixel[col]; 1266 m_aColumnWidths.push_back( MutableColumnMetrics( columnStart, columnEnd ) ); 1267 accumulatedWidthPixel = columnEnd; 1268 1269 // and don't forget to forward this to the column models 1270 PColumnModel const pColumn = m_pModel->getColumnModel( col ); 1271 ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" ); 1272 1273 long const oldColumnWidthAppFont = pColumn->getWidth(); 1274 long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] ); 1275 pColumn->setWidth( newColumnWidthAppFont ); 1276 1277 anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont ); 1278 } 1279 1280 // if the column widths changed, ensure everything is repainted 1281 if ( anyColumnWidthChanged ) 1282 invalidate( TableAreaAll ); 1283 1284 // if the column resizing happened to leave some space at the right, but there are columns 1285 // scrolled out to the left, scroll them in 1286 while ( ( m_nLeftColumn > 0 ) 1287 && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel ) 1288 ) 1289 { 1290 --m_nLeftColumn; 1291 } 1292 1293 // now adjust the column metrics, since they currently ignore the horizontal scroll position 1294 if ( m_nLeftColumn > 0 ) 1295 { 1296 const long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart(); 1297 for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin(); 1298 colPos != m_aColumnWidths.end(); 1299 ++colPos 1300 ) 1301 { 1302 colPos->move( offsetPixel ); 1303 } 1304 } 1305 1306 // show or hide the scrollbars as needed, and position the data window 1307 impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar ); 1308 } 1309 1310 //------------------------------------------------------------------------------------------------------------------ 1311 void TableControl_Impl::impl_ni_positionChildWindows( Rectangle const & i_dataCellPlayground, 1312 bool const i_verticalScrollbar, bool const i_horizontalScrollbar ) 1313 { 1314 long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize(); 1315 1316 // create or destroy the vertical scrollbar, as needed 1317 lcl_updateScrollbar( 1318 m_rAntiImpl, 1319 m_pVScroll, 1320 i_verticalScrollbar, 1321 lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel ), 1322 // visible units 1323 m_nTopRow, // current position 1324 1, // line size 1325 m_nRowCount, // range 1326 false, // vertical 1327 LINK( this, TableControl_Impl, OnScroll ) // scroll handler 1328 ); 1329 1330 // position it 1331 if ( m_pVScroll ) 1332 { 1333 Rectangle aScrollbarArea( 1334 Point( i_dataCellPlayground.Right() + 1, 0 ), 1335 Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 ) 1336 ); 1337 m_pVScroll->SetPosSizePixel( 1338 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); 1339 } 1340 1341 // create or destroy the horizontal scrollbar, as needed 1342 lcl_updateScrollbar( 1343 m_rAntiImpl, 1344 m_pHScroll, 1345 i_horizontalScrollbar, 1346 lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ), 1347 // visible units 1348 m_nLeftColumn, // current position 1349 1, // line size 1350 m_nColumnCount, // range 1351 true, // horizontal 1352 LINK( this, TableControl_Impl, OnScroll ) // scroll handler 1353 ); 1354 1355 // position it 1356 if ( m_pHScroll ) 1357 { 1358 TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ); 1359 TableMetrics const nRange = m_nColumnCount; 1360 if( m_nLeftColumn + nVisibleUnits == nRange - 1 ) 1361 { 1362 if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() ) 1363 { 1364 m_pHScroll->SetVisibleSize( nVisibleUnits -1 ); 1365 m_pHScroll->SetPageSize( nVisibleUnits - 1 ); 1366 } 1367 } 1368 Rectangle aScrollbarArea( 1369 Point( 0, i_dataCellPlayground.Bottom() + 1 ), 1370 Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics ) 1371 ); 1372 m_pHScroll->SetPosSizePixel( 1373 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() ); 1374 } 1375 1376 // the corner window connecting the two scrollbars in the lower right corner 1377 bool bHaveScrollCorner = NULL != m_pScrollCorner; 1378 bool bNeedScrollCorner = ( NULL != m_pHScroll ) && ( NULL != m_pVScroll ); 1379 if ( bHaveScrollCorner && !bNeedScrollCorner ) 1380 { 1381 DELETEZ( m_pScrollCorner ); 1382 } 1383 else if ( !bHaveScrollCorner && bNeedScrollCorner ) 1384 { 1385 m_pScrollCorner = new ScrollBarBox( &m_rAntiImpl ); 1386 m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) ); 1387 m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) ); 1388 m_pScrollCorner->Show(); 1389 } 1390 else if(bHaveScrollCorner && bNeedScrollCorner) 1391 { 1392 m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) ); 1393 m_pScrollCorner->Show(); 1394 } 1395 1396 // resize the data window 1397 m_pDataWindow->SetSizePixel( Size( 1398 i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel, 1399 i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel 1400 ) ); 1401 } 1402 1403 //------------------------------------------------------------------------------------------------------------------ 1404 void TableControl_Impl::onResize() 1405 { 1406 DBG_CHECK_ME(); 1407 1408 impl_ni_relayout(); 1409 checkCursorPosition(); 1410 } 1411 1412 //------------------------------------------------------------------------------------------------------------------ 1413 void TableControl_Impl::doPaintContent( const Rectangle& _rUpdateRect ) 1414 { 1415 DBG_CHECK_ME(); 1416 1417 if ( !getModel() ) 1418 return; 1419 PTableRenderer pRenderer = getModel()->getRenderer(); 1420 DBG_ASSERT( !!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!" ); 1421 if ( !pRenderer ) 1422 return; 1423 1424 // our current style settings, to be passed to the renderer 1425 const StyleSettings& rStyle = m_rAntiImpl.GetSettings().GetStyleSettings(); 1426 m_nRowCount = m_pModel->getRowCount(); 1427 // the area occupied by all (at least partially) visible cells, including 1428 // headers 1429 Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() ); 1430 1431 // ............................ 1432 // draw the header column area 1433 if ( m_pModel->hasColumnHeaders() ) 1434 { 1435 TableRowGeometry const aHeaderRow( *this, Rectangle( Point( 0, 0 ), 1436 aAllCellsWithHeaders.BottomRight() ), ROW_COL_HEADERS ); 1437 Rectangle const aColRect(aHeaderRow.getRect()); 1438 pRenderer->PaintHeaderArea( 1439 *m_pDataWindow, aColRect, true, false, rStyle 1440 ); 1441 // Note that strictly, aHeaderRow.getRect() also contains the intersection between column 1442 // and row header area. However, below we go to paint this intersection, again, 1443 // so this hopefully doesn't hurt if we already paint it here. 1444 1445 for ( TableCellGeometry aCell( aHeaderRow, m_nLeftColumn ); 1446 aCell.isValid(); 1447 aCell.moveRight() 1448 ) 1449 { 1450 if ( _rUpdateRect.GetIntersection( aCell.getRect() ).IsEmpty() ) 1451 continue; 1452 1453 bool isActiveColumn = ( aCell.getColumn() == getCurrentColumn() ); 1454 bool isSelectedColumn = false; 1455 pRenderer->PaintColumnHeader( aCell.getColumn(), isActiveColumn, isSelectedColumn, 1456 *m_pDataWindow, aCell.getRect(), rStyle ); 1457 } 1458 } 1459 // the area occupied by the row header, if any 1460 Rectangle aRowHeaderArea; 1461 if ( m_pModel->hasRowHeaders() ) 1462 { 1463 aRowHeaderArea = aAllCellsWithHeaders; 1464 aRowHeaderArea.Right() = m_nRowHeaderWidthPixel - 1; 1465 1466 TableSize const nVisibleRows = impl_getVisibleRows( true ); 1467 TableSize nActualRows = nVisibleRows; 1468 if ( m_nTopRow + nActualRows > m_nRowCount ) 1469 nActualRows = m_nRowCount - m_nTopRow; 1470 aRowHeaderArea.Bottom() = m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1; 1471 1472 pRenderer->PaintHeaderArea( *m_pDataWindow, aRowHeaderArea, false, true, rStyle ); 1473 // Note that strictly, aRowHeaderArea also contains the intersection between column 1474 // and row header area. However, below we go to paint this intersection, again, 1475 // so this hopefully doesn't hurt if we already paint it here. 1476 1477 if ( m_pModel->hasColumnHeaders() ) 1478 { 1479 TableCellGeometry const aIntersection( *this, Rectangle( Point( 0, 0 ), 1480 aAllCellsWithHeaders.BottomRight() ), COL_ROW_HEADERS, ROW_COL_HEADERS ); 1481 Rectangle const aInters( aIntersection.getRect() ); 1482 pRenderer->PaintHeaderArea( 1483 *m_pDataWindow, aInters, true, true, rStyle 1484 ); 1485 } 1486 } 1487 1488 // ............................ 1489 // draw the table content row by row 1490 1491 TableSize colCount = getModel()->getColumnCount(); 1492 1493 // paint all rows 1494 Rectangle const aAllDataCellsArea( impl_getAllVisibleDataCellArea() ); 1495 for ( TableRowGeometry aRowIterator( *this, aAllCellsWithHeaders, getTopRow() ); 1496 aRowIterator.isValid(); 1497 aRowIterator.moveDown() ) 1498 { 1499 if ( _rUpdateRect.GetIntersection( aRowIterator.getRect() ).IsEmpty() ) 1500 continue; 1501 1502 bool const isControlFocused = m_rAntiImpl.HasControlFocus(); 1503 bool const isSelectedRow = isRowSelected( aRowIterator.getRow() ); 1504 1505 Rectangle const aRect = aRowIterator.getRect().GetIntersection( aAllDataCellsArea ); 1506 1507 // give the redenderer a chance to prepare the row 1508 pRenderer->PrepareRow( 1509 aRowIterator.getRow(), isControlFocused, isSelectedRow, 1510 *m_pDataWindow, aRect, rStyle 1511 ); 1512 1513 // paint the row header 1514 if ( m_pModel->hasRowHeaders() ) 1515 { 1516 const Rectangle aCurrentRowHeader( aRowHeaderArea.GetIntersection( aRowIterator.getRect() ) ); 1517 pRenderer->PaintRowHeader( isControlFocused, isSelectedRow, *m_pDataWindow, aCurrentRowHeader, 1518 rStyle ); 1519 } 1520 1521 if ( !colCount ) 1522 continue; 1523 1524 // paint all cells in this row 1525 for ( TableCellGeometry aCell( aRowIterator, m_nLeftColumn ); 1526 aCell.isValid(); 1527 aCell.moveRight() 1528 ) 1529 { 1530 bool isSelectedColumn = false; 1531 pRenderer->PaintCell( aCell.getColumn(), isSelectedRow || isSelectedColumn, isControlFocused, 1532 *m_pDataWindow, aCell.getRect(), rStyle ); 1533 } 1534 } 1535 } 1536 //------------------------------------------------------------------------------------------------------------------ 1537 void TableControl_Impl::hideCursor() 1538 { 1539 DBG_CHECK_ME(); 1540 1541 if ( ++m_nCursorHidden == 1 ) 1542 impl_ni_doSwitchCursor( false ); 1543 } 1544 1545 //------------------------------------------------------------------------------------------------------------------ 1546 void TableControl_Impl::showCursor() 1547 { 1548 DBG_CHECK_ME(); 1549 1550 DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" ); 1551 if ( --m_nCursorHidden == 0 ) 1552 impl_ni_doSwitchCursor( true ); 1553 } 1554 1555 //------------------------------------------------------------------------------------------------------------------ 1556 bool TableControl_Impl::dispatchAction( TableControlAction _eAction ) 1557 { 1558 DBG_CHECK_ME(); 1559 1560 bool bSuccess = false; 1561 bool selectionChanged = false; 1562 1563 switch ( _eAction ) 1564 { 1565 case cursorDown: 1566 if ( m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION ) 1567 { 1568 //if other rows already selected, deselect them 1569 if ( m_aSelectedRows.size()>0 ) 1570 { 1571 invalidateSelectedRows(); 1572 m_aSelectedRows.clear(); 1573 } 1574 if ( m_nCurRow < m_nRowCount-1 ) 1575 { 1576 ++m_nCurRow; 1577 m_aSelectedRows.push_back(m_nCurRow); 1578 } 1579 else 1580 m_aSelectedRows.push_back(m_nCurRow); 1581 invalidateRow( m_nCurRow ); 1582 ensureVisible(m_nCurColumn,m_nCurRow,false); 1583 selectionChanged = true; 1584 bSuccess = true; 1585 } 1586 else 1587 { 1588 if ( m_nCurRow < m_nRowCount - 1 ) 1589 bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 ); 1590 } 1591 break; 1592 1593 case cursorUp: 1594 if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1595 { 1596 if(m_aSelectedRows.size()>0) 1597 { 1598 invalidateSelectedRows(); 1599 m_aSelectedRows.clear(); 1600 } 1601 if(m_nCurRow>0) 1602 { 1603 --m_nCurRow; 1604 m_aSelectedRows.push_back(m_nCurRow); 1605 invalidateRow( m_nCurRow ); 1606 } 1607 else 1608 { 1609 m_aSelectedRows.push_back(m_nCurRow); 1610 invalidateRow( m_nCurRow ); 1611 } 1612 ensureVisible(m_nCurColumn,m_nCurRow,false); 1613 selectionChanged = true; 1614 bSuccess = true; 1615 } 1616 else 1617 { 1618 if ( m_nCurRow > 0 ) 1619 bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 ); 1620 } 1621 break; 1622 case cursorLeft: 1623 if ( m_nCurColumn > 0 ) 1624 bSuccess = goTo( m_nCurColumn - 1, m_nCurRow ); 1625 else 1626 if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) ) 1627 bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 ); 1628 break; 1629 1630 case cursorRight: 1631 if ( m_nCurColumn < m_nColumnCount - 1 ) 1632 bSuccess = goTo( m_nCurColumn + 1, m_nCurRow ); 1633 else 1634 if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) ) 1635 bSuccess = goTo( 0, m_nCurRow + 1 ); 1636 break; 1637 1638 case cursorToLineStart: 1639 bSuccess = goTo( 0, m_nCurRow ); 1640 break; 1641 1642 case cursorToLineEnd: 1643 bSuccess = goTo( m_nColumnCount - 1, m_nCurRow ); 1644 break; 1645 1646 case cursorToFirstLine: 1647 bSuccess = goTo( m_nCurColumn, 0 ); 1648 break; 1649 1650 case cursorToLastLine: 1651 bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 ); 1652 break; 1653 1654 case cursorPageUp: 1655 { 1656 RowPos nNewRow = ::std::max( (RowPos)0, m_nCurRow - impl_getVisibleRows( false ) ); 1657 bSuccess = goTo( m_nCurColumn, nNewRow ); 1658 } 1659 break; 1660 1661 case cursorPageDown: 1662 { 1663 RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) ); 1664 bSuccess = goTo( m_nCurColumn, nNewRow ); 1665 } 1666 break; 1667 1668 case cursorTopLeft: 1669 bSuccess = goTo( 0, 0 ); 1670 break; 1671 1672 case cursorBottomRight: 1673 bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 ); 1674 break; 1675 1676 case cursorSelectRow: 1677 { 1678 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1679 return bSuccess = false; 1680 //pos is the position of the current row in the vector of selected rows, if current row is selected 1681 int pos = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); 1682 //if current row is selected, it should be deselected, when ALT+SPACE are pressed 1683 if(pos>-1) 1684 { 1685 m_aSelectedRows.erase(m_aSelectedRows.begin()+pos); 1686 if(m_aSelectedRows.empty() && m_nAnchor != -1) 1687 m_nAnchor = -1; 1688 } 1689 //else select the row->put it in the vector 1690 else 1691 m_aSelectedRows.push_back(m_nCurRow); 1692 invalidateRow( m_nCurRow ); 1693 selectionChanged = true; 1694 bSuccess = true; 1695 } 1696 break; 1697 case cursorSelectRowUp: 1698 { 1699 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1700 return bSuccess = false; 1701 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1702 { 1703 //if there are other selected rows, deselect them 1704 return false; 1705 } 1706 else 1707 { 1708 //there are other selected rows 1709 if(m_aSelectedRows.size()>0) 1710 { 1711 //the anchor wasn't set -> a region is not selected, that's why clear all selection 1712 //and select the current row 1713 if(m_nAnchor==-1) 1714 { 1715 invalidateSelectedRows(); 1716 m_aSelectedRows.clear(); 1717 m_aSelectedRows.push_back(m_nCurRow); 1718 invalidateRow( m_nCurRow ); 1719 } 1720 else 1721 { 1722 //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected 1723 int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); 1724 int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow-1); 1725 if(prevRow>-1) 1726 { 1727 //if m_nCurRow isn't the upper one, can move up, otherwise not 1728 if(m_nCurRow>0) 1729 m_nCurRow--; 1730 else 1731 return bSuccess = true; 1732 //if nextRow already selected, deselect it, otherwise select it 1733 if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) 1734 { 1735 m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); 1736 invalidateRow( m_nCurRow + 1 ); 1737 } 1738 else 1739 { 1740 m_aSelectedRows.push_back(m_nCurRow); 1741 invalidateRow( m_nCurRow ); 1742 } 1743 } 1744 else 1745 { 1746 if(m_nCurRow>0) 1747 { 1748 m_aSelectedRows.push_back(m_nCurRow); 1749 m_nCurRow--; 1750 m_aSelectedRows.push_back(m_nCurRow); 1751 invalidateSelectedRegion( m_nCurRow+1, m_nCurRow ); 1752 } 1753 } 1754 } 1755 } 1756 else 1757 { 1758 //if nothing is selected and the current row isn't the upper one 1759 //select the current and one row above 1760 //otherwise select only the upper row 1761 if(m_nCurRow>0) 1762 { 1763 m_aSelectedRows.push_back(m_nCurRow); 1764 m_nCurRow--; 1765 m_aSelectedRows.push_back(m_nCurRow); 1766 invalidateSelectedRegion( m_nCurRow+1, m_nCurRow ); 1767 } 1768 else 1769 { 1770 m_aSelectedRows.push_back(m_nCurRow); 1771 invalidateRow( m_nCurRow ); 1772 } 1773 } 1774 m_pSelEngine->SetAnchor(sal_True); 1775 m_nAnchor = m_nCurRow; 1776 ensureVisible(m_nCurColumn, m_nCurRow, false); 1777 selectionChanged = true; 1778 bSuccess = true; 1779 } 1780 } 1781 break; 1782 case cursorSelectRowDown: 1783 { 1784 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1785 bSuccess = false; 1786 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1787 { 1788 bSuccess = false; 1789 } 1790 else 1791 { 1792 if(m_aSelectedRows.size()>0) 1793 { 1794 //the anchor wasn't set -> a region is not selected, that's why clear all selection 1795 //and select the current row 1796 if(m_nAnchor==-1) 1797 { 1798 invalidateSelectedRows(); 1799 m_aSelectedRows.clear(); 1800 m_aSelectedRows.push_back(m_nCurRow); 1801 invalidateRow( m_nCurRow ); 1802 } 1803 else 1804 { 1805 //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected 1806 int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow); 1807 int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow+1); 1808 if(prevRow>-1) 1809 { 1810 //if m_nCurRow isn't the last one, can move down, otherwise not 1811 if(m_nCurRow<m_nRowCount-1) 1812 m_nCurRow++; 1813 else 1814 return bSuccess = true; 1815 //if next row already selected, deselect it, otherwise select it 1816 if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow) 1817 { 1818 m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow); 1819 invalidateRow( m_nCurRow - 1 ); 1820 } 1821 else 1822 { 1823 m_aSelectedRows.push_back(m_nCurRow); 1824 invalidateRow( m_nCurRow ); 1825 } 1826 } 1827 else 1828 { 1829 if(m_nCurRow<m_nRowCount-1) 1830 { 1831 m_aSelectedRows.push_back(m_nCurRow); 1832 m_nCurRow++; 1833 m_aSelectedRows.push_back(m_nCurRow); 1834 invalidateSelectedRegion( m_nCurRow-1, m_nCurRow ); 1835 } 1836 } 1837 } 1838 } 1839 else 1840 { 1841 //there wasn't any selection, select current and row beneath, otherwise only row beneath 1842 if(m_nCurRow<m_nRowCount-1) 1843 { 1844 m_aSelectedRows.push_back(m_nCurRow); 1845 m_nCurRow++; 1846 m_aSelectedRows.push_back(m_nCurRow); 1847 invalidateSelectedRegion( m_nCurRow-1, m_nCurRow ); 1848 } 1849 else 1850 { 1851 m_aSelectedRows.push_back(m_nCurRow); 1852 invalidateRow( m_nCurRow ); 1853 } 1854 } 1855 m_pSelEngine->SetAnchor(sal_True); 1856 m_nAnchor = m_nCurRow; 1857 ensureVisible(m_nCurColumn, m_nCurRow, false); 1858 selectionChanged = true; 1859 bSuccess = true; 1860 } 1861 } 1862 break; 1863 1864 case cursorSelectRowAreaTop: 1865 { 1866 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1867 bSuccess = false; 1868 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1869 bSuccess = false; 1870 else 1871 { 1872 //select the region between the current and the upper row 1873 RowPos iter = m_nCurRow; 1874 invalidateSelectedRegion( m_nCurRow, 0 ); 1875 //put the rows in vector 1876 while(iter>=0) 1877 { 1878 if ( !isRowSelected( iter ) ) 1879 m_aSelectedRows.push_back(iter); 1880 --iter; 1881 } 1882 m_nCurRow = 0; 1883 m_nAnchor = m_nCurRow; 1884 m_pSelEngine->SetAnchor(sal_True); 1885 ensureVisible(m_nCurColumn, 0, false); 1886 selectionChanged = true; 1887 bSuccess = true; 1888 } 1889 } 1890 break; 1891 1892 case cursorSelectRowAreaBottom: 1893 { 1894 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION) 1895 return bSuccess = false; 1896 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION) 1897 return bSuccess = false; 1898 //select the region between the current and the last row 1899 RowPos iter = m_nCurRow; 1900 invalidateSelectedRegion( m_nCurRow, m_nRowCount-1 ); 1901 //put the rows in the vector 1902 while(iter<=m_nRowCount) 1903 { 1904 if ( !isRowSelected( iter ) ) 1905 m_aSelectedRows.push_back(iter); 1906 ++iter; 1907 } 1908 m_nCurRow = m_nRowCount-1; 1909 m_nAnchor = m_nCurRow; 1910 m_pSelEngine->SetAnchor(sal_True); 1911 ensureVisible(m_nCurColumn, m_nRowCount-1, false); 1912 selectionChanged = true; 1913 bSuccess = true; 1914 } 1915 break; 1916 default: 1917 DBG_ERROR( "TableControl_Impl::dispatchAction: unsupported action!" ); 1918 break; 1919 } 1920 1921 if ( bSuccess && selectionChanged ) 1922 { 1923 m_rAntiImpl.Select(); 1924 } 1925 1926 return bSuccess; 1927 } 1928 1929 //------------------------------------------------------------------------------------------------------------------ 1930 void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow ) 1931 { 1932 PTableRenderer pRenderer = !!m_pModel ? m_pModel->getRenderer() : PTableRenderer(); 1933 if ( !!pRenderer ) 1934 { 1935 Rectangle aCellRect; 1936 impl_getCellRect( m_nCurColumn, m_nCurRow, aCellRect ); 1937 if ( _bShow ) 1938 pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect ); 1939 else 1940 pRenderer->HideCellCursor( *m_pDataWindow, aCellRect ); 1941 } 1942 } 1943 1944 //------------------------------------------------------------------------------------------------------------------ 1945 void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, Rectangle& _rCellRect ) const 1946 { 1947 DBG_CHECK_ME(); 1948 1949 if ( !m_pModel 1950 || ( COL_INVALID == _nColumn ) 1951 || ( ROW_INVALID == _nRow ) 1952 ) 1953 { 1954 _rCellRect.SetEmpty(); 1955 return; 1956 } 1957 1958 TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow ); 1959 _rCellRect = aCell.getRect(); 1960 } 1961 1962 //------------------------------------------------------------------------------------------------------------------ 1963 RowPos TableControl_Impl::getRowAtPoint( const Point& rPoint ) const 1964 { 1965 DBG_CHECK_ME(); 1966 return impl_getRowForAbscissa( rPoint.Y() ); 1967 } 1968 1969 //------------------------------------------------------------------------------------------------------------------ 1970 ColPos TableControl_Impl::getColAtPoint( const Point& rPoint ) const 1971 { 1972 DBG_CHECK_ME(); 1973 return impl_getColumnForOrdinate( rPoint.X() ); 1974 } 1975 1976 //------------------------------------------------------------------------------------------------------------------ 1977 TableCell TableControl_Impl::hitTest( Point const & i_point ) const 1978 { 1979 TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) ); 1980 if ( aCell.nColumn > COL_ROW_HEADERS ) 1981 { 1982 PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn ); 1983 MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] ); 1984 if ( ( rColInfo.getEnd() - 3 <= i_point.X() ) 1985 && ( rColInfo.getEnd() >= i_point.X() ) 1986 && pColumn->isResizable() 1987 ) 1988 { 1989 aCell.eArea = ColumnDivider; 1990 } 1991 } 1992 return aCell; 1993 } 1994 1995 //------------------------------------------------------------------------------------------------------------------ 1996 ColumnMetrics TableControl_Impl::getColumnMetrics( ColPos const i_column ) const 1997 { 1998 DBG_CHECK_ME(); 1999 2000 ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ), 2001 "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() ); 2002 return (ColumnMetrics const &)m_aColumnWidths[ i_column ]; 2003 } 2004 2005 //------------------------------------------------------------------------------------------------------------------ 2006 PTableModel TableControl_Impl::getModel() const 2007 { 2008 return m_pModel; 2009 } 2010 2011 //------------------------------------------------------------------------------------------------------------------ 2012 RowPos TableControl_Impl::getCurrentColumn() const 2013 { 2014 return m_nCurColumn; 2015 } 2016 2017 //------------------------------------------------------------------------------------------------------------------ 2018 RowPos TableControl_Impl::getCurrentRow() const 2019 { 2020 return m_nCurRow; 2021 } 2022 2023 //------------------------------------------------------------------------------------------------------------------ 2024 ::Size TableControl_Impl::getTableSizePixel() const 2025 { 2026 return m_pDataWindow->GetOutputSizePixel(); 2027 } 2028 2029 //------------------------------------------------------------------------------------------------------------------ 2030 void TableControl_Impl::setPointer( Pointer const & i_pointer ) 2031 { 2032 DBG_CHECK_ME(); 2033 m_pDataWindow->SetPointer( i_pointer ); 2034 } 2035 2036 //------------------------------------------------------------------------------------------------------------------ 2037 void TableControl_Impl::captureMouse() 2038 { 2039 m_pDataWindow->CaptureMouse(); 2040 } 2041 2042 //------------------------------------------------------------------------------------------------------------------ 2043 void TableControl_Impl::releaseMouse() 2044 { 2045 m_pDataWindow->ReleaseMouse(); 2046 } 2047 2048 //------------------------------------------------------------------------------------------------------------------ 2049 void TableControl_Impl::invalidate( TableArea const i_what ) 2050 { 2051 switch ( i_what ) 2052 { 2053 case TableAreaColumnHeaders: 2054 m_pDataWindow->Invalidate( calcHeaderRect( true ) ); 2055 break; 2056 2057 case TableAreaRowHeaders: 2058 m_pDataWindow->Invalidate( calcHeaderRect( false ) ); 2059 break; 2060 2061 case TableAreaDataArea: 2062 m_pDataWindow->Invalidate( impl_getAllVisibleDataCellArea() ); 2063 break; 2064 2065 case TableAreaAll: 2066 m_pDataWindow->Invalidate(); 2067 break; 2068 } 2069 } 2070 2071 //------------------------------------------------------------------------------------------------------------------ 2072 long TableControl_Impl::pixelWidthToAppFont( long const i_pixels ) const 2073 { 2074 return m_pDataWindow->PixelToLogic( Size( i_pixels, 0 ), MAP_APPFONT ).Width(); 2075 } 2076 2077 //------------------------------------------------------------------------------------------------------------------ 2078 long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits ) const 2079 { 2080 return m_pDataWindow->LogicToPixel( Size( i_appFontUnits, 0 ), MAP_APPFONT ).Width(); 2081 } 2082 2083 //------------------------------------------------------------------------------------------------------------------ 2084 void TableControl_Impl::hideTracking() 2085 { 2086 m_pDataWindow->HideTracking(); 2087 } 2088 2089 //------------------------------------------------------------------------------------------------------------------ 2090 void TableControl_Impl::showTracking( Rectangle const & i_location, sal_uInt16 const i_flags ) 2091 { 2092 m_pDataWindow->ShowTracking( i_location, i_flags ); 2093 } 2094 2095 //------------------------------------------------------------------------------------------------------------------ 2096 bool TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row ) 2097 { 2098 DBG_CHECK_ME(); 2099 return goTo( i_col, i_row ); 2100 } 2101 2102 //------------------------------------------------------------------------------------------------------------------ 2103 void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow ) 2104 { 2105 DBG_CHECK_ME(); 2106 // get the visible area of the table control and set the Left and right border of the region to be repainted 2107 Rectangle const aAllCells( impl_getAllVisibleCellsArea() ); 2108 2109 Rectangle aInvalidateRect; 2110 aInvalidateRect.Left() = aAllCells.Left(); 2111 aInvalidateRect.Right() = aAllCells.Right(); 2112 // if only one row is selected 2113 if ( _nPrevRow == _nCurRow ) 2114 { 2115 Rectangle aCellRect; 2116 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2117 aInvalidateRect.Top() = aCellRect.Top(); 2118 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2119 } 2120 //if the region is above the current row 2121 else if(_nPrevRow < _nCurRow ) 2122 { 2123 Rectangle aCellRect; 2124 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); 2125 aInvalidateRect.Top() = aCellRect.Top(); 2126 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2127 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2128 } 2129 //if the region is beneath the current row 2130 else 2131 { 2132 Rectangle aCellRect; 2133 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect ); 2134 aInvalidateRect.Top() = aCellRect.Top(); 2135 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect ); 2136 aInvalidateRect.Bottom() = aCellRect.Bottom(); 2137 } 2138 m_pDataWindow->Invalidate( aInvalidateRect ); 2139 } 2140 2141 //------------------------------------------------------------------------------------------------------------------ 2142 void TableControl_Impl::invalidateSelectedRows() 2143 { 2144 for ( ::std::vector< RowPos >::iterator selRow = m_aSelectedRows.begin(); 2145 selRow != m_aSelectedRows.end(); 2146 ++selRow 2147 ) 2148 { 2149 invalidateRow( *selRow ); 2150 } 2151 } 2152 2153 //------------------------------------------------------------------------------------------------------------------ 2154 void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow ) 2155 { 2156 RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow; 2157 RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1; 2158 RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow; 2159 2160 Rectangle aInvalidateRect; 2161 2162 Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() ); 2163 TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true ); 2164 while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) ) 2165 { 2166 aInvalidateRect.Union( aRow.getRect() ); 2167 aRow.moveDown(); 2168 } 2169 2170 if ( i_lastRow == ROW_INVALID ) 2171 aInvalidateRect.Bottom() = m_pDataWindow->GetOutputSizePixel().Height(); 2172 2173 m_pDataWindow->Invalidate( aInvalidateRect, 2174 m_pDataWindow->GetControlBackground().GetTransparency() ? INVALIDATE_TRANSPARENT : 0 ); 2175 } 2176 2177 //------------------------------------------------------------------------------ 2178 void TableControl_Impl::checkCursorPosition() 2179 { 2180 DBG_CHECK_ME(); 2181 2182 TableSize nVisibleRows = impl_getVisibleRows(true); 2183 TableSize nVisibleCols = impl_getVisibleColumns(true); 2184 if ( ( m_nTopRow + nVisibleRows > m_nRowCount ) 2185 && ( m_nRowCount >= nVisibleRows ) 2186 ) 2187 { 2188 --m_nTopRow; 2189 } 2190 else 2191 { 2192 m_nTopRow = 0; 2193 } 2194 2195 if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount ) 2196 && ( m_nColumnCount >= nVisibleCols ) 2197 ) 2198 { 2199 --m_nLeftColumn; 2200 } 2201 else 2202 { 2203 m_nLeftColumn = 0; 2204 } 2205 2206 m_pDataWindow->Invalidate(); 2207 } 2208 2209 //-------------------------------------------------------------------- 2210 TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const 2211 { 2212 DBG_CHECK_ME(); 2213 2214 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" ); 2215 2216 return lcl_getRowsFittingInto( 2217 m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel, 2218 m_nRowHeightPixel, 2219 _bAcceptPartialRow 2220 ); 2221 } 2222 2223 //-------------------------------------------------------------------- 2224 TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const 2225 { 2226 DBG_CHECK_ME(); 2227 2228 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" ); 2229 2230 return lcl_getColumnsVisibleWithin( 2231 Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ), 2232 m_nLeftColumn, 2233 *this, 2234 _bAcceptPartialCol 2235 ); 2236 } 2237 2238 //-------------------------------------------------------------------- 2239 bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow ) 2240 { 2241 DBG_CHECK_ME(); 2242 2243 // TODO: give veto listeners a chance 2244 2245 if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount ) 2246 || ( _nRow < 0 ) || ( _nRow >= m_nRowCount ) 2247 ) 2248 { 2249 OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" ); 2250 return false; 2251 } 2252 2253 SuppressCursor aHideCursor( *this ); 2254 m_nCurColumn = _nColumn; 2255 m_nCurRow = _nRow; 2256 2257 // ensure that the new cell is visible 2258 ensureVisible( m_nCurColumn, m_nCurRow, false ); 2259 return true; 2260 } 2261 2262 //-------------------------------------------------------------------- 2263 void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility ) 2264 { 2265 DBG_CHECK_ME(); 2266 DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount ) 2267 && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ), 2268 "TableControl_Impl::ensureVisible: invalid coordinates!" ); 2269 2270 SuppressCursor aHideCursor( *this ); 2271 2272 if ( _nColumn < m_nLeftColumn ) 2273 impl_scrollColumns( _nColumn - m_nLeftColumn ); 2274 else 2275 { 2276 TableSize nVisibleColumns = impl_getVisibleColumns( _bAcceptPartialVisibility ); 2277 if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 ) 2278 { 2279 impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) ); 2280 // TODO: since not all columns have the same width, this might in theory result 2281 // in the column still not being visible. 2282 } 2283 } 2284 2285 if ( _nRow < m_nTopRow ) 2286 impl_scrollRows( _nRow - m_nTopRow ); 2287 else 2288 { 2289 TableSize nVisibleRows = impl_getVisibleRows( _bAcceptPartialVisibility ); 2290 if ( _nRow > m_nTopRow + nVisibleRows - 1 ) 2291 impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) ); 2292 } 2293 } 2294 2295 //-------------------------------------------------------------------- 2296 ::rtl::OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col ) 2297 { 2298 Any aCellValue; 2299 m_pModel->getCellContent( i_col, i_row, aCellValue ); 2300 2301 ::rtl::OUString sCellStringContent; 2302 m_pModel->getRenderer()->GetFormattedCellString( aCellValue, i_col, i_row, sCellStringContent ); 2303 2304 return sCellStringContent; 2305 } 2306 2307 //-------------------------------------------------------------------- 2308 TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta ) 2309 { 2310 // compute new top row 2311 RowPos nNewTopRow = 2312 ::std::max( 2313 ::std::min( (RowPos)( m_nTopRow + _nRowDelta ), (RowPos)( m_nRowCount - 1 ) ), 2314 (RowPos)0 2315 ); 2316 2317 RowPos nOldTopRow = m_nTopRow; 2318 m_nTopRow = nNewTopRow; 2319 2320 // if updates are enabled currently, scroll the viewport 2321 if ( m_nTopRow != nOldTopRow ) 2322 { 2323 DBG_SUSPEND_INV( INV_SCROLL_POSITION ); 2324 SuppressCursor aHideCursor( *this ); 2325 // TODO: call a onStartScroll at our listener (or better an own onStartScroll, 2326 // which hides the cursor and then calls the listener) 2327 // Same for onEndScroll 2328 2329 // scroll the view port, if possible 2330 long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow ); 2331 2332 Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() ); 2333 2334 if ( m_pDataWindow->GetBackground().IsScrollable() 2335 && abs( nPixelDelta ) < aDataArea.GetHeight() 2336 ) 2337 { 2338 m_pDataWindow->Scroll( 0, (long)-nPixelDelta, aDataArea, SCROLL_CLIP | SCROLL_UPDATE | SCROLL_CHILDREN); 2339 } 2340 else 2341 m_pDataWindow->Invalidate( INVALIDATE_UPDATE ); 2342 2343 // update the position at the vertical scrollbar 2344 if ( m_pVScroll != NULL ) 2345 m_pVScroll->SetThumbPos( m_nTopRow ); 2346 } 2347 2348 // The scroll bar availaility might change when we scrolled. 2349 // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10. 2350 // Now let 2351 // - the user scroll to row number 6, so the last 5 rows are visible 2352 // - somebody remove the last 4 rows 2353 // - the user scroll to row number 5 being the top row, so the last two rows are visible 2354 // - somebody remove row number 6 2355 // - the user scroll to row number 1 2356 // => in this case, the need for the scrollbar vanishes immediately. 2357 if ( m_nTopRow == 0 ) 2358 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); 2359 2360 return (TableSize)( m_nTopRow - nOldTopRow ); 2361 } 2362 2363 //-------------------------------------------------------------------- 2364 TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta ) 2365 { 2366 DBG_CHECK_ME(); 2367 return impl_ni_ScrollRows( i_rowDelta ); 2368 } 2369 2370 //-------------------------------------------------------------------- 2371 TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta ) 2372 { 2373 // compute new left column 2374 const ColPos nNewLeftColumn = 2375 ::std::max( 2376 ::std::min( (ColPos)( m_nLeftColumn + _nColumnDelta ), (ColPos)( m_nColumnCount - 1 ) ), 2377 (ColPos)0 2378 ); 2379 2380 const ColPos nOldLeftColumn = m_nLeftColumn; 2381 m_nLeftColumn = nNewLeftColumn; 2382 2383 // if updates are enabled currently, scroll the viewport 2384 if ( m_nLeftColumn != nOldLeftColumn ) 2385 { 2386 DBG_SUSPEND_INV( INV_SCROLL_POSITION ); 2387 SuppressCursor aHideCursor( *this ); 2388 // TODO: call a onStartScroll at our listener (or better an own onStartScroll, 2389 // which hides the cursor and then calls the listener) 2390 // Same for onEndScroll 2391 2392 // scroll the view port, if possible 2393 const Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() ); 2394 2395 long nPixelDelta = 2396 m_aColumnWidths[ nOldLeftColumn ].getStart() 2397 - m_aColumnWidths[ m_nLeftColumn ].getStart(); 2398 2399 // update our column positions 2400 // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct 2401 // information in m_aColumnWidths 2402 for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin(); 2403 colPos != m_aColumnWidths.end(); 2404 ++colPos 2405 ) 2406 { 2407 colPos->move( nPixelDelta ); 2408 } 2409 2410 // scroll the window content (if supported and possible), or invalidate the complete window 2411 if ( m_pDataWindow->GetBackground().IsScrollable() 2412 && abs( nPixelDelta ) < aDataArea.GetWidth() 2413 ) 2414 { 2415 m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, SCROLL_CLIP | SCROLL_UPDATE ); 2416 } 2417 else 2418 m_pDataWindow->Invalidate( INVALIDATE_UPDATE ); 2419 2420 // update the position at the horizontal scrollbar 2421 if ( m_pHScroll != NULL ) 2422 m_pHScroll->SetThumbPos( m_nLeftColumn ); 2423 } 2424 2425 // The scroll bar availaility might change when we scrolled. This is because we do not hide 2426 // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will 2427 // be auto-hidden when it's scrolled back to pos 0. 2428 if ( m_nLeftColumn == 0 ) 2429 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) ); 2430 2431 return (TableSize)( m_nLeftColumn - nOldLeftColumn ); 2432 } 2433 2434 //------------------------------------------------------------------------------------------------------------------ 2435 TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta ) 2436 { 2437 DBG_CHECK_ME(); 2438 return impl_ni_ScrollColumns( i_columnDelta ); 2439 } 2440 2441 //------------------------------------------------------------------------------------------------------------------ 2442 SelectionEngine* TableControl_Impl::getSelEngine() 2443 { 2444 return m_pSelEngine; 2445 } 2446 2447 //------------------------------------------------------------------------------------------------------------------ 2448 ScrollBar* TableControl_Impl::getHorzScrollbar() 2449 { 2450 return m_pHScroll; 2451 } 2452 2453 //------------------------------------------------------------------------------------------------------------------ 2454 ScrollBar* TableControl_Impl::getVertScrollbar() 2455 { 2456 return m_pVScroll; 2457 } 2458 2459 //------------------------------------------------------------------------------------------------------------------ 2460 bool TableControl_Impl::isRowSelected( RowPos i_row ) const 2461 { 2462 return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end(); 2463 } 2464 2465 //------------------------------------------------------------------------------------------------------------------ 2466 RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const 2467 { 2468 if ( i_selectionIndex < m_aSelectedRows.size() ) 2469 return m_aSelectedRows[ i_selectionIndex ]; 2470 return ROW_INVALID; 2471 } 2472 2473 //------------------------------------------------------------------------------------------------------------------ 2474 int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current) 2475 { 2476 std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current); 2477 if ( it != selectedRows.end() ) 2478 { 2479 return it - selectedRows.begin(); 2480 } 2481 return -1; 2482 } 2483 2484 //-------------------------------------------------------------------- 2485 ColPos TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate ) const 2486 { 2487 DBG_CHECK_ME(); 2488 2489 if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) ) 2490 return COL_INVALID; 2491 2492 if ( i_ordinate < m_nRowHeaderWidthPixel ) 2493 return COL_ROW_HEADERS; 2494 2495 ColumnPositions::const_iterator lowerBound = ::std::lower_bound( 2496 m_aColumnWidths.begin(), 2497 m_aColumnWidths.end(), 2498 i_ordinate + 1, 2499 ColumnInfoPositionLess() 2500 ); 2501 if ( lowerBound == m_aColumnWidths.end() ) 2502 { 2503 // point is *behind* the start of the last column ... 2504 if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() ) 2505 // ... but still before its end 2506 return m_nColumnCount - 1; 2507 return COL_INVALID; 2508 } 2509 return lowerBound - m_aColumnWidths.begin(); 2510 } 2511 2512 //-------------------------------------------------------------------- 2513 RowPos TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa ) const 2514 { 2515 DBG_CHECK_ME(); 2516 2517 if ( i_abscissa < 0 ) 2518 return ROW_INVALID; 2519 2520 if ( i_abscissa < m_nColHeaderHeightPixel ) 2521 return ROW_COL_HEADERS; 2522 2523 long const abscissa = i_abscissa - m_nColHeaderHeightPixel; 2524 long const row = m_nTopRow + abscissa / m_nRowHeightPixel; 2525 return row < m_pModel->getRowCount() ? row : ROW_INVALID; 2526 } 2527 2528 //-------------------------------------------------------------------- 2529 bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex ) 2530 { 2531 DBG_CHECK_ME(); 2532 2533 ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex ); 2534 if ( selPos == m_aSelectedRows.end() ) 2535 return false; 2536 2537 m_aSelectedRows.erase( selPos ); 2538 return true; 2539 } 2540 2541 //-------------------------------------------------------------------- 2542 bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex ) 2543 { 2544 DBG_CHECK_ME(); 2545 2546 if ( isRowSelected( i_rowIndex ) ) 2547 return false; 2548 2549 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); 2550 switch ( eSelMode ) 2551 { 2552 case SINGLE_SELECTION: 2553 if ( !m_aSelectedRows.empty() ) 2554 { 2555 OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" ); 2556 m_aSelectedRows[0] = i_rowIndex; 2557 break; 2558 } 2559 // fall through 2560 2561 case MULTIPLE_SELECTION: 2562 m_aSelectedRows.push_back( i_rowIndex ); 2563 break; 2564 2565 default: 2566 OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" ); 2567 return false; 2568 } 2569 2570 return true; 2571 } 2572 2573 //-------------------------------------------------------------------- 2574 bool TableControl_Impl::markAllRowsAsDeselected() 2575 { 2576 if ( m_aSelectedRows.empty() ) 2577 return false; 2578 2579 m_aSelectedRows.clear(); 2580 return true; 2581 } 2582 2583 //-------------------------------------------------------------------- 2584 bool TableControl_Impl::markAllRowsAsSelected() 2585 { 2586 DBG_CHECK_ME(); 2587 2588 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode(); 2589 ENSURE_OR_RETURN_FALSE( eSelMode == MULTIPLE_SELECTION, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" ); 2590 2591 if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) ) 2592 { 2593 #if OSL_DEBUG_LEVEL > 0 2594 for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row ) 2595 { 2596 OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" ); 2597 } 2598 #endif 2599 // already all rows marked as selected 2600 return false; 2601 } 2602 2603 m_aSelectedRows.clear(); 2604 for ( RowPos i=0; i < m_pModel->getRowCount(); ++i ) 2605 m_aSelectedRows.push_back(i); 2606 2607 return true; 2608 } 2609 2610 //-------------------------------------------------------------------- 2611 void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) 2612 { 2613 impl_commitAccessibleEvent( i_eventID, i_newValue, i_oldValue ); 2614 } 2615 2616 //-------------------------------------------------------------------- 2617 void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue ) 2618 { 2619 DBG_CHECK_ME(); 2620 if ( impl_isAccessibleAlive() ) 2621 m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue ); 2622 } 2623 2624 //-------------------------------------------------------------------- 2625 void TableControl_Impl::commitTableEvent( 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->commitTableEvent( i_eventID, i_newValue, i_oldValue ); 2630 } 2631 2632 //-------------------------------------------------------------------- 2633 Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader) 2634 { 2635 Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() ); 2636 Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() ); 2637 if ( bColHeader ) 2638 return Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) ); 2639 else 2640 return Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) ); 2641 } 2642 2643 //-------------------------------------------------------------------- 2644 Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos ) 2645 { 2646 Rectangle const aHeaderRect = calcHeaderRect( bColHeader ); 2647 TableCellGeometry const aGeometry( 2648 *this, aHeaderRect, 2649 bColHeader ? nPos : COL_ROW_HEADERS, 2650 bColHeader ? ROW_COL_HEADERS : nPos 2651 ); 2652 return aGeometry.getRect(); 2653 } 2654 2655 //-------------------------------------------------------------------- 2656 Rectangle TableControl_Impl::calcTableRect() 2657 { 2658 return impl_getAllVisibleDataCellArea(); 2659 } 2660 2661 //-------------------------------------------------------------------- 2662 Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol ) 2663 { 2664 Rectangle aCellRect; 2665 impl_getCellRect( nRow, nCol, aCellRect ); 2666 return aCellRect; 2667 } 2668 2669 //-------------------------------------------------------------------- 2670 IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ ) 2671 { 2672 DBG_CHECK_ME(); 2673 // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of 2674 // doing a complete re-layout? 2675 impl_ni_relayout(); 2676 return 1L; 2677 } 2678 2679 //-------------------------------------------------------------------- 2680 IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar ) 2681 { 2682 DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ), 2683 "TableControl_Impl::OnScroll: where did this come from?" ); 2684 2685 if ( _pScrollbar == m_pVScroll ) 2686 impl_ni_ScrollRows( _pScrollbar->GetDelta() ); 2687 else 2688 impl_ni_ScrollColumns( _pScrollbar->GetDelta() ); 2689 2690 return 0L; 2691 } 2692 2693 //------------------------------------------------------------------------------------------------------------------ 2694 Reference< XAccessible > TableControl_Impl::getAccessible( Window& i_parentWindow ) 2695 { 2696 DBG_TESTSOLARMUTEX(); 2697 if ( m_pAccessibleTable == NULL ) 2698 { 2699 Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible(); 2700 if ( xAccParent.is() ) 2701 { 2702 m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl( 2703 xAccParent, m_rAntiImpl 2704 ); 2705 } 2706 } 2707 2708 Reference< XAccessible > xAccessible; 2709 if ( m_pAccessibleTable ) 2710 xAccessible = m_pAccessibleTable->getMyself(); 2711 return xAccessible; 2712 } 2713 2714 //------------------------------------------------------------------------------------------------------------------ 2715 void TableControl_Impl::disposeAccessible() 2716 { 2717 if ( m_pAccessibleTable ) 2718 m_pAccessibleTable->dispose(); 2719 m_pAccessibleTable = NULL; 2720 } 2721 2722 //------------------------------------------------------------------------------------------------------------------ 2723 bool TableControl_Impl::impl_isAccessibleAlive() const 2724 { 2725 DBG_CHECK_ME(); 2726 return ( NULL != m_pAccessibleTable ) && m_pAccessibleTable->isAlive(); 2727 } 2728 2729 //------------------------------------------------------------------------------------------------------------------ 2730 void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue, Any const & i_oldValue ) 2731 { 2732 DBG_CHECK_ME(); 2733 if ( impl_isAccessibleAlive() ) 2734 m_pAccessibleTable->commitEvent( i_eventID, i_newValue, i_oldValue ); 2735 } 2736 2737 //================================================================================================================== 2738 //= TableFunctionSet 2739 //================================================================================================================== 2740 //------------------------------------------------------------------------------------------------------------------ 2741 TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl) 2742 :m_pTableControl( _pTableControl) 2743 ,m_nCurrentRow( ROW_INVALID ) 2744 { 2745 } 2746 //------------------------------------------------------------------------------------------------------------------ 2747 TableFunctionSet::~TableFunctionSet() 2748 { 2749 } 2750 //------------------------------------------------------------------------------------------------------------------ 2751 void TableFunctionSet::BeginDrag() 2752 { 2753 } 2754 //------------------------------------------------------------------------------------------------------------------ 2755 void TableFunctionSet::CreateAnchor() 2756 { 2757 m_pTableControl->setAnchor( m_pTableControl->getCurRow() ); 2758 } 2759 2760 //------------------------------------------------------------------------------------------------------------------ 2761 void TableFunctionSet::DestroyAnchor() 2762 { 2763 m_pTableControl->setAnchor( ROW_INVALID ); 2764 } 2765 2766 //------------------------------------------------------------------------------------------------------------------ 2767 sal_Bool TableFunctionSet::SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor) 2768 { 2769 sal_Bool bHandled = sal_False; 2770 // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click 2771 RowPos newRow = m_pTableControl->getRowAtPoint( rPoint ); 2772 if ( newRow == ROW_COL_HEADERS ) 2773 newRow = m_pTableControl->getTopRow(); 2774 2775 ColPos newCol = m_pTableControl->getColAtPoint( rPoint ); 2776 if ( newCol == COL_ROW_HEADERS ) 2777 newCol = m_pTableControl->getLeftColumn(); 2778 2779 if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) ) 2780 return sal_False; 2781 2782 if ( bDontSelectAtCursor ) 2783 { 2784 if ( m_pTableControl->getSelectedRowCount() > 1 ) 2785 m_pTableControl->getSelEngine()->AddAlways(sal_True); 2786 bHandled = sal_True; 2787 } 2788 else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() ) 2789 { 2790 //selecting region, 2791 int diff = m_pTableControl->getCurRow() - newRow; 2792 //selected region lies above the last selection 2793 if( diff >= 0) 2794 { 2795 //put selected rows in vector 2796 while ( m_pTableControl->getAnchor() >= newRow ) 2797 { 2798 m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); 2799 m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); 2800 diff--; 2801 } 2802 m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); 2803 } 2804 //selected region lies beneath the last selected row 2805 else 2806 { 2807 while ( m_pTableControl->getAnchor() <= newRow ) 2808 { 2809 m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() ); 2810 m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 ); 2811 diff++; 2812 } 2813 m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 ); 2814 } 2815 m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow ); 2816 bHandled = sal_True; 2817 } 2818 //no region selected 2819 else 2820 { 2821 if ( !m_pTableControl->hasRowSelection() ) 2822 m_pTableControl->markRowAsSelected( newRow ); 2823 else 2824 { 2825 if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION ) 2826 { 2827 DeselectAll(); 2828 m_pTableControl->markRowAsSelected( newRow ); 2829 } 2830 else 2831 { 2832 m_pTableControl->markRowAsSelected( newRow ); 2833 } 2834 } 2835 if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION ) 2836 m_pTableControl->getSelEngine()->AddAlways(sal_True); 2837 2838 m_pTableControl->invalidateRow( newRow ); 2839 bHandled = sal_True; 2840 } 2841 m_pTableControl->goTo( newCol, newRow ); 2842 return bHandled; 2843 } 2844 //------------------------------------------------------------------------------------------------------------------ 2845 sal_Bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint ) 2846 { 2847 m_pTableControl->getSelEngine()->AddAlways(sal_False); 2848 if ( !m_pTableControl->hasRowSelection() ) 2849 return sal_False; 2850 else 2851 { 2852 RowPos curRow = m_pTableControl->getRowAtPoint( rPoint ); 2853 m_pTableControl->setAnchor( ROW_INVALID ); 2854 bool selected = m_pTableControl->isRowSelected( curRow ); 2855 m_nCurrentRow = curRow; 2856 return selected; 2857 } 2858 } 2859 //------------------------------------------------------------------------------------------------------------------ 2860 void TableFunctionSet::DeselectAtPoint( const Point& rPoint ) 2861 { 2862 (void)rPoint; 2863 m_pTableControl->invalidateRow( m_nCurrentRow ); 2864 m_pTableControl->markRowAsDeselected( m_nCurrentRow ); 2865 } 2866 2867 //------------------------------------------------------------------------------------------------------------------ 2868 void TableFunctionSet::DeselectAll() 2869 { 2870 if ( m_pTableControl->hasRowSelection() ) 2871 { 2872 for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i ) 2873 { 2874 RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i); 2875 m_pTableControl->invalidateRow( rowIndex ); 2876 } 2877 2878 m_pTableControl->markAllRowsAsDeselected(); 2879 } 2880 } 2881 2882 //...................................................................................................................... 2883 } } // namespace svt::table 2884 //...................................................................................................................... 2885