update credits
[LibreOffice.git] / svtools / source / table / tablecontrol_impl.cxx
blobcf89b13803dc185af18b7b75022804def122bbb5
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "svtools/table/tablecontrol.hxx"
22 #include "svtools/table/defaultinputhandler.hxx"
23 #include "svtools/table/tablemodel.hxx"
25 #include "tabledatawindow.hxx"
26 #include "tablecontrol_impl.hxx"
27 #include "tablegeometry.hxx"
29 #include <com/sun/star/accessibility/XAccessible.hpp>
30 #include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
31 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
32 #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
34 #include <comphelper/flagguard.hxx>
35 #include <vcl/scrbar.hxx>
36 #include <vcl/seleng.hxx>
37 #include <rtl/ref.hxx>
38 #include <vcl/image.hxx>
39 #include <tools/diagnose_ex.h>
41 #include <functional>
42 #include <numeric>
44 #define MIN_COLUMN_WIDTH_PIXEL 4
46 //......................................................................................................................
47 namespace svt { namespace table
49 //......................................................................................................................
51 using ::com::sun::star::accessibility::AccessibleTableModelChange;
52 using ::com::sun::star::uno::makeAny;
53 using ::com::sun::star::uno::Any;
54 using ::com::sun::star::accessibility::XAccessible;
55 using ::com::sun::star::uno::Reference;
57 namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId;
58 namespace AccessibleTableModelChangeType = ::com::sun::star::accessibility::AccessibleTableModelChangeType;
60 //==================================================================================================================
61 //= SuppressCursor
62 //==================================================================================================================
63 class SuppressCursor
65 private:
66 ITableControl& m_rTable;
68 public:
69 SuppressCursor( ITableControl& _rTable )
70 :m_rTable( _rTable )
72 m_rTable.hideCursor();
74 ~SuppressCursor()
76 m_rTable.showCursor();
80 //====================================================================
81 //= EmptyTableModel
82 //====================================================================
83 /** default implementation of an ->ITableModel, used as fallback when no
84 real model is present
86 Instances of this class are static in any way, and provide the least
87 necessary default functionality for a table model.
89 class EmptyTableModel : public ITableModel
91 public:
92 EmptyTableModel()
96 // ITableModel overridables
97 virtual TableSize getColumnCount() const
99 return 0;
101 virtual TableSize getRowCount() const
103 return 0;
105 virtual bool hasColumnHeaders() const
107 return false;
109 virtual bool hasRowHeaders() const
111 return false;
113 virtual bool isCellEditable( ColPos col, RowPos row ) const
115 (void)col;
116 (void)row;
117 return false;
119 virtual PColumnModel getColumnModel( ColPos column )
121 OSL_FAIL( "EmptyTableModel::getColumnModel: invalid call!" );
122 (void)column;
123 return PColumnModel();
125 virtual PTableRenderer getRenderer() const
127 return PTableRenderer();
129 virtual PTableInputHandler getInputHandler() const
131 return PTableInputHandler();
133 virtual TableMetrics getRowHeight() const
135 return 5 * 100;
137 virtual void setRowHeight(TableMetrics _nRowHeight)
139 (void)_nRowHeight;
141 virtual TableMetrics getColumnHeaderHeight() const
143 return 0;
145 virtual TableMetrics getRowHeaderWidth() const
147 return 0;
149 virtual ScrollbarVisibility getVerticalScrollbarVisibility() const
151 return ScrollbarShowNever;
153 virtual ScrollbarVisibility getHorizontalScrollbarVisibility() const
155 return ScrollbarShowNever;
157 virtual void addTableModelListener( const PTableModelListener& i_listener )
159 (void)i_listener;
161 virtual void removeTableModelListener( const PTableModelListener& i_listener )
163 (void)i_listener;
165 virtual ::boost::optional< ::Color > getLineColor() const
167 return ::boost::optional< ::Color >();
169 virtual ::boost::optional< ::Color > getHeaderBackgroundColor() const
171 return ::boost::optional< ::Color >();
173 virtual ::boost::optional< ::Color > getHeaderTextColor() const
175 return ::boost::optional< ::Color >();
177 virtual ::boost::optional< ::Color > getActiveSelectionBackColor() const
179 return ::boost::optional< ::Color >();
181 virtual ::boost::optional< ::Color > getInactiveSelectionBackColor() const
183 return ::boost::optional< ::Color >();
185 virtual ::boost::optional< ::Color > getActiveSelectionTextColor() const
187 return ::boost::optional< ::Color >();
189 virtual ::boost::optional< ::Color > getInactiveSelectionTextColor() const
191 return ::boost::optional< ::Color >();
193 virtual ::boost::optional< ::Color > getTextColor() const
195 return ::boost::optional< ::Color >();
197 virtual ::boost::optional< ::Color > getTextLineColor() const
199 return ::boost::optional< ::Color >();
201 virtual ::boost::optional< ::std::vector< ::Color > > getRowBackgroundColors() const
203 return ::boost::optional< ::std::vector< ::Color > >();
205 virtual ::com::sun::star::style::VerticalAlignment getVerticalAlign() const
207 return com::sun::star::style::VerticalAlignment(0);
209 virtual ITableDataSort* getSortAdapter()
211 return NULL;
213 virtual void getCellContent( ColPos const i_col, RowPos const i_row, ::com::sun::star::uno::Any& o_cellContent )
215 (void)i_row;
216 (void)i_col;
217 o_cellContent.clear();
219 virtual void getCellToolTip( ColPos const, RowPos const, ::com::sun::star::uno::Any& )
222 virtual Any getRowHeading( RowPos const i_rowPos ) const
224 (void)i_rowPos;
225 return Any();
230 //====================================================================
231 //= TableControl_Impl
232 //====================================================================
233 DBG_NAME( TableControl_Impl )
235 #ifdef DBG_UTIL
236 //====================================================================
237 //= SuspendInvariants
238 //====================================================================
239 class SuspendInvariants
241 private:
242 const TableControl_Impl& m_rTable;
243 sal_Int32 m_nSuspendFlags;
245 public:
246 SuspendInvariants( const TableControl_Impl& _rTable, sal_Int32 _nSuspendFlags )
247 :m_rTable( _rTable )
248 ,m_nSuspendFlags( _nSuspendFlags )
250 //DBG_ASSERT( ( m_rTable.m_nRequiredInvariants & m_nSuspendFlags ) == m_nSuspendFlags,
251 // "SuspendInvariants: cannot suspend what is already suspended!" );
252 const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants &= ~m_nSuspendFlags;
254 ~SuspendInvariants()
256 const_cast< TableControl_Impl& >( m_rTable ).m_nRequiredInvariants |= m_nSuspendFlags;
259 #define DBG_SUSPEND_INV( flags ) \
260 SuspendInvariants aSuspendInv( *this, flags );
261 #else
262 #define DBG_SUSPEND_INV( flags )
263 #endif
265 #ifdef DBG_UTIL
266 //====================================================================
267 const char* TableControl_Impl_checkInvariants( const void* _pInstance )
269 return static_cast< const TableControl_Impl* >( _pInstance )->impl_checkInvariants();
272 namespace
274 template< typename SCALAR_TYPE >
275 bool lcl_checkLimitsExclusive( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax )
277 return ( _nValue > _nMin ) && ( _nValue < _nMax );
280 template< typename SCALAR_TYPE >
281 bool lcl_checkLimitsExclusive_OrDefault_OrFallback( SCALAR_TYPE _nValue, SCALAR_TYPE _nMin, SCALAR_TYPE _nMax,
282 PTableModel _pModel, SCALAR_TYPE _nDefaultOrFallback )
284 if ( !_pModel )
285 return _nValue == _nDefaultOrFallback;
286 if ( _nMax <= _nMin )
287 return _nDefaultOrFallback == _nValue;
288 return lcl_checkLimitsExclusive( _nValue, _nMin, _nMax );
292 //------------------------------------------------------------------------------------------------------------------
293 const sal_Char* TableControl_Impl::impl_checkInvariants() const
295 if ( !m_pModel )
296 return "no model, not even an EmptyTableModel";
298 if ( !m_pDataWindow )
299 return "invalid data window!";
301 if ( m_pModel->getColumnCount() != m_nColumnCount )
302 return "column counts are inconsistent!";
304 if ( m_pModel->getRowCount() != m_nRowCount )
305 return "row counts are inconsistent!";
307 if ( ( ( m_nCurColumn != COL_INVALID ) && !m_aColumnWidths.empty() && ( m_nCurColumn < 0 ) ) || ( m_nCurColumn >= (ColPos)m_aColumnWidths.size() ) )
308 return "current column is invalid!";
310 if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nTopRow, (RowPos)-1, m_nRowCount, getModel(), (RowPos)0 ) )
311 return "invalid top row value!";
313 if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurRow, (RowPos)-1, m_nRowCount, getModel(), ROW_INVALID ) )
314 return "invalid current row value!";
316 if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nLeftColumn, (ColPos)-1, m_nColumnCount, getModel(), (ColPos)0 ) )
317 return "invalid current column value!";
319 if ( !lcl_checkLimitsExclusive_OrDefault_OrFallback( m_nCurColumn, (ColPos)-1, m_nColumnCount, getModel(), COL_INVALID ) )
320 return "invalid current column value!";
322 if ( m_pInputHandler != m_pModel->getInputHandler() )
323 return "input handler is not the model-provided one!";
325 // m_aSelectedRows should have reasonable content
327 if ( m_aSelectedRows.size() > size_t( m_pModel->getRowCount() ) )
328 return "there are more rows selected than actually exist";
329 for ( ::std::vector< RowPos >::const_iterator selRow = m_aSelectedRows.begin();
330 selRow != m_aSelectedRows.end();
331 ++selRow
334 if ( ( *selRow < 0 ) || ( *selRow >= m_pModel->getRowCount() ) )
335 return "a non-existent row is selected";
339 // m_nColHeaderHeightPixel consistent with the model's value?
341 TableMetrics nHeaderHeight = m_pModel->hasColumnHeaders() ? m_pModel->getColumnHeaderHeight() : 0;
342 nHeaderHeight = m_rAntiImpl.LogicToPixel( Size( 0, nHeaderHeight ), MAP_APPFONT ).Height();
343 if ( nHeaderHeight != m_nColHeaderHeightPixel )
344 return "column header heights are inconsistent!";
347 bool isDummyModel = dynamic_cast< const EmptyTableModel* >( m_pModel.get() ) != NULL;
348 if ( !isDummyModel )
350 TableMetrics nRowHeight = m_pModel->getRowHeight();
351 nRowHeight = m_rAntiImpl.LogicToPixel( Size( 0, nRowHeight ), MAP_APPFONT).Height();
352 if ( nRowHeight != m_nRowHeightPixel )
353 return "row heights are inconsistent!";
356 // m_nRowHeaderWidthPixel consistent with the model's value?
358 TableMetrics nHeaderWidth = m_pModel->hasRowHeaders() ? m_pModel->getRowHeaderWidth() : 0;
359 nHeaderWidth = m_rAntiImpl.LogicToPixel( Size( nHeaderWidth, 0 ), MAP_APPFONT ).Width();
360 if ( nHeaderWidth != m_nRowHeaderWidthPixel )
361 return "row header widths are inconsistent!";
364 // m_aColumnWidths consistency
365 if ( size_t( m_nColumnCount ) != m_aColumnWidths.size() )
366 return "wrong number of cached column widths";
368 for ( ColumnPositions::const_iterator col = m_aColumnWidths.begin();
369 col != m_aColumnWidths.end();
372 if ( col->getEnd() < col->getStart() )
373 return "column widths: 'end' is expected to not be smaller than start";
375 ColumnPositions::const_iterator nextCol = col + 1;
376 if ( nextCol != m_aColumnWidths.end() )
377 if ( col->getEnd() != nextCol->getStart() )
378 return "column widths: one column's end should be the next column's start";
379 col = nextCol;
382 if ( m_nLeftColumn < m_nColumnCount )
383 if ( m_aColumnWidths[ m_nLeftColumn ].getStart() != m_nRowHeaderWidthPixel )
384 return "the left-most column should start immediately after the row header";
386 if ( m_nCursorHidden < 0 )
387 return "invalid hidden count for the cursor!";
389 if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pVScroll )
391 DBG_SUSPEND_INV( INV_SCROLL_POSITION );
392 // prevent infinite recursion
394 if ( m_nLeftColumn < 0 )
395 return "invalid left-most column index";
396 if ( m_pVScroll->GetThumbPos() != m_nTopRow )
397 return "vertical scroll bar |position| is incorrect!";
398 if ( m_pVScroll->GetRange().Max() != m_nRowCount )
399 return "vertical scroll bar |range| is incorrect!";
400 if ( m_pVScroll->GetVisibleSize() != impl_getVisibleRows( false ) )
401 return "vertical scroll bar |visible size| is incorrect!";
404 if ( ( m_nRequiredInvariants & INV_SCROLL_POSITION ) && m_pHScroll )
406 DBG_SUSPEND_INV( INV_SCROLL_POSITION );
407 // prevent infinite recursion
409 if ( m_pHScroll->GetThumbPos() != m_nLeftColumn )
410 return "horizontal scroll bar |position| is incorrect!";
411 if ( m_pHScroll->GetRange().Max() != m_nColumnCount )
412 return "horizontal scroll bar |range| is incorrect!";
413 if ( m_pHScroll->GetVisibleSize() != impl_getVisibleColumns( false ) )
414 return "horizontal scroll bar |visible size| is incorrect!";
417 return NULL;
419 #endif
421 #define DBG_CHECK_ME() \
422 DBG_CHKTHIS( TableControl_Impl, TableControl_Impl_checkInvariants )
424 //------------------------------------------------------------------------------------------------------------------
425 TableControl_Impl::TableControl_Impl( TableControl& _rAntiImpl )
426 :m_rAntiImpl ( _rAntiImpl )
427 ,m_pModel ( new EmptyTableModel )
428 ,m_pInputHandler ( )
429 ,m_nRowHeightPixel ( 15 )
430 ,m_nColHeaderHeightPixel( 0 )
431 ,m_nRowHeaderWidthPixel ( 0 )
432 ,m_nColumnCount ( 0 )
433 ,m_nRowCount ( 0 )
434 ,m_bColumnsFit ( true )
435 ,m_nCurColumn ( COL_INVALID )
436 ,m_nCurRow ( ROW_INVALID )
437 ,m_nLeftColumn ( 0 )
438 ,m_nTopRow ( 0 )
439 ,m_nCursorHidden ( 1 )
440 ,m_pDataWindow ( new TableDataWindow( *this ) )
441 ,m_pVScroll ( NULL )
442 ,m_pHScroll ( NULL )
443 ,m_pScrollCorner ( NULL )
444 ,m_pSelEngine ( )
445 ,m_aSelectedRows ( )
446 ,m_pTableFunctionSet ( new TableFunctionSet( this ) )
447 ,m_nAnchor ( -1 )
448 ,m_bUpdatingColWidths ( false )
449 ,m_pAccessibleTable ( NULL )
450 #ifdef DBG_UTIL
451 ,m_nRequiredInvariants ( INV_SCROLL_POSITION )
452 #endif
454 DBG_CTOR( TableControl_Impl, TableControl_Impl_checkInvariants );
455 m_pSelEngine = new SelectionEngine( m_pDataWindow.get(), m_pTableFunctionSet );
456 m_pSelEngine->SetSelectionMode(SINGLE_SELECTION);
457 m_pDataWindow->SetPosPixel( Point( 0, 0 ) );
458 m_pDataWindow->Show();
461 //------------------------------------------------------------------------------------------------------------------
462 TableControl_Impl::~TableControl_Impl()
464 DBG_DTOR( TableControl_Impl, TableControl_Impl_checkInvariants );
466 DELETEZ( m_pVScroll );
467 DELETEZ( m_pHScroll );
468 DELETEZ( m_pScrollCorner );
469 DELETEZ( m_pTableFunctionSet );
470 DELETEZ( m_pSelEngine );
473 //------------------------------------------------------------------------------------------------------------------
474 void TableControl_Impl::setModel( PTableModel _pModel )
476 DBG_CHECK_ME();
478 SuppressCursor aHideCursor( *this );
480 if ( !!m_pModel )
481 m_pModel->removeTableModelListener( shared_from_this() );
483 m_pModel = _pModel;
484 if ( !m_pModel)
485 m_pModel.reset( new EmptyTableModel );
487 m_pModel->addTableModelListener( shared_from_this() );
489 m_nCurRow = ROW_INVALID;
490 m_nCurColumn = COL_INVALID;
492 // recalc some model-dependent cached info
493 impl_ni_updateCachedModelValues();
494 impl_ni_relayout();
496 // completely invalidate
497 m_rAntiImpl.Invalidate();
499 // reset cursor to (0,0)
500 if ( m_nRowCount ) m_nCurRow = 0;
501 if ( m_nColumnCount ) m_nCurColumn = 0;
504 //------------------------------------------------------------------------------------------------------------------
505 namespace
507 bool lcl_adjustSelectedRows( ::std::vector< RowPos >& io_selectionIndexes, RowPos const i_firstAffectedRowIndex, TableSize const i_offset )
509 bool didChanges = false;
510 for ( ::std::vector< RowPos >::iterator selPos = io_selectionIndexes.begin();
511 selPos != io_selectionIndexes.end();
512 ++selPos
515 if ( *selPos < i_firstAffectedRowIndex )
516 continue;
517 *selPos += i_offset;
518 didChanges = true;
520 return didChanges;
524 //------------------------------------------------------------------------------------------------------------------
525 void TableControl_Impl::rowsInserted( RowPos i_first, RowPos i_last )
527 DBG_CHECK_ME();
528 OSL_PRECOND( i_last >= i_first, "TableControl_Impl::rowsInserted: invalid row indexes!" );
530 TableSize const insertedRows = i_last - i_first + 1;
532 // adjust selection, if necessary
533 bool const selectionChanged = lcl_adjustSelectedRows( m_aSelectedRows, i_first, insertedRows );
535 // adjust our cached row count
536 m_nRowCount = m_pModel->getRowCount();
538 // if the rows have been inserted before the current row, adjust this
539 if ( i_first <= m_nCurRow )
540 goTo( m_nCurColumn, m_nCurRow + insertedRows );
542 // relayout, since the scrollbar need might have changed
543 impl_ni_relayout();
545 // notify A1YY events
546 if ( impl_isAccessibleAlive() )
548 impl_commitAccessibleEvent( AccessibleEventId::TABLE_MODEL_CHANGED,
549 makeAny( AccessibleTableModelChange( AccessibleTableModelChangeType::INSERT, i_first, i_last, 0, m_pModel->getColumnCount() ) ),
550 Any()
554 // schedule repaint
555 invalidateRowRange( i_first, ROW_INVALID );
557 // call selection handlers, if necessary
558 if ( selectionChanged )
559 m_rAntiImpl.Select();
562 //------------------------------------------------------------------------------------------------------------------
563 void TableControl_Impl::rowsRemoved( RowPos i_first, RowPos i_last )
565 sal_Int32 firstRemovedRow = i_first;
566 sal_Int32 lastRemovedRow = i_last;
568 // adjust selection, if necessary
569 bool selectionChanged = false;
570 if ( i_first == -1 )
572 selectionChanged = markAllRowsAsDeselected();
574 firstRemovedRow = 0;
575 lastRemovedRow = m_nRowCount - 1;
577 else
579 ENSURE_OR_RETURN_VOID( i_last >= i_first, "TableControl_Impl::rowsRemoved: illegal indexes!" );
581 for ( sal_Int32 row = i_first; row <= i_last; ++row )
583 if ( markRowAsDeselected( row ) )
584 selectionChanged = true;
587 if ( lcl_adjustSelectedRows( m_aSelectedRows, i_last + 1, i_first - i_last - 1 ) )
588 selectionChanged = true;
591 // adjust cached row count
592 m_nRowCount = m_pModel->getRowCount();
594 // adjust the current row, if it is larger than the row count now
595 if ( m_nCurRow >= m_nRowCount )
597 if ( m_nRowCount > 0 )
598 goTo( m_nCurColumn, m_nRowCount - 1 );
599 else
601 m_nCurRow = ROW_INVALID;
602 m_nTopRow = 0;
605 else if ( m_nRowCount == 0 )
607 m_nTopRow = 0;
611 // relayout, since the scrollbar need might have changed
612 impl_ni_relayout();
614 // notify A11Y events
615 if ( impl_isAccessibleAlive() )
617 commitTableEvent(
618 AccessibleEventId::TABLE_MODEL_CHANGED,
619 makeAny( AccessibleTableModelChange(
620 AccessibleTableModelChangeType::DELETE,
621 firstRemovedRow,
622 lastRemovedRow,
624 m_pModel->getColumnCount()
625 ) ),
626 Any()
630 // schedule a repaint
631 invalidateRowRange( firstRemovedRow, ROW_INVALID );
633 // call selection handlers, if necessary
634 if ( selectionChanged )
635 m_rAntiImpl.Select();
638 //------------------------------------------------------------------------------------------------------------------
639 void TableControl_Impl::columnInserted( ColPos const i_colIndex )
641 m_nColumnCount = m_pModel->getColumnCount();
642 impl_ni_relayout();
644 m_rAntiImpl.Invalidate();
646 OSL_UNUSED( i_colIndex );
649 //------------------------------------------------------------------------------------------------------------------
650 void TableControl_Impl::columnRemoved( ColPos const i_colIndex )
652 m_nColumnCount = m_pModel->getColumnCount();
654 // adjust the current column, if it is larger than the column count now
655 if ( m_nCurColumn >= m_nColumnCount )
657 if ( m_nColumnCount > 0 )
658 goTo( m_nCurColumn - 1, m_nCurRow );
659 else
660 m_nCurColumn = COL_INVALID;
663 impl_ni_relayout();
665 m_rAntiImpl.Invalidate();
667 OSL_UNUSED( i_colIndex );
670 //------------------------------------------------------------------------------------------------------------------
671 void TableControl_Impl::allColumnsRemoved()
673 m_nColumnCount = m_pModel->getColumnCount();
674 impl_ni_relayout();
676 m_rAntiImpl.Invalidate();
679 //------------------------------------------------------------------------------------------------------------------
680 void TableControl_Impl::cellsUpdated( ColPos const i_firstCol, ColPos i_lastCol, RowPos const i_firstRow, RowPos const i_lastRow )
682 invalidateRowRange( i_firstRow, i_lastRow );
684 OSL_UNUSED( i_firstCol );
685 OSL_UNUSED( i_lastCol );
688 //------------------------------------------------------------------------------------------------------------------
689 void TableControl_Impl::tableMetricsChanged()
691 impl_ni_updateCachedTableMetrics();
692 impl_ni_relayout();
693 m_rAntiImpl.Invalidate();
696 //------------------------------------------------------------------------------------------------------------------
697 void TableControl_Impl::impl_invalidateColumn( ColPos const i_column )
699 DBG_CHECK_ME();
701 Rectangle const aAllCellsArea( impl_getAllVisibleCellsArea() );
703 const TableColumnGeometry aColumn( *this, aAllCellsArea, i_column );
704 if ( aColumn.isValid() )
705 m_rAntiImpl.Invalidate( aColumn.getRect() );
708 //------------------------------------------------------------------------------------------------------------------
709 void TableControl_Impl::columnChanged( ColPos const i_column, ColumnAttributeGroup const i_attributeGroup )
711 ColumnAttributeGroup nGroup( i_attributeGroup );
712 if ( nGroup & COL_ATTRS_APPEARANCE )
714 impl_invalidateColumn( i_column );
715 nGroup &= ~COL_ATTRS_APPEARANCE;
718 if ( nGroup & COL_ATTRS_WIDTH )
720 if ( !m_bUpdatingColWidths )
722 impl_ni_relayout( i_column );
723 invalidate( TableAreaAll );
726 nGroup &= ~COL_ATTRS_WIDTH;
729 OSL_ENSURE( ( nGroup == COL_ATTRS_NONE ) || ( i_attributeGroup == COL_ATTRS_ALL ),
730 "TableControl_Impl::columnChanged: don't know how to handle this change!" );
733 //------------------------------------------------------------------------------------------------------------------
734 Rectangle TableControl_Impl::impl_getAllVisibleCellsArea() const
736 DBG_CHECK_ME();
738 Rectangle aArea( Point( 0, 0 ), Size( 0, 0 ) );
740 // determine the right-most border of the last column which is
741 // at least partially visible
742 aArea.Right() = m_nRowHeaderWidthPixel;
743 if ( !m_aColumnWidths.empty() )
745 // the number of pixels which are scrolled out of the left hand
746 // side of the window
747 const long nScrolledOutLeft = m_nLeftColumn == 0 ? 0 : m_aColumnWidths[ m_nLeftColumn - 1 ].getEnd();
749 ColumnPositions::const_reverse_iterator loop = m_aColumnWidths.rbegin();
752 aArea.Right() = loop->getEnd() - nScrolledOutLeft + m_nRowHeaderWidthPixel;
753 ++loop;
755 while ( ( loop != m_aColumnWidths.rend() )
756 && ( loop->getEnd() - nScrolledOutLeft >= aArea.Right() )
759 // so far, aArea.Right() denotes the first pixel *after* the cell area
760 --aArea.Right();
762 // determine the last row which is at least partially visible
763 aArea.Bottom() =
764 m_nColHeaderHeightPixel
765 + impl_getVisibleRows( true ) * m_nRowHeightPixel
766 - 1;
768 return aArea;
771 //------------------------------------------------------------------------------------------------------------------
772 Rectangle TableControl_Impl::impl_getAllVisibleDataCellArea() const
774 DBG_CHECK_ME();
776 Rectangle aArea( impl_getAllVisibleCellsArea() );
777 aArea.Left() = m_nRowHeaderWidthPixel;
778 aArea.Top() = m_nColHeaderHeightPixel;
779 return aArea;
782 //------------------------------------------------------------------------------------------------------------------
783 void TableControl_Impl::impl_ni_updateCachedTableMetrics()
785 m_nRowHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getRowHeight() ), MAP_APPFONT ).Height();
787 m_nColHeaderHeightPixel = 0;
788 if ( m_pModel->hasColumnHeaders() )
789 m_nColHeaderHeightPixel = m_rAntiImpl.LogicToPixel( Size( 0, m_pModel->getColumnHeaderHeight() ), MAP_APPFONT ).Height();
791 m_nRowHeaderWidthPixel = 0;
792 if ( m_pModel->hasRowHeaders() )
793 m_nRowHeaderWidthPixel = m_rAntiImpl.LogicToPixel( Size( m_pModel->getRowHeaderWidth(), 0 ), MAP_APPFONT).Width();
796 //------------------------------------------------------------------------------------------------------------------
797 void TableControl_Impl::impl_ni_updateCachedModelValues()
799 m_pInputHandler = m_pModel->getInputHandler();
800 if ( !m_pInputHandler )
801 m_pInputHandler.reset( new DefaultInputHandler );
803 m_nColumnCount = m_pModel->getColumnCount();
804 if ( m_nLeftColumn >= m_nColumnCount )
805 m_nLeftColumn = ( m_nColumnCount > 0 ) ? m_nColumnCount - 1 : 0;
807 m_nRowCount = m_pModel->getRowCount();
808 if ( m_nTopRow >= m_nRowCount )
809 m_nTopRow = ( m_nRowCount > 0 ) ? m_nRowCount - 1 : 0;
811 impl_ni_updateCachedTableMetrics();
814 //------------------------------------------------------------------------------------------------------------------
815 namespace
817 //..............................................................................................................
818 /// determines whether a scrollbar is needed for the given values
819 bool lcl_determineScrollbarNeed( long const i_position, ScrollbarVisibility const i_visibility,
820 long const i_availableSpace, long const i_neededSpace )
822 if ( i_visibility == ScrollbarShowNever )
823 return false;
824 if ( i_visibility == ScrollbarShowAlways )
825 return true;
826 if ( i_position > 0 )
827 return true;
828 if ( i_availableSpace >= i_neededSpace )
829 return false;
830 return true;
833 //..............................................................................................................
834 void lcl_setButtonRepeat( Window& _rWindow, sal_uLong _nDelay )
836 AllSettings aSettings = _rWindow.GetSettings();
837 MouseSettings aMouseSettings = aSettings.GetMouseSettings();
839 aMouseSettings.SetButtonRepeat( _nDelay );
840 aSettings.SetMouseSettings( aMouseSettings );
842 _rWindow.SetSettings( aSettings, sal_True );
845 //..............................................................................................................
846 bool lcl_updateScrollbar( Window& _rParent, ScrollBar*& _rpBar,
847 bool const i_needBar, long _nVisibleUnits,
848 long _nPosition, long _nLineSize, long _nRange,
849 bool _bHorizontal, const Link& _rScrollHandler )
851 // do we currently have the scrollbar?
852 bool bHaveBar = _rpBar != NULL;
854 // do we need to correct the scrollbar visibility?
855 if ( bHaveBar && !i_needBar )
857 if ( _rpBar->IsTracking() )
858 _rpBar->EndTracking();
859 DELETEZ( _rpBar );
861 else if ( !bHaveBar && i_needBar )
863 _rpBar = new ScrollBar(
864 &_rParent,
865 WB_DRAG | ( _bHorizontal ? WB_HSCROLL : WB_VSCROLL )
867 _rpBar->SetScrollHdl( _rScrollHandler );
868 // get some speed into the scrolling ....
869 lcl_setButtonRepeat( *_rpBar, 0 );
872 if ( _rpBar )
874 _rpBar->SetRange( Range( 0, _nRange ) );
875 _rpBar->SetVisibleSize( _nVisibleUnits );
876 _rpBar->SetPageSize( _nVisibleUnits );
877 _rpBar->SetLineSize( _nLineSize );
878 _rpBar->SetThumbPos( _nPosition );
879 _rpBar->Show();
882 return ( bHaveBar != i_needBar );
885 //..............................................................................................................
886 /** returns the number of rows fitting into the given range,
887 for the given row height. Partially fitting rows are counted, too, if the
888 respective parameter says so.
890 TableSize lcl_getRowsFittingInto( long _nOverallHeight, long _nRowHeightPixel, bool _bAcceptPartialRow = false )
892 return _bAcceptPartialRow
893 ? ( _nOverallHeight + ( _nRowHeightPixel - 1 ) ) / _nRowHeightPixel
894 : _nOverallHeight / _nRowHeightPixel;
897 //..............................................................................................................
898 /** returns the number of columns fitting into the given area,
899 with the first visible column as given. Partially fitting columns are counted, too,
900 if the respective parameter says so.
902 TableSize lcl_getColumnsVisibleWithin( const Rectangle& _rArea, ColPos _nFirstVisibleColumn,
903 const TableControl_Impl& _rControl, bool _bAcceptPartialRow )
905 TableSize visibleColumns = 0;
906 TableColumnGeometry aColumn( _rControl, _rArea, _nFirstVisibleColumn );
907 while ( aColumn.isValid() )
909 if ( !_bAcceptPartialRow )
910 if ( aColumn.getRect().Right() > _rArea.Right() )
911 // this column is only partially visible, and this is not allowed
912 break;
914 aColumn.moveRight();
915 ++visibleColumns;
917 return visibleColumns;
922 //------------------------------------------------------------------------------------------------------------------
923 long TableControl_Impl::impl_ni_calculateColumnWidths( ColPos const i_assumeInflexibleColumnsUpToIncluding,
924 bool const i_assumeVerticalScrollbar, ::std::vector< long >& o_newColWidthsPixel ) const
926 // the available horizontal space
927 long gridWidthPixel = m_rAntiImpl.GetOutputSizePixel().Width();
928 ENSURE_OR_RETURN( !!m_pModel, "TableControl_Impl::impl_ni_calculateColumnWidths: not allowed without a model!", gridWidthPixel );
929 if ( m_pModel->hasRowHeaders() && ( gridWidthPixel != 0 ) )
931 gridWidthPixel -= m_nRowHeaderWidthPixel;
934 if ( i_assumeVerticalScrollbar && ( m_pModel->getVerticalScrollbarVisibility() != ScrollbarShowNever ) )
936 long nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
937 gridWidthPixel -= nScrollbarMetrics;
940 // no need to do anything without columns
941 TableSize const colCount = m_pModel->getColumnCount();
942 if ( colCount == 0 )
943 return gridWidthPixel;
945 // collect some meta data for our columns:
946 // - their current (pixel) metrics
947 long accumulatedCurrentWidth = 0;
948 ::std::vector< long > currentColWidths;
949 currentColWidths.reserve( colCount );
950 typedef ::std::vector< ::std::pair< long, long > > ColumnLimits;
951 ColumnLimits effectiveColumnLimits;
952 effectiveColumnLimits.reserve( colCount );
953 long accumulatedMinWidth = 0;
954 long accumulatedMaxWidth = 0;
955 // - their relative flexibility
956 ::std::vector< ::sal_Int32 > columnFlexibilities;
957 columnFlexibilities.reserve( colCount );
958 long flexibilityDenominator = 0;
959 size_t flexibleColumnCount = 0;
960 for ( ColPos col = 0; col < colCount; ++col )
962 PColumnModel const pColumn = m_pModel->getColumnModel( col );
963 ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
965 // current width
966 long const currentWidth = appFontWidthToPixel( pColumn->getWidth() );
967 currentColWidths.push_back( currentWidth );
969 // accumulated width
970 accumulatedCurrentWidth += currentWidth;
972 // flexibility
973 ::sal_Int32 flexibility = pColumn->getFlexibility();
974 OSL_ENSURE( flexibility >= 0, "TableControl_Impl::impl_ni_calculateColumnWidths: a column's flexibility should be non-negative." );
975 if ( ( flexibility < 0 ) // normalization
976 || ( !pColumn->isResizable() ) // column not resizable => no auto-resize
977 || ( col <= i_assumeInflexibleColumnsUpToIncluding ) // column shall be treated as inflexible => respec this
979 flexibility = 0;
981 // min/max width
982 long effectiveMin = currentWidth, effectiveMax = currentWidth;
983 // if the column is not flexible, it will not be asked for min/max, but we assume the current width as limit then
984 if ( flexibility > 0 )
986 long const minWidth = appFontWidthToPixel( pColumn->getMinWidth() );
987 if ( minWidth > 0 )
988 effectiveMin = minWidth;
989 else
990 effectiveMin = MIN_COLUMN_WIDTH_PIXEL;
992 long const maxWidth = appFontWidthToPixel( pColumn->getMaxWidth() );
993 OSL_ENSURE( minWidth <= maxWidth, "TableControl_Impl::impl_ni_calculateColumnWidths: pretty undecided 'bout its width limits, this column!" );
994 if ( ( maxWidth > 0 ) && ( maxWidth >= minWidth ) )
995 effectiveMax = maxWidth;
996 else
997 effectiveMax = gridWidthPixel; // TODO: any better guess here?
999 if ( effectiveMin == effectiveMax )
1000 // if the min and the max are identical, this implies no flexibility at all
1001 flexibility = 0;
1004 columnFlexibilities.push_back( flexibility );
1005 flexibilityDenominator += flexibility;
1006 if ( flexibility > 0 )
1007 ++flexibleColumnCount;
1009 effectiveColumnLimits.push_back( ::std::pair< long, long >( effectiveMin, effectiveMax ) );
1010 accumulatedMinWidth += effectiveMin;
1011 accumulatedMaxWidth += effectiveMax;
1014 o_newColWidthsPixel = currentColWidths;
1015 if ( flexibilityDenominator == 0 )
1017 // no column is flexible => don't adjust anything
1019 else if ( gridWidthPixel > accumulatedCurrentWidth )
1020 { // we have space to give away ...
1021 long distributePixel = gridWidthPixel - accumulatedCurrentWidth;
1022 if ( gridWidthPixel > accumulatedMaxWidth )
1024 // ... but the column's maximal widths are still less than we have
1025 // => set them all to max
1026 for ( size_t i = 0; i < size_t( colCount ); ++i )
1028 o_newColWidthsPixel[i] = effectiveColumnLimits[i].second;
1031 else
1033 bool startOver = false;
1036 startOver = false;
1037 // distribute the remaining space amongst all columns with a positive flexibility
1038 for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
1040 long const columnFlexibility = columnFlexibilities[i];
1041 if ( columnFlexibility == 0 )
1042 continue;
1044 long newColWidth = currentColWidths[i] + columnFlexibility * distributePixel / flexibilityDenominator;
1046 if ( newColWidth > effectiveColumnLimits[i].second )
1047 { // that was too much, we hit the col's maximum
1048 // set the new width to exactly this maximum
1049 newColWidth = effectiveColumnLimits[i].second;
1050 // adjust the flexibility denominator ...
1051 flexibilityDenominator -= columnFlexibility;
1052 columnFlexibilities[i] = 0;
1053 --flexibleColumnCount;
1054 // ... and the remaining width ...
1055 long const difference = newColWidth - currentColWidths[i];
1056 distributePixel -= difference;
1057 // ... this way, we ensure that the width not taken up by this column is consumed by the other
1058 // flexible ones (if there are some)
1060 // and start over with the first column, since there might be earlier columns which need
1061 // to be recalculated now
1062 startOver = true;
1065 o_newColWidthsPixel[i] = newColWidth;
1068 while ( startOver );
1070 // are there pixels left (might be caused by rounding errors)?
1071 distributePixel = gridWidthPixel - ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 );
1072 while ( ( distributePixel > 0 ) && ( flexibleColumnCount > 0 ) )
1074 // yes => ignore relative flexibilities, and subsequently distribute single pixels to all flexible
1075 // columns which did not yet reach their maximum.
1076 for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( distributePixel > 0 ); ++i )
1078 if ( columnFlexibilities[i] == 0 )
1079 continue;
1081 OSL_ENSURE( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].second,
1082 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
1083 if ( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first )
1085 columnFlexibilities[i] = 0;
1086 --flexibleColumnCount;
1087 continue;
1090 ++o_newColWidthsPixel[i];
1091 --distributePixel;
1096 else if ( gridWidthPixel < accumulatedCurrentWidth )
1097 { // we need to take away some space from the columns which allow it ...
1098 long takeAwayPixel = accumulatedCurrentWidth - gridWidthPixel;
1099 if ( gridWidthPixel < accumulatedMinWidth )
1101 // ... but the column's minimal widths are still more than we have
1102 // => set them all to min
1103 for ( size_t i = 0; i < size_t( colCount ); ++i )
1105 o_newColWidthsPixel[i] = effectiveColumnLimits[i].first;
1108 else
1110 bool startOver = false;
1113 startOver = false;
1114 // take away the space we need from the columns with a positive flexibility
1115 for ( size_t i=0; i<o_newColWidthsPixel.size() && !startOver; ++i )
1117 long const columnFlexibility = columnFlexibilities[i];
1118 if ( columnFlexibility == 0 )
1119 continue;
1121 long newColWidth = currentColWidths[i] - columnFlexibility * takeAwayPixel / flexibilityDenominator;
1123 if ( newColWidth < effectiveColumnLimits[i].first )
1124 { // that was too much, we hit the col's minimum
1125 // set the new width to exactly this minimum
1126 newColWidth = effectiveColumnLimits[i].first;
1127 // adjust the flexibility denominator ...
1128 flexibilityDenominator -= columnFlexibility;
1129 columnFlexibilities[i] = 0;
1130 --flexibleColumnCount;
1131 // ... and the remaining width ...
1132 long const difference = currentColWidths[i] - newColWidth;
1133 takeAwayPixel -= difference;
1135 // and start over with the first column, since there might be earlier columns which need
1136 // to be recalculated now
1137 startOver = true;
1140 o_newColWidthsPixel[i] = newColWidth;
1143 while ( startOver );
1145 // are there pixels left (might be caused by rounding errors)?
1146 takeAwayPixel = ::std::accumulate( o_newColWidthsPixel.begin(), o_newColWidthsPixel.end(), 0 ) - gridWidthPixel;
1147 while ( ( takeAwayPixel > 0 ) && ( flexibleColumnCount > 0 ) )
1149 // yes => ignore relative flexibilities, and subsequently take away pixels from all flexible
1150 // columns which did not yet reach their minimum.
1151 for ( size_t i=0; ( i < o_newColWidthsPixel.size() ) && ( takeAwayPixel > 0 ); ++i )
1153 if ( columnFlexibilities[i] == 0 )
1154 continue;
1156 OSL_ENSURE( o_newColWidthsPixel[i] >= effectiveColumnLimits[i].first,
1157 "TableControl_Impl::impl_ni_calculateColumnWidths: inconsitency!" );
1158 if ( o_newColWidthsPixel[i] <= effectiveColumnLimits[i].first )
1160 columnFlexibilities[i] = 0;
1161 --flexibleColumnCount;
1162 continue;
1165 --o_newColWidthsPixel[i];
1166 --takeAwayPixel;
1172 return gridWidthPixel;
1175 //------------------------------------------------------------------------------------------------------------------
1176 void TableControl_Impl::impl_ni_relayout( ColPos const i_assumeInflexibleColumnsUpToIncluding )
1178 ENSURE_OR_RETURN_VOID( !m_bUpdatingColWidths, "TableControl_Impl::impl_ni_relayout: recursive call detected!" );
1180 m_aColumnWidths.resize( 0 );
1181 if ( !m_pModel )
1182 return;
1184 ::comphelper::FlagRestorationGuard const aWidthUpdateFlag( m_bUpdatingColWidths, true );
1185 SuppressCursor aHideCursor( *this );
1187 // layouting steps:
1189 // 1. adjust column widths, leaving space for a vertical scrollbar
1190 // 2. determine need for a vertical scrollbar
1191 // - V-YES: all fine, result from 1. is still valid
1192 // - V-NO: result from 1. is still under consideration
1194 // 3. determine need for a horizontal scrollbar
1195 // - H-NO: all fine, result from 2. is still valid
1196 // - H-YES: reconsider need for a vertical scrollbar, if result of 2. was V-NO
1197 // - V-YES: all fine, result from 1. is still valid
1198 // - V-NO: redistribute the remaining space (if any) amongst all columns which allow it
1200 ::std::vector< long > newWidthsPixel;
1201 long gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, true, newWidthsPixel );
1203 // the width/height of a scrollbar, needed several times below
1204 long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
1206 // determine the playground for the data cells (excluding headers)
1207 // TODO: what if the control is smaller than needed for the headers/scrollbars?
1208 Rectangle aDataCellPlayground( Point( 0, 0 ), m_rAntiImpl.GetOutputSizePixel() );
1209 aDataCellPlayground.Left() = m_nRowHeaderWidthPixel;
1210 aDataCellPlayground.Top() = m_nColHeaderHeightPixel;
1212 OSL_ENSURE( ( m_nRowCount == m_pModel->getRowCount() ) && ( m_nColumnCount == m_pModel->getColumnCount() ),
1213 "TableControl_Impl::impl_ni_relayout: how is this expected to work with invalid data?" );
1214 long const nAllColumnsWidth = ::std::accumulate( newWidthsPixel.begin(), newWidthsPixel.end(), 0 );
1216 ScrollbarVisibility const eVertScrollbar = m_pModel->getVerticalScrollbarVisibility();
1217 ScrollbarVisibility const eHorzScrollbar = m_pModel->getHorizontalScrollbarVisibility();
1219 // do we need a vertical scrollbar?
1220 bool bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
1221 m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount );
1222 bool bFirstRoundVScrollNeed = false;
1223 if ( bNeedVerticalScrollbar )
1225 aDataCellPlayground.Right() -= nScrollbarMetrics;
1226 bFirstRoundVScrollNeed = true;
1229 // do we need a horizontal scrollbar?
1230 bool const bNeedHorizontalScrollbar = lcl_determineScrollbarNeed(
1231 m_nLeftColumn, eHorzScrollbar, aDataCellPlayground.GetWidth(), nAllColumnsWidth );
1232 if ( bNeedHorizontalScrollbar )
1234 aDataCellPlayground.Bottom() -= nScrollbarMetrics;
1236 // now that we just found that we need a horizontal scrollbar,
1237 // the need for a vertical one may have changed, since the horizontal
1238 // SB might just occupy enough space so that not all rows do fit
1239 // anymore
1240 if ( !bFirstRoundVScrollNeed )
1242 bNeedVerticalScrollbar = lcl_determineScrollbarNeed(
1243 m_nTopRow, eVertScrollbar, aDataCellPlayground.GetHeight(), m_nRowHeightPixel * m_nRowCount );
1244 if ( bNeedVerticalScrollbar )
1246 aDataCellPlayground.Right() -= nScrollbarMetrics;
1251 // the initial call to impl_ni_calculateColumnWidths assumed that we need a vertical scrollbar. If, by now,
1252 // we know that this is not the case, re-calculate the column widths.
1253 if ( !bNeedVerticalScrollbar )
1254 gridWidthPixel = impl_ni_calculateColumnWidths( i_assumeInflexibleColumnsUpToIncluding, false, newWidthsPixel );
1256 // update the column objects with the new widths we finally calculated
1257 TableSize const colCount = m_pModel->getColumnCount();
1258 m_aColumnWidths.reserve( colCount );
1259 long accumulatedWidthPixel = m_nRowHeaderWidthPixel;
1260 bool anyColumnWidthChanged = false;
1261 for ( ColPos col = 0; col < colCount; ++col )
1263 const long columnStart = accumulatedWidthPixel;
1264 const long columnEnd = columnStart + newWidthsPixel[col];
1265 m_aColumnWidths.push_back( MutableColumnMetrics( columnStart, columnEnd ) );
1266 accumulatedWidthPixel = columnEnd;
1268 // and don't forget to forward this to the column models
1269 PColumnModel const pColumn = m_pModel->getColumnModel( col );
1270 ENSURE_OR_THROW( !!pColumn, "invalid column returned by the model!" );
1272 long const oldColumnWidthAppFont = pColumn->getWidth();
1273 long const newColumnWidthAppFont = pixelWidthToAppFont( newWidthsPixel[col] );
1274 pColumn->setWidth( newColumnWidthAppFont );
1276 anyColumnWidthChanged |= ( oldColumnWidthAppFont != newColumnWidthAppFont );
1279 // if the column widths changed, ensure everything is repainted
1280 if ( anyColumnWidthChanged )
1281 invalidate( TableAreaAll );
1283 // if the column resizing happened to leave some space at the right, but there are columns
1284 // scrolled out to the left, scroll them in
1285 while ( ( m_nLeftColumn > 0 )
1286 && ( accumulatedWidthPixel - m_aColumnWidths[ m_nLeftColumn - 1 ].getStart() <= gridWidthPixel )
1289 --m_nLeftColumn;
1292 // now adjust the column metrics, since they currently ignore the horizontal scroll position
1293 if ( m_nLeftColumn > 0 )
1295 const long offsetPixel = m_aColumnWidths[ 0 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getStart();
1296 for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin();
1297 colPos != m_aColumnWidths.end();
1298 ++colPos
1301 colPos->move( offsetPixel );
1305 // show or hide the scrollbars as needed, and position the data window
1306 impl_ni_positionChildWindows( aDataCellPlayground, bNeedVerticalScrollbar, bNeedHorizontalScrollbar );
1309 //------------------------------------------------------------------------------------------------------------------
1310 void TableControl_Impl::impl_ni_positionChildWindows( Rectangle const & i_dataCellPlayground,
1311 bool const i_verticalScrollbar, bool const i_horizontalScrollbar )
1313 long const nScrollbarMetrics = m_rAntiImpl.GetSettings().GetStyleSettings().GetScrollBarSize();
1315 // create or destroy the vertical scrollbar, as needed
1316 lcl_updateScrollbar(
1317 m_rAntiImpl,
1318 m_pVScroll,
1319 i_verticalScrollbar,
1320 lcl_getRowsFittingInto( i_dataCellPlayground.GetHeight(), m_nRowHeightPixel ),
1321 // visible units
1322 m_nTopRow, // current position
1323 1, // line size
1324 m_nRowCount, // range
1325 false, // vertical
1326 LINK( this, TableControl_Impl, OnScroll ) // scroll handler
1329 // position it
1330 if ( m_pVScroll )
1332 Rectangle aScrollbarArea(
1333 Point( i_dataCellPlayground.Right() + 1, 0 ),
1334 Size( nScrollbarMetrics, i_dataCellPlayground.Bottom() + 1 )
1336 m_pVScroll->SetPosSizePixel(
1337 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
1340 // create or destroy the horizontal scrollbar, as needed
1341 lcl_updateScrollbar(
1342 m_rAntiImpl,
1343 m_pHScroll,
1344 i_horizontalScrollbar,
1345 lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false ),
1346 // visible units
1347 m_nLeftColumn, // current position
1348 1, // line size
1349 m_nColumnCount, // range
1350 true, // horizontal
1351 LINK( this, TableControl_Impl, OnScroll ) // scroll handler
1354 // position it
1355 if ( m_pHScroll )
1357 TableSize const nVisibleUnits = lcl_getColumnsVisibleWithin( i_dataCellPlayground, m_nLeftColumn, *this, false );
1358 TableMetrics const nRange = m_nColumnCount;
1359 if( m_nLeftColumn + nVisibleUnits == nRange - 1 )
1361 if ( m_aColumnWidths[ nRange - 1 ].getStart() - m_aColumnWidths[ m_nLeftColumn ].getEnd() + m_aColumnWidths[ nRange-1 ].getWidth() > i_dataCellPlayground.GetWidth() )
1363 m_pHScroll->SetVisibleSize( nVisibleUnits -1 );
1364 m_pHScroll->SetPageSize( nVisibleUnits - 1 );
1367 Rectangle aScrollbarArea(
1368 Point( 0, i_dataCellPlayground.Bottom() + 1 ),
1369 Size( i_dataCellPlayground.Right() + 1, nScrollbarMetrics )
1371 m_pHScroll->SetPosSizePixel(
1372 aScrollbarArea.TopLeft(), aScrollbarArea.GetSize() );
1375 // the corner window connecting the two scrollbars in the lower right corner
1376 bool bHaveScrollCorner = NULL != m_pScrollCorner;
1377 bool bNeedScrollCorner = ( NULL != m_pHScroll ) && ( NULL != m_pVScroll );
1378 if ( bHaveScrollCorner && !bNeedScrollCorner )
1380 DELETEZ( m_pScrollCorner );
1382 else if ( !bHaveScrollCorner && bNeedScrollCorner )
1384 m_pScrollCorner = new ScrollBarBox( &m_rAntiImpl );
1385 m_pScrollCorner->SetSizePixel( Size( nScrollbarMetrics, nScrollbarMetrics ) );
1386 m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
1387 m_pScrollCorner->Show();
1389 else if(bHaveScrollCorner && bNeedScrollCorner)
1391 m_pScrollCorner->SetPosPixel( Point( i_dataCellPlayground.Right() + 1, i_dataCellPlayground.Bottom() + 1 ) );
1392 m_pScrollCorner->Show();
1395 // resize the data window
1396 m_pDataWindow->SetSizePixel( Size(
1397 i_dataCellPlayground.GetWidth() + m_nRowHeaderWidthPixel,
1398 i_dataCellPlayground.GetHeight() + m_nColHeaderHeightPixel
1399 ) );
1402 //------------------------------------------------------------------------------------------------------------------
1403 void TableControl_Impl::onResize()
1405 DBG_CHECK_ME();
1407 impl_ni_relayout();
1408 checkCursorPosition();
1411 //------------------------------------------------------------------------------------------------------------------
1412 void TableControl_Impl::doPaintContent( const Rectangle& _rUpdateRect )
1414 DBG_CHECK_ME();
1416 if ( !getModel() )
1417 return;
1418 PTableRenderer pRenderer = getModel()->getRenderer();
1419 DBG_ASSERT( !!pRenderer, "TableDataWindow::doPaintContent: invalid renderer!" );
1420 if ( !pRenderer )
1421 return;
1423 // our current style settings, to be passed to the renderer
1424 const StyleSettings& rStyle = m_rAntiImpl.GetSettings().GetStyleSettings();
1425 m_nRowCount = m_pModel->getRowCount();
1426 // the area occupied by all (at least partially) visible cells, including
1427 // headers
1428 Rectangle const aAllCellsWithHeaders( impl_getAllVisibleCellsArea() );
1430 // ............................
1431 // draw the header column area
1432 if ( m_pModel->hasColumnHeaders() )
1434 TableRowGeometry const aHeaderRow( *this, Rectangle( Point( 0, 0 ),
1435 aAllCellsWithHeaders.BottomRight() ), ROW_COL_HEADERS );
1436 Rectangle const aColRect(aHeaderRow.getRect());
1437 pRenderer->PaintHeaderArea(
1438 *m_pDataWindow, aColRect, true, false, rStyle
1440 // Note that strictly, aHeaderRow.getRect() also contains the intersection between column
1441 // and row header area. However, below we go to paint this intersection, again,
1442 // so this hopefully doesn't hurt if we already paint it here.
1444 for ( TableCellGeometry aCell( aHeaderRow, m_nLeftColumn );
1445 aCell.isValid();
1446 aCell.moveRight()
1449 if ( _rUpdateRect.GetIntersection( aCell.getRect() ).IsEmpty() )
1450 continue;
1452 bool isActiveColumn = ( aCell.getColumn() == getCurrentColumn() );
1453 bool isSelectedColumn = false;
1454 pRenderer->PaintColumnHeader( aCell.getColumn(), isActiveColumn, isSelectedColumn,
1455 *m_pDataWindow, aCell.getRect(), rStyle );
1458 // the area occupied by the row header, if any
1459 Rectangle aRowHeaderArea;
1460 if ( m_pModel->hasRowHeaders() )
1462 aRowHeaderArea = aAllCellsWithHeaders;
1463 aRowHeaderArea.Right() = m_nRowHeaderWidthPixel - 1;
1465 TableSize const nVisibleRows = impl_getVisibleRows( true );
1466 TableSize nActualRows = nVisibleRows;
1467 if ( m_nTopRow + nActualRows > m_nRowCount )
1468 nActualRows = m_nRowCount - m_nTopRow;
1469 aRowHeaderArea.Bottom() = m_nColHeaderHeightPixel + m_nRowHeightPixel * nActualRows - 1;
1471 pRenderer->PaintHeaderArea( *m_pDataWindow, aRowHeaderArea, false, true, rStyle );
1472 // Note that strictly, aRowHeaderArea also contains the intersection between column
1473 // and row header area. However, below we go to paint this intersection, again,
1474 // so this hopefully doesn't hurt if we already paint it here.
1476 if ( m_pModel->hasColumnHeaders() )
1478 TableCellGeometry const aIntersection( *this, Rectangle( Point( 0, 0 ),
1479 aAllCellsWithHeaders.BottomRight() ), COL_ROW_HEADERS, ROW_COL_HEADERS );
1480 Rectangle const aInters( aIntersection.getRect() );
1481 pRenderer->PaintHeaderArea(
1482 *m_pDataWindow, aInters, true, true, rStyle
1487 // ............................
1488 // draw the table content row by row
1490 TableSize colCount = getModel()->getColumnCount();
1492 // paint all rows
1493 Rectangle const aAllDataCellsArea( impl_getAllVisibleDataCellArea() );
1494 for ( TableRowGeometry aRowIterator( *this, aAllCellsWithHeaders, getTopRow() );
1495 aRowIterator.isValid();
1496 aRowIterator.moveDown() )
1498 if ( _rUpdateRect.GetIntersection( aRowIterator.getRect() ).IsEmpty() )
1499 continue;
1501 bool const isControlFocused = m_rAntiImpl.HasControlFocus();
1502 bool const isSelectedRow = isRowSelected( aRowIterator.getRow() );
1504 Rectangle const aRect = aRowIterator.getRect().GetIntersection( aAllDataCellsArea );
1506 // give the redenderer a chance to prepare the row
1507 pRenderer->PrepareRow(
1508 aRowIterator.getRow(), isControlFocused, isSelectedRow,
1509 *m_pDataWindow, aRect, rStyle
1512 // paint the row header
1513 if ( m_pModel->hasRowHeaders() )
1515 const Rectangle aCurrentRowHeader( aRowHeaderArea.GetIntersection( aRowIterator.getRect() ) );
1516 pRenderer->PaintRowHeader( isControlFocused, isSelectedRow, *m_pDataWindow, aCurrentRowHeader,
1517 rStyle );
1520 if ( !colCount )
1521 continue;
1523 // paint all cells in this row
1524 for ( TableCellGeometry aCell( aRowIterator, m_nLeftColumn );
1525 aCell.isValid();
1526 aCell.moveRight()
1529 bool isSelectedColumn = false;
1530 pRenderer->PaintCell( aCell.getColumn(), isSelectedRow || isSelectedColumn, isControlFocused,
1531 *m_pDataWindow, aCell.getRect(), rStyle );
1535 //------------------------------------------------------------------------------------------------------------------
1536 void TableControl_Impl::hideCursor()
1538 DBG_CHECK_ME();
1540 if ( ++m_nCursorHidden == 1 )
1541 impl_ni_doSwitchCursor( false );
1544 //------------------------------------------------------------------------------------------------------------------
1545 void TableControl_Impl::showCursor()
1547 DBG_CHECK_ME();
1549 DBG_ASSERT( m_nCursorHidden > 0, "TableControl_Impl::showCursor: cursor not hidden!" );
1550 if ( --m_nCursorHidden == 0 )
1551 impl_ni_doSwitchCursor( true );
1554 //------------------------------------------------------------------------------------------------------------------
1555 bool TableControl_Impl::dispatchAction( TableControlAction _eAction )
1557 DBG_CHECK_ME();
1559 bool bSuccess = false;
1560 bool selectionChanged = false;
1562 switch ( _eAction )
1564 case cursorDown:
1565 if ( m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION )
1567 //if other rows already selected, deselect them
1568 if(!m_aSelectedRows.empty())
1570 invalidateSelectedRows();
1571 m_aSelectedRows.clear();
1573 if ( m_nCurRow < m_nRowCount-1 )
1575 ++m_nCurRow;
1576 m_aSelectedRows.push_back(m_nCurRow);
1578 else
1579 m_aSelectedRows.push_back(m_nCurRow);
1580 invalidateRow( m_nCurRow );
1581 ensureVisible(m_nCurColumn,m_nCurRow,false);
1582 selectionChanged = true;
1583 bSuccess = true;
1585 else
1587 if ( m_nCurRow < m_nRowCount - 1 )
1588 bSuccess = goTo( m_nCurColumn, m_nCurRow + 1 );
1590 break;
1592 case cursorUp:
1593 if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1595 if(!m_aSelectedRows.empty())
1597 invalidateSelectedRows();
1598 m_aSelectedRows.clear();
1600 if(m_nCurRow>0)
1602 --m_nCurRow;
1603 m_aSelectedRows.push_back(m_nCurRow);
1604 invalidateRow( m_nCurRow );
1606 else
1608 m_aSelectedRows.push_back(m_nCurRow);
1609 invalidateRow( m_nCurRow );
1611 ensureVisible(m_nCurColumn,m_nCurRow,false);
1612 selectionChanged = true;
1613 bSuccess = true;
1615 else
1617 if ( m_nCurRow > 0 )
1618 bSuccess = goTo( m_nCurColumn, m_nCurRow - 1 );
1620 break;
1621 case cursorLeft:
1622 if ( m_nCurColumn > 0 )
1623 bSuccess = goTo( m_nCurColumn - 1, m_nCurRow );
1624 else
1625 if ( ( m_nCurColumn == 0) && ( m_nCurRow > 0 ) )
1626 bSuccess = goTo( m_nColumnCount - 1, m_nCurRow - 1 );
1627 break;
1629 case cursorRight:
1630 if ( m_nCurColumn < m_nColumnCount - 1 )
1631 bSuccess = goTo( m_nCurColumn + 1, m_nCurRow );
1632 else
1633 if ( ( m_nCurColumn == m_nColumnCount - 1 ) && ( m_nCurRow < m_nRowCount - 1 ) )
1634 bSuccess = goTo( 0, m_nCurRow + 1 );
1635 break;
1637 case cursorToLineStart:
1638 bSuccess = goTo( 0, m_nCurRow );
1639 break;
1641 case cursorToLineEnd:
1642 bSuccess = goTo( m_nColumnCount - 1, m_nCurRow );
1643 break;
1645 case cursorToFirstLine:
1646 bSuccess = goTo( m_nCurColumn, 0 );
1647 break;
1649 case cursorToLastLine:
1650 bSuccess = goTo( m_nCurColumn, m_nRowCount - 1 );
1651 break;
1653 case cursorPageUp:
1655 RowPos nNewRow = ::std::max( (RowPos)0, m_nCurRow - impl_getVisibleRows( false ) );
1656 bSuccess = goTo( m_nCurColumn, nNewRow );
1658 break;
1660 case cursorPageDown:
1662 RowPos nNewRow = ::std::min( m_nRowCount - 1, m_nCurRow + impl_getVisibleRows( false ) );
1663 bSuccess = goTo( m_nCurColumn, nNewRow );
1665 break;
1667 case cursorTopLeft:
1668 bSuccess = goTo( 0, 0 );
1669 break;
1671 case cursorBottomRight:
1672 bSuccess = goTo( m_nColumnCount - 1, m_nRowCount - 1 );
1673 break;
1675 case cursorSelectRow:
1677 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1678 return bSuccess = false;
1679 //pos is the position of the current row in the vector of selected rows, if current row is selected
1680 int pos = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1681 //if current row is selected, it should be deselected, when ALT+SPACE are pressed
1682 if(pos>-1)
1684 m_aSelectedRows.erase(m_aSelectedRows.begin()+pos);
1685 if(m_aSelectedRows.empty() && m_nAnchor != -1)
1686 m_nAnchor = -1;
1688 //else select the row->put it in the vector
1689 else
1690 m_aSelectedRows.push_back(m_nCurRow);
1691 invalidateRow( m_nCurRow );
1692 selectionChanged = true;
1693 bSuccess = true;
1695 break;
1696 case cursorSelectRowUp:
1698 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1699 return bSuccess = false;
1700 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1702 //if there are other selected rows, deselect them
1703 return false;
1705 else
1707 //there are other selected rows
1708 if(!m_aSelectedRows.empty())
1710 //the anchor wasn't set -> a region is not selected, that's why clear all selection
1711 //and select the current row
1712 if(m_nAnchor==-1)
1714 invalidateSelectedRows();
1715 m_aSelectedRows.clear();
1716 m_aSelectedRows.push_back(m_nCurRow);
1717 invalidateRow( m_nCurRow );
1719 else
1721 //a region is already selected, prevRow is last selected row and the row above - nextRow - should be selected
1722 int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1723 int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow-1);
1724 if(prevRow>-1)
1726 //if m_nCurRow isn't the upper one, can move up, otherwise not
1727 if(m_nCurRow>0)
1728 m_nCurRow--;
1729 else
1730 return bSuccess = true;
1731 //if nextRow already selected, deselect it, otherwise select it
1732 if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow)
1734 m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow);
1735 invalidateRow( m_nCurRow + 1 );
1737 else
1739 m_aSelectedRows.push_back(m_nCurRow);
1740 invalidateRow( m_nCurRow );
1743 else
1745 if(m_nCurRow>0)
1747 m_aSelectedRows.push_back(m_nCurRow);
1748 m_nCurRow--;
1749 m_aSelectedRows.push_back(m_nCurRow);
1750 invalidateSelectedRegion( m_nCurRow+1, m_nCurRow );
1755 else
1757 //if nothing is selected and the current row isn't the upper one
1758 //select the current and one row above
1759 //otherwise select only the upper row
1760 if(m_nCurRow>0)
1762 m_aSelectedRows.push_back(m_nCurRow);
1763 m_nCurRow--;
1764 m_aSelectedRows.push_back(m_nCurRow);
1765 invalidateSelectedRegion( m_nCurRow+1, m_nCurRow );
1767 else
1769 m_aSelectedRows.push_back(m_nCurRow);
1770 invalidateRow( m_nCurRow );
1773 m_pSelEngine->SetAnchor(sal_True);
1774 m_nAnchor = m_nCurRow;
1775 ensureVisible(m_nCurColumn, m_nCurRow, false);
1776 selectionChanged = true;
1777 bSuccess = true;
1780 break;
1781 case cursorSelectRowDown:
1783 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1784 bSuccess = false;
1785 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1787 bSuccess = false;
1789 else
1791 if(!m_aSelectedRows.empty())
1793 //the anchor wasn't set -> a region is not selected, that's why clear all selection
1794 //and select the current row
1795 if(m_nAnchor==-1)
1797 invalidateSelectedRows();
1798 m_aSelectedRows.clear();
1799 m_aSelectedRows.push_back(m_nCurRow);
1800 invalidateRow( m_nCurRow );
1802 else
1804 //a region is already selected, prevRow is last selected row and the row beneath - nextRow - should be selected
1805 int prevRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow);
1806 int nextRow = getRowSelectedNumber(m_aSelectedRows, m_nCurRow+1);
1807 if(prevRow>-1)
1809 //if m_nCurRow isn't the last one, can move down, otherwise not
1810 if(m_nCurRow<m_nRowCount-1)
1811 m_nCurRow++;
1812 else
1813 return bSuccess = true;
1814 //if next row already selected, deselect it, otherwise select it
1815 if(nextRow>-1 && m_aSelectedRows[nextRow] == m_nCurRow)
1817 m_aSelectedRows.erase(m_aSelectedRows.begin()+prevRow);
1818 invalidateRow( m_nCurRow - 1 );
1820 else
1822 m_aSelectedRows.push_back(m_nCurRow);
1823 invalidateRow( m_nCurRow );
1826 else
1828 if(m_nCurRow<m_nRowCount-1)
1830 m_aSelectedRows.push_back(m_nCurRow);
1831 m_nCurRow++;
1832 m_aSelectedRows.push_back(m_nCurRow);
1833 invalidateSelectedRegion( m_nCurRow-1, m_nCurRow );
1838 else
1840 //there wasn't any selection, select current and row beneath, otherwise only row beneath
1841 if(m_nCurRow<m_nRowCount-1)
1843 m_aSelectedRows.push_back(m_nCurRow);
1844 m_nCurRow++;
1845 m_aSelectedRows.push_back(m_nCurRow);
1846 invalidateSelectedRegion( m_nCurRow-1, m_nCurRow );
1848 else
1850 m_aSelectedRows.push_back(m_nCurRow);
1851 invalidateRow( m_nCurRow );
1854 m_pSelEngine->SetAnchor(sal_True);
1855 m_nAnchor = m_nCurRow;
1856 ensureVisible(m_nCurColumn, m_nCurRow, false);
1857 selectionChanged = true;
1858 bSuccess = true;
1861 break;
1863 case cursorSelectRowAreaTop:
1865 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1866 bSuccess = false;
1867 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1868 bSuccess = false;
1869 else
1871 //select the region between the current and the upper row
1872 RowPos iter = m_nCurRow;
1873 invalidateSelectedRegion( m_nCurRow, 0 );
1874 //put the rows in vector
1875 while(iter>=0)
1877 if ( !isRowSelected( iter ) )
1878 m_aSelectedRows.push_back(iter);
1879 --iter;
1881 m_nCurRow = 0;
1882 m_nAnchor = m_nCurRow;
1883 m_pSelEngine->SetAnchor(sal_True);
1884 ensureVisible(m_nCurColumn, 0, false);
1885 selectionChanged = true;
1886 bSuccess = true;
1889 break;
1891 case cursorSelectRowAreaBottom:
1893 if(m_pSelEngine->GetSelectionMode() == NO_SELECTION)
1894 return bSuccess = false;
1895 else if(m_pSelEngine->GetSelectionMode() == SINGLE_SELECTION)
1896 return bSuccess = false;
1897 //select the region between the current and the last row
1898 RowPos iter = m_nCurRow;
1899 invalidateSelectedRegion( m_nCurRow, m_nRowCount-1 );
1900 //put the rows in the vector
1901 while(iter<=m_nRowCount)
1903 if ( !isRowSelected( iter ) )
1904 m_aSelectedRows.push_back(iter);
1905 ++iter;
1907 m_nCurRow = m_nRowCount-1;
1908 m_nAnchor = m_nCurRow;
1909 m_pSelEngine->SetAnchor(sal_True);
1910 ensureVisible(m_nCurColumn, m_nRowCount-1, false);
1911 selectionChanged = true;
1912 bSuccess = true;
1914 break;
1915 default:
1916 OSL_FAIL( "TableControl_Impl::dispatchAction: unsupported action!" );
1917 break;
1920 if ( bSuccess && selectionChanged )
1922 m_rAntiImpl.Select();
1925 return bSuccess;
1928 //------------------------------------------------------------------------------------------------------------------
1929 void TableControl_Impl::impl_ni_doSwitchCursor( bool _bShow )
1931 PTableRenderer pRenderer = !!m_pModel ? m_pModel->getRenderer() : PTableRenderer();
1932 if ( !!pRenderer )
1934 Rectangle aCellRect;
1935 impl_getCellRect( m_nCurColumn, m_nCurRow, aCellRect );
1936 if ( _bShow )
1937 pRenderer->ShowCellCursor( *m_pDataWindow, aCellRect );
1938 else
1939 pRenderer->HideCellCursor( *m_pDataWindow, aCellRect );
1943 //------------------------------------------------------------------------------------------------------------------
1944 void TableControl_Impl::impl_getCellRect( ColPos _nColumn, RowPos _nRow, Rectangle& _rCellRect ) const
1946 DBG_CHECK_ME();
1948 if ( !m_pModel
1949 || ( COL_INVALID == _nColumn )
1950 || ( ROW_INVALID == _nRow )
1953 _rCellRect.SetEmpty();
1954 return;
1957 TableCellGeometry aCell( *this, impl_getAllVisibleCellsArea(), _nColumn, _nRow );
1958 _rCellRect = aCell.getRect();
1961 //------------------------------------------------------------------------------------------------------------------
1962 RowPos TableControl_Impl::getRowAtPoint( const Point& rPoint ) const
1964 DBG_CHECK_ME();
1965 return impl_getRowForAbscissa( rPoint.Y() );
1968 //------------------------------------------------------------------------------------------------------------------
1969 ColPos TableControl_Impl::getColAtPoint( const Point& rPoint ) const
1971 DBG_CHECK_ME();
1972 return impl_getColumnForOrdinate( rPoint.X() );
1975 //------------------------------------------------------------------------------------------------------------------
1976 TableCell TableControl_Impl::hitTest( Point const & i_point ) const
1978 TableCell aCell( getColAtPoint( i_point ), getRowAtPoint( i_point ) );
1979 if ( aCell.nColumn > COL_ROW_HEADERS )
1981 PColumnModel const pColumn = m_pModel->getColumnModel( aCell.nColumn );
1982 MutableColumnMetrics const & rColInfo( m_aColumnWidths[ aCell.nColumn ] );
1983 if ( ( rColInfo.getEnd() - 3 <= i_point.X() )
1984 && ( rColInfo.getEnd() >= i_point.X() )
1985 && pColumn->isResizable()
1988 aCell.eArea = ColumnDivider;
1991 return aCell;
1994 //------------------------------------------------------------------------------------------------------------------
1995 ColumnMetrics TableControl_Impl::getColumnMetrics( ColPos const i_column ) const
1997 DBG_CHECK_ME();
1999 ENSURE_OR_RETURN( ( i_column >= 0 ) && ( i_column < m_pModel->getColumnCount() ),
2000 "TableControl_Impl::getColumnMetrics: illegal column index!", ColumnMetrics() );
2001 return (ColumnMetrics const &)m_aColumnWidths[ i_column ];
2004 //------------------------------------------------------------------------------------------------------------------
2005 PTableModel TableControl_Impl::getModel() const
2007 return m_pModel;
2010 //------------------------------------------------------------------------------------------------------------------
2011 RowPos TableControl_Impl::getCurrentColumn() const
2013 return m_nCurColumn;
2016 //------------------------------------------------------------------------------------------------------------------
2017 RowPos TableControl_Impl::getCurrentRow() const
2019 return m_nCurRow;
2022 //------------------------------------------------------------------------------------------------------------------
2023 ::Size TableControl_Impl::getTableSizePixel() const
2025 return m_pDataWindow->GetOutputSizePixel();
2028 //------------------------------------------------------------------------------------------------------------------
2029 void TableControl_Impl::setPointer( Pointer const & i_pointer )
2031 DBG_CHECK_ME();
2032 m_pDataWindow->SetPointer( i_pointer );
2035 //------------------------------------------------------------------------------------------------------------------
2036 void TableControl_Impl::captureMouse()
2038 m_pDataWindow->CaptureMouse();
2041 //------------------------------------------------------------------------------------------------------------------
2042 void TableControl_Impl::releaseMouse()
2044 m_pDataWindow->ReleaseMouse();
2047 //------------------------------------------------------------------------------------------------------------------
2048 void TableControl_Impl::invalidate( TableArea const i_what )
2050 switch ( i_what )
2052 case TableAreaColumnHeaders:
2053 m_pDataWindow->Invalidate( calcHeaderRect( true ) );
2054 break;
2056 case TableAreaRowHeaders:
2057 m_pDataWindow->Invalidate( calcHeaderRect( false ) );
2058 break;
2060 case TableAreaDataArea:
2061 m_pDataWindow->Invalidate( impl_getAllVisibleDataCellArea() );
2062 break;
2064 case TableAreaAll:
2065 m_pDataWindow->Invalidate();
2066 break;
2070 //------------------------------------------------------------------------------------------------------------------
2071 long TableControl_Impl::pixelWidthToAppFont( long const i_pixels ) const
2073 return m_pDataWindow->PixelToLogic( Size( i_pixels, 0 ), MAP_APPFONT ).Width();
2076 //------------------------------------------------------------------------------------------------------------------
2077 long TableControl_Impl::appFontWidthToPixel( long const i_appFontUnits ) const
2079 return m_pDataWindow->LogicToPixel( Size( i_appFontUnits, 0 ), MAP_APPFONT ).Width();
2082 //------------------------------------------------------------------------------------------------------------------
2083 void TableControl_Impl::hideTracking()
2085 m_pDataWindow->HideTracking();
2088 //------------------------------------------------------------------------------------------------------------------
2089 void TableControl_Impl::showTracking( Rectangle const & i_location, sal_uInt16 const i_flags )
2091 m_pDataWindow->ShowTracking( i_location, i_flags );
2094 //------------------------------------------------------------------------------------------------------------------
2095 bool TableControl_Impl::activateCell( ColPos const i_col, RowPos const i_row )
2097 DBG_CHECK_ME();
2098 return goTo( i_col, i_row );
2101 //------------------------------------------------------------------------------------------------------------------
2102 void TableControl_Impl::invalidateSelectedRegion( RowPos _nPrevRow, RowPos _nCurRow )
2104 DBG_CHECK_ME();
2105 // get the visible area of the table control and set the Left and right border of the region to be repainted
2106 Rectangle const aAllCells( impl_getAllVisibleCellsArea() );
2108 Rectangle aInvalidateRect;
2109 aInvalidateRect.Left() = aAllCells.Left();
2110 aInvalidateRect.Right() = aAllCells.Right();
2111 // if only one row is selected
2112 if ( _nPrevRow == _nCurRow )
2114 Rectangle aCellRect;
2115 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2116 aInvalidateRect.Top() = aCellRect.Top();
2117 aInvalidateRect.Bottom() = aCellRect.Bottom();
2119 //if the region is above the current row
2120 else if(_nPrevRow < _nCurRow )
2122 Rectangle aCellRect;
2123 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
2124 aInvalidateRect.Top() = aCellRect.Top();
2125 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2126 aInvalidateRect.Bottom() = aCellRect.Bottom();
2128 //if the region is beneath the current row
2129 else
2131 Rectangle aCellRect;
2132 impl_getCellRect( m_nCurColumn, _nCurRow, aCellRect );
2133 aInvalidateRect.Top() = aCellRect.Top();
2134 impl_getCellRect( m_nCurColumn, _nPrevRow, aCellRect );
2135 aInvalidateRect.Bottom() = aCellRect.Bottom();
2138 invalidateRect(aInvalidateRect);
2141 void TableControl_Impl::invalidateRect(const Rectangle &rInvalidateRect)
2143 m_pDataWindow->Invalidate( rInvalidateRect,
2144 m_pDataWindow->GetControlBackground().GetTransparency() ? INVALIDATE_TRANSPARENT : 0 );
2147 //------------------------------------------------------------------------------------------------------------------
2148 void TableControl_Impl::invalidateSelectedRows()
2150 for ( ::std::vector< RowPos >::iterator selRow = m_aSelectedRows.begin();
2151 selRow != m_aSelectedRows.end();
2152 ++selRow
2155 invalidateRow( *selRow );
2159 //------------------------------------------------------------------------------------------------------------------
2160 void TableControl_Impl::invalidateRowRange( RowPos const i_firstRow, RowPos const i_lastRow )
2162 RowPos const firstRow = i_firstRow < m_nTopRow ? m_nTopRow : i_firstRow;
2163 RowPos const lastVisibleRow = m_nTopRow + impl_getVisibleRows( true ) - 1;
2164 RowPos const lastRow = ( ( i_lastRow == ROW_INVALID ) || ( i_lastRow > lastVisibleRow ) ) ? lastVisibleRow : i_lastRow;
2166 Rectangle aInvalidateRect;
2168 Rectangle const aVisibleCellsArea( impl_getAllVisibleCellsArea() );
2169 TableRowGeometry aRow( *this, aVisibleCellsArea, firstRow, true );
2170 while ( aRow.isValid() && ( aRow.getRow() <= lastRow ) )
2172 aInvalidateRect.Union( aRow.getRect() );
2173 aRow.moveDown();
2176 if ( i_lastRow == ROW_INVALID )
2177 aInvalidateRect.Bottom() = m_pDataWindow->GetOutputSizePixel().Height();
2179 invalidateRect(aInvalidateRect);
2182 //------------------------------------------------------------------------------
2183 void TableControl_Impl::checkCursorPosition()
2185 DBG_CHECK_ME();
2187 TableSize nVisibleRows = impl_getVisibleRows(true);
2188 TableSize nVisibleCols = impl_getVisibleColumns(true);
2189 if ( ( m_nTopRow + nVisibleRows > m_nRowCount )
2190 && ( m_nRowCount >= nVisibleRows )
2193 --m_nTopRow;
2195 else
2197 m_nTopRow = 0;
2200 if ( ( m_nLeftColumn + nVisibleCols > m_nColumnCount )
2201 && ( m_nColumnCount >= nVisibleCols )
2204 --m_nLeftColumn;
2206 else
2208 m_nLeftColumn = 0;
2211 m_pDataWindow->Invalidate();
2214 //--------------------------------------------------------------------
2215 TableSize TableControl_Impl::impl_getVisibleRows( bool _bAcceptPartialRow ) const
2217 DBG_CHECK_ME();
2219 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleRows: no data window!" );
2221 return lcl_getRowsFittingInto(
2222 m_pDataWindow->GetOutputSizePixel().Height() - m_nColHeaderHeightPixel,
2223 m_nRowHeightPixel,
2224 _bAcceptPartialRow
2228 //--------------------------------------------------------------------
2229 TableSize TableControl_Impl::impl_getVisibleColumns( bool _bAcceptPartialCol ) const
2231 DBG_CHECK_ME();
2233 DBG_ASSERT( m_pDataWindow, "TableControl_Impl::impl_getVisibleColumns: no data window!" );
2235 return lcl_getColumnsVisibleWithin(
2236 Rectangle( Point( 0, 0 ), m_pDataWindow->GetOutputSizePixel() ),
2237 m_nLeftColumn,
2238 *this,
2239 _bAcceptPartialCol
2243 //--------------------------------------------------------------------
2244 bool TableControl_Impl::goTo( ColPos _nColumn, RowPos _nRow )
2246 DBG_CHECK_ME();
2248 // TODO: give veto listeners a chance
2250 if ( ( _nColumn < 0 ) || ( _nColumn >= m_nColumnCount )
2251 || ( _nRow < 0 ) || ( _nRow >= m_nRowCount )
2254 OSL_ENSURE( false, "TableControl_Impl::goTo: invalid row or column index!" );
2255 return false;
2258 SuppressCursor aHideCursor( *this );
2259 m_nCurColumn = _nColumn;
2260 m_nCurRow = _nRow;
2262 // ensure that the new cell is visible
2263 ensureVisible( m_nCurColumn, m_nCurRow, false );
2264 return true;
2267 //--------------------------------------------------------------------
2268 void TableControl_Impl::ensureVisible( ColPos _nColumn, RowPos _nRow, bool _bAcceptPartialVisibility )
2270 DBG_CHECK_ME();
2271 DBG_ASSERT( ( _nColumn >= 0 ) && ( _nColumn < m_nColumnCount )
2272 && ( _nRow >= 0 ) && ( _nRow < m_nRowCount ),
2273 "TableControl_Impl::ensureVisible: invalid coordinates!" );
2275 SuppressCursor aHideCursor( *this );
2277 if ( _nColumn < m_nLeftColumn )
2278 impl_scrollColumns( _nColumn - m_nLeftColumn );
2279 else
2281 TableSize nVisibleColumns = impl_getVisibleColumns( _bAcceptPartialVisibility );
2282 if ( _nColumn > m_nLeftColumn + nVisibleColumns - 1 )
2284 impl_scrollColumns( _nColumn - ( m_nLeftColumn + nVisibleColumns - 1 ) );
2285 // TODO: since not all columns have the same width, this might in theory result
2286 // in the column still not being visible.
2290 if ( _nRow < m_nTopRow )
2291 impl_scrollRows( _nRow - m_nTopRow );
2292 else
2294 TableSize nVisibleRows = impl_getVisibleRows( _bAcceptPartialVisibility );
2295 if ( _nRow > m_nTopRow + nVisibleRows - 1 )
2296 impl_scrollRows( _nRow - ( m_nTopRow + nVisibleRows - 1 ) );
2300 //--------------------------------------------------------------------
2301 OUString TableControl_Impl::getCellContentAsString( RowPos const i_row, ColPos const i_col )
2303 Any aCellValue;
2304 m_pModel->getCellContent( i_col, i_row, aCellValue );
2306 OUString sCellStringContent;
2307 m_pModel->getRenderer()->GetFormattedCellString( aCellValue, i_col, i_row, sCellStringContent );
2309 return sCellStringContent;
2312 //--------------------------------------------------------------------
2313 TableSize TableControl_Impl::impl_ni_ScrollRows( TableSize _nRowDelta )
2315 // compute new top row
2316 RowPos nNewTopRow =
2317 ::std::max(
2318 ::std::min( (RowPos)( m_nTopRow + _nRowDelta ), (RowPos)( m_nRowCount - 1 ) ),
2319 (RowPos)0
2322 RowPos nOldTopRow = m_nTopRow;
2323 m_nTopRow = nNewTopRow;
2325 // if updates are enabled currently, scroll the viewport
2326 if ( m_nTopRow != nOldTopRow )
2328 DBG_SUSPEND_INV( INV_SCROLL_POSITION );
2329 SuppressCursor aHideCursor( *this );
2330 // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2331 // which hides the cursor and then calls the listener)
2332 // Same for onEndScroll
2334 // scroll the view port, if possible
2335 long nPixelDelta = m_nRowHeightPixel * ( m_nTopRow - nOldTopRow );
2337 Rectangle aDataArea( Point( 0, m_nColHeaderHeightPixel ), m_pDataWindow->GetOutputSizePixel() );
2339 if ( m_pDataWindow->GetBackground().IsScrollable()
2340 && abs( nPixelDelta ) < aDataArea.GetHeight()
2343 m_pDataWindow->Scroll( 0, (long)-nPixelDelta, aDataArea, SCROLL_CLIP | SCROLL_UPDATE | SCROLL_CHILDREN);
2345 else
2346 m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
2348 // update the position at the vertical scrollbar
2349 if ( m_pVScroll != NULL )
2350 m_pVScroll->SetThumbPos( m_nTopRow );
2353 // The scroll bar availaility might change when we scrolled.
2354 // For instance, imagine a view with 10 rows, if which 5 fit into the window, numbered 1 to 10.
2355 // Now let
2356 // - the user scroll to row number 6, so the last 5 rows are visible
2357 // - somebody remove the last 4 rows
2358 // - the user scroll to row number 5 being the top row, so the last two rows are visible
2359 // - somebody remove row number 6
2360 // - the user scroll to row number 1
2361 // => in this case, the need for the scrollbar vanishes immediately.
2362 if ( m_nTopRow == 0 )
2363 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2365 return (TableSize)( m_nTopRow - nOldTopRow );
2368 //--------------------------------------------------------------------
2369 TableSize TableControl_Impl::impl_scrollRows( TableSize const i_rowDelta )
2371 DBG_CHECK_ME();
2372 return impl_ni_ScrollRows( i_rowDelta );
2375 //--------------------------------------------------------------------
2376 TableSize TableControl_Impl::impl_ni_ScrollColumns( TableSize _nColumnDelta )
2378 // compute new left column
2379 const ColPos nNewLeftColumn =
2380 ::std::max(
2381 ::std::min( (ColPos)( m_nLeftColumn + _nColumnDelta ), (ColPos)( m_nColumnCount - 1 ) ),
2382 (ColPos)0
2385 const ColPos nOldLeftColumn = m_nLeftColumn;
2386 m_nLeftColumn = nNewLeftColumn;
2388 // if updates are enabled currently, scroll the viewport
2389 if ( m_nLeftColumn != nOldLeftColumn )
2391 DBG_SUSPEND_INV( INV_SCROLL_POSITION );
2392 SuppressCursor aHideCursor( *this );
2393 // TODO: call a onStartScroll at our listener (or better an own onStartScroll,
2394 // which hides the cursor and then calls the listener)
2395 // Same for onEndScroll
2397 // scroll the view port, if possible
2398 const Rectangle aDataArea( Point( m_nRowHeaderWidthPixel, 0 ), m_pDataWindow->GetOutputSizePixel() );
2400 long nPixelDelta =
2401 m_aColumnWidths[ nOldLeftColumn ].getStart()
2402 - m_aColumnWidths[ m_nLeftColumn ].getStart();
2404 // update our column positions
2405 // Do this *before* scrolling, as SCROLL_UPDATE will trigger a paint, which already needs the correct
2406 // information in m_aColumnWidths
2407 for ( ColumnPositions::iterator colPos = m_aColumnWidths.begin();
2408 colPos != m_aColumnWidths.end();
2409 ++colPos
2412 colPos->move( nPixelDelta );
2415 // scroll the window content (if supported and possible), or invalidate the complete window
2416 if ( m_pDataWindow->GetBackground().IsScrollable()
2417 && abs( nPixelDelta ) < aDataArea.GetWidth()
2420 m_pDataWindow->Scroll( nPixelDelta, 0, aDataArea, SCROLL_CLIP | SCROLL_UPDATE );
2422 else
2423 m_pDataWindow->Invalidate( INVALIDATE_UPDATE );
2425 // update the position at the horizontal scrollbar
2426 if ( m_pHScroll != NULL )
2427 m_pHScroll->SetThumbPos( m_nLeftColumn );
2430 // The scroll bar availaility might change when we scrolled. This is because we do not hide
2431 // the scrollbar when it is, in theory, unnecessary, but currently at a position > 0. In this case, it will
2432 // be auto-hidden when it's scrolled back to pos 0.
2433 if ( m_nLeftColumn == 0 )
2434 m_rAntiImpl.PostUserEvent( LINK( this, TableControl_Impl, OnUpdateScrollbars ) );
2436 return (TableSize)( m_nLeftColumn - nOldLeftColumn );
2439 //------------------------------------------------------------------------------------------------------------------
2440 TableSize TableControl_Impl::impl_scrollColumns( TableSize const i_columnDelta )
2442 DBG_CHECK_ME();
2443 return impl_ni_ScrollColumns( i_columnDelta );
2446 //------------------------------------------------------------------------------------------------------------------
2447 SelectionEngine* TableControl_Impl::getSelEngine()
2449 return m_pSelEngine;
2452 //------------------------------------------------------------------------------------------------------------------
2453 ScrollBar* TableControl_Impl::getHorzScrollbar()
2455 return m_pHScroll;
2458 //------------------------------------------------------------------------------------------------------------------
2459 ScrollBar* TableControl_Impl::getVertScrollbar()
2461 return m_pVScroll;
2464 //------------------------------------------------------------------------------------------------------------------
2465 bool TableControl_Impl::isRowSelected( RowPos i_row ) const
2467 return ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_row ) != m_aSelectedRows.end();
2470 //------------------------------------------------------------------------------------------------------------------
2471 RowPos TableControl_Impl::getSelectedRowIndex( size_t const i_selectionIndex ) const
2473 if ( i_selectionIndex < m_aSelectedRows.size() )
2474 return m_aSelectedRows[ i_selectionIndex ];
2475 return ROW_INVALID;
2478 //------------------------------------------------------------------------------------------------------------------
2479 int TableControl_Impl::getRowSelectedNumber(const ::std::vector<RowPos>& selectedRows, RowPos current)
2481 std::vector<RowPos>::const_iterator it = ::std::find(selectedRows.begin(),selectedRows.end(),current);
2482 if ( it != selectedRows.end() )
2484 return it - selectedRows.begin();
2486 return -1;
2489 //--------------------------------------------------------------------
2490 ColPos TableControl_Impl::impl_getColumnForOrdinate( long const i_ordinate ) const
2492 DBG_CHECK_ME();
2494 if ( ( m_aColumnWidths.empty() ) || ( i_ordinate < 0 ) )
2495 return COL_INVALID;
2497 if ( i_ordinate < m_nRowHeaderWidthPixel )
2498 return COL_ROW_HEADERS;
2500 ColumnPositions::const_iterator lowerBound = ::std::lower_bound(
2501 m_aColumnWidths.begin(),
2502 m_aColumnWidths.end(),
2503 MutableColumnMetrics(i_ordinate+1, i_ordinate+1),
2504 ColumnInfoPositionLess()
2506 if ( lowerBound == m_aColumnWidths.end() )
2508 // point is *behind* the start of the last column ...
2509 if ( i_ordinate < m_aColumnWidths.rbegin()->getEnd() )
2510 // ... but still before its end
2511 return m_nColumnCount - 1;
2512 return COL_INVALID;
2514 return lowerBound - m_aColumnWidths.begin();
2517 //--------------------------------------------------------------------
2518 RowPos TableControl_Impl::impl_getRowForAbscissa( long const i_abscissa ) const
2520 DBG_CHECK_ME();
2522 if ( i_abscissa < 0 )
2523 return ROW_INVALID;
2525 if ( i_abscissa < m_nColHeaderHeightPixel )
2526 return ROW_COL_HEADERS;
2528 long const abscissa = i_abscissa - m_nColHeaderHeightPixel;
2529 long const row = m_nTopRow + abscissa / m_nRowHeightPixel;
2530 return row < m_pModel->getRowCount() ? row : ROW_INVALID;
2533 //--------------------------------------------------------------------
2534 bool TableControl_Impl::markRowAsDeselected( RowPos const i_rowIndex )
2536 DBG_CHECK_ME();
2538 ::std::vector< RowPos >::iterator selPos = ::std::find( m_aSelectedRows.begin(), m_aSelectedRows.end(), i_rowIndex );
2539 if ( selPos == m_aSelectedRows.end() )
2540 return false;
2542 m_aSelectedRows.erase( selPos );
2543 return true;
2546 //--------------------------------------------------------------------
2547 bool TableControl_Impl::markRowAsSelected( RowPos const i_rowIndex )
2549 DBG_CHECK_ME();
2551 if ( isRowSelected( i_rowIndex ) )
2552 return false;
2554 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2555 switch ( eSelMode )
2557 case SINGLE_SELECTION:
2558 if ( !m_aSelectedRows.empty() )
2560 OSL_ENSURE( m_aSelectedRows.size() == 1, "TableControl::markRowAsSelected: SingleSelection with more than one selected element?" );
2561 m_aSelectedRows[0] = i_rowIndex;
2562 break;
2564 // fall through
2566 case MULTIPLE_SELECTION:
2567 m_aSelectedRows.push_back( i_rowIndex );
2568 break;
2570 default:
2571 OSL_ENSURE( false, "TableControl_Impl::markRowAsSelected: unsupported selection mode!" );
2572 return false;
2575 return true;
2578 //--------------------------------------------------------------------
2579 bool TableControl_Impl::markAllRowsAsDeselected()
2581 if ( m_aSelectedRows.empty() )
2582 return false;
2584 m_aSelectedRows.clear();
2585 return true;
2588 //--------------------------------------------------------------------
2589 bool TableControl_Impl::markAllRowsAsSelected()
2591 DBG_CHECK_ME();
2593 SelectionMode const eSelMode = getSelEngine()->GetSelectionMode();
2594 ENSURE_OR_RETURN_FALSE( eSelMode == MULTIPLE_SELECTION, "TableControl_Impl::markAllRowsAsSelected: unsupported selection mode!" );
2596 if ( m_aSelectedRows.size() == size_t( m_pModel->getRowCount() ) )
2598 #if OSL_DEBUG_LEVEL > 0
2599 for ( TableSize row = 0; row < m_pModel->getRowCount(); ++row )
2601 OSL_ENSURE( isRowSelected( row ), "TableControl_Impl::markAllRowsAsSelected: inconsistency in the selected rows!" );
2603 #endif
2604 // already all rows marked as selected
2605 return false;
2608 m_aSelectedRows.clear();
2609 for ( RowPos i=0; i < m_pModel->getRowCount(); ++i )
2610 m_aSelectedRows.push_back(i);
2612 return true;
2615 //--------------------------------------------------------------------
2616 void TableControl_Impl::commitAccessibleEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2618 impl_commitAccessibleEvent( i_eventID, i_newValue, i_oldValue );
2621 //--------------------------------------------------------------------
2622 void TableControl_Impl::commitCellEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2624 DBG_CHECK_ME();
2625 if ( impl_isAccessibleAlive() )
2626 m_pAccessibleTable->commitCellEvent( i_eventID, i_newValue, i_oldValue );
2629 //--------------------------------------------------------------------
2630 void TableControl_Impl::commitTableEvent( sal_Int16 const i_eventID, const Any& i_newValue, const Any& i_oldValue )
2632 DBG_CHECK_ME();
2633 if ( impl_isAccessibleAlive() )
2634 m_pAccessibleTable->commitTableEvent( i_eventID, i_newValue, i_oldValue );
2637 //--------------------------------------------------------------------
2638 Rectangle TableControl_Impl::calcHeaderRect(bool bColHeader)
2640 Rectangle const aRectTableWithHeaders( impl_getAllVisibleCellsArea() );
2641 Size const aSizeTableWithHeaders( aRectTableWithHeaders.GetSize() );
2642 if ( bColHeader )
2643 return Rectangle( aRectTableWithHeaders.TopLeft(), Size( aSizeTableWithHeaders.Width(), m_nColHeaderHeightPixel ) );
2644 else
2645 return Rectangle( aRectTableWithHeaders.TopLeft(), Size( m_nRowHeaderWidthPixel, aSizeTableWithHeaders.Height() ) );
2648 //--------------------------------------------------------------------
2649 Rectangle TableControl_Impl::calcHeaderCellRect( bool bColHeader, sal_Int32 nPos )
2651 Rectangle const aHeaderRect = calcHeaderRect( bColHeader );
2652 TableCellGeometry const aGeometry(
2653 *this, aHeaderRect,
2654 bColHeader ? nPos : COL_ROW_HEADERS,
2655 bColHeader ? ROW_COL_HEADERS : nPos
2657 return aGeometry.getRect();
2660 //--------------------------------------------------------------------
2661 Rectangle TableControl_Impl::calcTableRect()
2663 return impl_getAllVisibleDataCellArea();
2666 //--------------------------------------------------------------------
2667 Rectangle TableControl_Impl::calcCellRect( sal_Int32 nRow, sal_Int32 nCol )
2669 Rectangle aCellRect;
2670 impl_getCellRect( nRow, nCol, aCellRect );
2671 return aCellRect;
2674 //--------------------------------------------------------------------
2675 IMPL_LINK( TableControl_Impl, OnUpdateScrollbars, void*, /**/ )
2677 DBG_CHECK_ME();
2678 // TODO: can't we simply use lcl_updateScrollbar here, so the scrollbars ranges are updated, instead of
2679 // doing a complete re-layout?
2680 impl_ni_relayout();
2681 return 1L;
2684 //--------------------------------------------------------------------
2685 IMPL_LINK( TableControl_Impl, OnScroll, ScrollBar*, _pScrollbar )
2687 DBG_ASSERT( ( _pScrollbar == m_pVScroll ) || ( _pScrollbar == m_pHScroll ),
2688 "TableControl_Impl::OnScroll: where did this come from?" );
2690 if ( _pScrollbar == m_pVScroll )
2691 impl_ni_ScrollRows( _pScrollbar->GetDelta() );
2692 else
2693 impl_ni_ScrollColumns( _pScrollbar->GetDelta() );
2695 return 0L;
2698 //------------------------------------------------------------------------------------------------------------------
2699 Reference< XAccessible > TableControl_Impl::getAccessible( Window& i_parentWindow )
2701 DBG_TESTSOLARMUTEX();
2702 if ( m_pAccessibleTable == NULL )
2704 Reference< XAccessible > const xAccParent = i_parentWindow.GetAccessible();
2705 if ( xAccParent.is() )
2707 m_pAccessibleTable = m_aFactoryAccess.getFactory().createAccessibleTableControl(
2708 xAccParent, m_rAntiImpl
2713 Reference< XAccessible > xAccessible;
2714 if ( m_pAccessibleTable )
2715 xAccessible = m_pAccessibleTable->getMyself();
2716 return xAccessible;
2719 //------------------------------------------------------------------------------------------------------------------
2720 void TableControl_Impl::disposeAccessible()
2722 if ( m_pAccessibleTable )
2723 m_pAccessibleTable->dispose();
2724 m_pAccessibleTable = NULL;
2727 //------------------------------------------------------------------------------------------------------------------
2728 bool TableControl_Impl::impl_isAccessibleAlive() const
2730 DBG_CHECK_ME();
2731 return ( NULL != m_pAccessibleTable ) && m_pAccessibleTable->isAlive();
2734 //------------------------------------------------------------------------------------------------------------------
2735 void TableControl_Impl::impl_commitAccessibleEvent( sal_Int16 const i_eventID, Any const & i_newValue, Any const & i_oldValue )
2737 DBG_CHECK_ME();
2738 if ( impl_isAccessibleAlive() )
2739 m_pAccessibleTable->commitEvent( i_eventID, i_newValue, i_oldValue );
2742 //==================================================================================================================
2743 //= TableFunctionSet
2744 //==================================================================================================================
2745 //------------------------------------------------------------------------------------------------------------------
2746 TableFunctionSet::TableFunctionSet(TableControl_Impl* _pTableControl)
2747 :m_pTableControl( _pTableControl)
2748 ,m_nCurrentRow( ROW_INVALID )
2751 //------------------------------------------------------------------------------------------------------------------
2752 TableFunctionSet::~TableFunctionSet()
2755 //------------------------------------------------------------------------------------------------------------------
2756 void TableFunctionSet::BeginDrag()
2759 //------------------------------------------------------------------------------------------------------------------
2760 void TableFunctionSet::CreateAnchor()
2762 m_pTableControl->setAnchor( m_pTableControl->getCurRow() );
2765 //------------------------------------------------------------------------------------------------------------------
2766 void TableFunctionSet::DestroyAnchor()
2768 m_pTableControl->setAnchor( ROW_INVALID );
2771 //------------------------------------------------------------------------------------------------------------------
2772 sal_Bool TableFunctionSet::SetCursorAtPoint(const Point& rPoint, sal_Bool bDontSelectAtCursor)
2774 sal_Bool bHandled = sal_False;
2775 // newRow is the row which includes the point, getCurRow() is the last selected row, before the mouse click
2776 RowPos newRow = m_pTableControl->getRowAtPoint( rPoint );
2777 if ( newRow == ROW_COL_HEADERS )
2778 newRow = m_pTableControl->getTopRow();
2780 ColPos newCol = m_pTableControl->getColAtPoint( rPoint );
2781 if ( newCol == COL_ROW_HEADERS )
2782 newCol = m_pTableControl->getLeftColumn();
2784 if ( ( newRow == ROW_INVALID ) || ( newCol == COL_INVALID ) )
2785 return sal_False;
2787 if ( bDontSelectAtCursor )
2789 if ( m_pTableControl->getSelectedRowCount() > 1 )
2790 m_pTableControl->getSelEngine()->AddAlways(sal_True);
2791 bHandled = sal_True;
2793 else if ( m_pTableControl->getAnchor() == m_pTableControl->getCurRow() )
2795 //selecting region,
2796 int diff = m_pTableControl->getCurRow() - newRow;
2797 //selected region lies above the last selection
2798 if( diff >= 0)
2800 //put selected rows in vector
2801 while ( m_pTableControl->getAnchor() >= newRow )
2803 m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() );
2804 m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 );
2805 diff--;
2807 m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 );
2809 //selected region lies beneath the last selected row
2810 else
2812 while ( m_pTableControl->getAnchor() <= newRow )
2814 m_pTableControl->markRowAsSelected( m_pTableControl->getAnchor() );
2815 m_pTableControl->setAnchor( m_pTableControl->getAnchor() + 1 );
2816 diff++;
2818 m_pTableControl->setAnchor( m_pTableControl->getAnchor() - 1 );
2820 m_pTableControl->invalidateSelectedRegion( m_pTableControl->getCurRow(), newRow );
2821 bHandled = sal_True;
2823 //no region selected
2824 else
2826 if ( !m_pTableControl->hasRowSelection() )
2827 m_pTableControl->markRowAsSelected( newRow );
2828 else
2830 if ( m_pTableControl->getSelEngine()->GetSelectionMode() == SINGLE_SELECTION )
2832 DeselectAll();
2833 m_pTableControl->markRowAsSelected( newRow );
2835 else
2837 m_pTableControl->markRowAsSelected( newRow );
2840 if ( m_pTableControl->getSelectedRowCount() > 1 && m_pTableControl->getSelEngine()->GetSelectionMode() != SINGLE_SELECTION )
2841 m_pTableControl->getSelEngine()->AddAlways(sal_True);
2843 m_pTableControl->invalidateRow( newRow );
2844 bHandled = sal_True;
2846 m_pTableControl->goTo( newCol, newRow );
2847 return bHandled;
2849 //------------------------------------------------------------------------------------------------------------------
2850 sal_Bool TableFunctionSet::IsSelectionAtPoint( const Point& rPoint )
2852 m_pTableControl->getSelEngine()->AddAlways(sal_False);
2853 if ( !m_pTableControl->hasRowSelection() )
2854 return sal_False;
2855 else
2857 RowPos curRow = m_pTableControl->getRowAtPoint( rPoint );
2858 m_pTableControl->setAnchor( ROW_INVALID );
2859 bool selected = m_pTableControl->isRowSelected( curRow );
2860 m_nCurrentRow = curRow;
2861 return selected;
2864 //------------------------------------------------------------------------------------------------------------------
2865 void TableFunctionSet::DeselectAtPoint( const Point& rPoint )
2867 (void)rPoint;
2868 m_pTableControl->invalidateRow( m_nCurrentRow );
2869 m_pTableControl->markRowAsDeselected( m_nCurrentRow );
2872 //------------------------------------------------------------------------------------------------------------------
2873 void TableFunctionSet::DeselectAll()
2875 if ( m_pTableControl->hasRowSelection() )
2877 for ( size_t i=0; i<m_pTableControl->getSelectedRowCount(); ++i )
2879 RowPos const rowIndex = m_pTableControl->getSelectedRowIndex(i);
2880 m_pTableControl->invalidateRow( rowIndex );
2883 m_pTableControl->markAllRowsAsDeselected();
2887 //......................................................................................................................
2888 } } // namespace svt::table
2889 //......................................................................................................................
2891 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */