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