1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
20 #include "table/tablecontrol.hxx"
22 #include "tablegeometry.hxx"
23 #include "tablecontrol_impl.hxx"
24 #include "tabledatawindow.hxx"
26 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
27 #include <com/sun/star/accessibility/AccessibleRole.hpp>
28 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
30 #include <tools/diagnose_ex.h>
31 #include <vcl/settings.hxx>
33 using namespace ::com::sun::star::uno
;
34 using ::com::sun::star::accessibility::XAccessible
;
35 using namespace ::com::sun::star::accessibility
;
36 using namespace ::com::sun::star::lang
;
39 namespace svt
{ namespace table
43 namespace AccessibleEventId
= ::com::sun::star::accessibility::AccessibleEventId
;
49 TableControl::TableControl( vcl::Window
* _pParent
, WinBits _nStyle
)
50 :Control( _pParent
, _nStyle
)
51 ,m_pImpl( new TableControl_Impl( *this ) )
53 TableDataWindow
& rDataWindow
= m_pImpl
->getDataWindow();
54 rDataWindow
.SetSelectHdl( LINK( this, TableControl
, ImplSelectHdl
) );
56 // by default, use the background as determined by the style settings
57 const Color
aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() );
58 SetBackground( Wallpaper( aWindowColor
) );
59 SetFillColor( aWindowColor
);
61 SetCompoundControl( true );
65 TableControl::~TableControl()
70 void TableControl::dispose()
72 CallEventListeners( VCLEVENT_OBJECT_DYING
);
74 m_pImpl
->setModel( PTableModel() );
75 m_pImpl
->disposeAccessible();
81 void TableControl::GetFocus()
83 if ( !m_pImpl
|| !m_pImpl
->getInputHandler()->GetFocus( *m_pImpl
) )
88 void TableControl::LoseFocus()
90 if ( !m_pImpl
|| !m_pImpl
->getInputHandler()->LoseFocus( *m_pImpl
) )
95 void TableControl::KeyInput( const KeyEvent
& rKEvt
)
97 if ( !m_pImpl
->getInputHandler()->KeyInput( *m_pImpl
, rKEvt
) )
98 Control::KeyInput( rKEvt
);
101 if ( m_pImpl
->isAccessibleAlive() )
103 m_pImpl
->commitCellEvent( AccessibleEventId::STATE_CHANGED
,
104 makeAny( AccessibleStateType::FOCUSED
),
107 // Huh? What the heck? Why do we unconditionally notify a STATE_CHANGE/FOCUSED after each and every
108 // (handled) key stroke?
110 m_pImpl
->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
,
114 // ditto: Why do we notify this unconditionally? We should find the right place to notify the
115 // ACTIVE_DESCENDANT_CHANGED event.
116 // Also, we should check if STATE_CHANGED/FOCUSED is really necessary: finally, the children are
117 // transient, aren't they?
124 void TableControl::StateChanged( StateChangedType i_nStateChange
)
126 Control::StateChanged( i_nStateChange
);
128 // forward certain settings to the data window
129 switch ( i_nStateChange
)
131 case StateChangedType::ControlFocus
:
132 m_pImpl
->invalidateSelectedRows();
135 case StateChangedType::ControlBackground
:
136 if ( IsControlBackground() )
137 getDataWindow().SetControlBackground( GetControlBackground() );
139 getDataWindow().SetControlBackground();
142 case StateChangedType::ControlForeground
:
143 if ( IsControlForeground() )
144 getDataWindow().SetControlForeground( GetControlForeground() );
146 getDataWindow().SetControlForeground();
149 case StateChangedType::ControlFont
:
150 if ( IsControlFont() )
151 getDataWindow().SetControlFont( GetControlFont() );
153 getDataWindow().SetControlFont();
160 void TableControl::Resize()
167 void TableControl::SetModel( PTableModel _pModel
)
169 m_pImpl
->setModel( _pModel
);
173 PTableModel
TableControl::GetModel() const
175 return m_pImpl
->getModel();
179 sal_Int32
TableControl::GetCurrentRow() const
181 return m_pImpl
->getCurrentRow();
185 sal_Int32
TableControl::GetCurrentColumn() const
187 return m_pImpl
->getCurrentColumn();
191 bool TableControl::GoTo( ColPos _nColumn
, RowPos _nRow
)
193 return m_pImpl
->goTo( _nColumn
, _nRow
);
197 bool TableControl::GoToCell(sal_Int32 _nColPos
, sal_Int32 _nRowPos
)
199 return m_pImpl
->goTo( _nColPos
, _nRowPos
);
203 sal_Int32
TableControl::GetSelectedRowCount() const
205 return sal_Int32( m_pImpl
->getSelectedRowCount() );
209 sal_Int32
TableControl::GetSelectedRowIndex( sal_Int32
const i_selectionIndex
) const
211 return sal_Int32( m_pImpl
->getSelectedRowIndex( i_selectionIndex
) );
215 bool TableControl::IsRowSelected( sal_Int32
const i_rowIndex
) const
217 return m_pImpl
->isRowSelected( i_rowIndex
);
221 void TableControl::SelectRow( RowPos
const i_rowIndex
, bool const i_select
)
223 ENSURE_OR_RETURN_VOID( ( i_rowIndex
>= 0 ) && ( i_rowIndex
< m_pImpl
->getModel()->getRowCount() ),
224 "TableControl::SelectRow: invalid row index!" );
228 if ( !m_pImpl
->markRowAsSelected( i_rowIndex
) )
234 m_pImpl
->markRowAsDeselected( i_rowIndex
);
237 m_pImpl
->invalidateRowRange( i_rowIndex
, i_rowIndex
);
242 void TableControl::SelectAllRows( bool const i_select
)
246 if ( !m_pImpl
->markAllRowsAsSelected() )
252 if ( !m_pImpl
->markAllRowsAsDeselected() )
259 // TODO: can't we do better than this, and invalidate only the rows which changed?
264 ITableControl
& TableControl::getTableControlInterface()
270 SelectionEngine
* TableControl::getSelEngine()
272 return m_pImpl
->getSelEngine();
276 vcl::Window
& TableControl::getDataWindow()
278 return m_pImpl
->getDataWindow();
282 Reference
< XAccessible
> TableControl::CreateAccessible()
284 vcl::Window
* pParent
= GetAccessibleParentWindow();
285 ENSURE_OR_RETURN( pParent
, "TableControl::CreateAccessible - parent not found", NULL
);
287 return m_pImpl
->getAccessible( *pParent
);
291 Reference
<XAccessible
> TableControl::CreateAccessibleControl( sal_Int32 _nIndex
)
294 DBG_ASSERT( false, "TableControl::CreateAccessibleControl: to be overwritten!" );
299 OUString
TableControl::GetAccessibleObjectName( AccessibleTableControlObjType eObjType
, sal_Int32 _nRow
, sal_Int32 _nCol
) const
305 case TCTYPE_GRIDCONTROL
:
306 aRetText
= "Grid control";
309 aRetText
= "Grid conrol";
311 case TCTYPE_ROWHEADERBAR
:
312 aRetText
= "RowHeaderBar";
314 case TCTYPE_COLUMNHEADERBAR
:
315 aRetText
= "ColumnHeaderBar";
317 case TCTYPE_TABLECELL
:
318 //the name of the cell constists of column name and row name if defined
319 //if the name is equal to cell content, it'll be read twice
320 if(GetModel()->hasColumnHeaders())
322 aRetText
= GetColumnName(_nCol
) + " , ";
324 if(GetModel()->hasRowHeaders())
326 aRetText
+= GetRowName(_nRow
) + " , ";
328 //aRetText = GetAccessibleCellText(_nRow, _nCol);
330 case TCTYPE_ROWHEADERCELL
:
331 aRetText
= GetRowName(_nRow
);
333 case TCTYPE_COLUMNHEADERCELL
:
334 aRetText
= GetColumnName(_nCol
);
337 OSL_FAIL("GridControl::GetAccessibleName: invalid enum!");
343 OUString
TableControl::GetAccessibleObjectDescription( AccessibleTableControlObjType eObjType
, sal_Int32
) const
348 case TCTYPE_GRIDCONTROL
:
349 aRetText
= "Grid control description";
352 aRetText
= "TABLE description";
354 case TCTYPE_ROWHEADERBAR
:
355 aRetText
= "ROWHEADERBAR description";
357 case TCTYPE_COLUMNHEADERBAR
:
358 aRetText
= "COLUMNHEADERBAR description";
360 case TCTYPE_TABLECELL
:
361 // the description of the cell consists of column name and row name if defined
362 // if the name is equal to cell content, it'll be read twice
363 if ( GetModel()->hasColumnHeaders() )
365 aRetText
= GetColumnName( GetCurrentColumn() ) + " , ";
367 if ( GetModel()->hasRowHeaders() )
369 aRetText
+= GetRowName( GetCurrentRow() );
372 case TCTYPE_ROWHEADERCELL
:
373 aRetText
= "ROWHEADERCELL description";
375 case TCTYPE_COLUMNHEADERCELL
:
376 aRetText
= "COLUMNHEADERCELL description";
383 OUString
TableControl::GetRowDescription( sal_Int32 _nRow
) const
386 return OUString( "row description" );
390 OUString
TableControl::GetRowName( sal_Int32 _nIndex
) const
393 GetModel()->getRowHeading( _nIndex
) >>= sRowName
;
398 OUString
TableControl::GetColumnDescription( sal_uInt16 _nColumn
) const
401 return OUString( "col description" );
405 OUString
TableControl::GetColumnName( sal_Int32 _nIndex
) const
407 return GetModel()->getColumnModel(_nIndex
)->getName();
411 ::com::sun::star::uno::Any
TableControl::GetCellContent( sal_Int32 _nRowPos
, sal_Int32 _nColPos
) const
414 GetModel()->getCellContent( _nColPos
, _nRowPos
, aCellContent
);
419 OUString
TableControl::GetAccessibleCellText( sal_Int32 _nRowPos
, sal_Int32 _nColPos
) const
421 return m_pImpl
->getCellContentAsString( _nRowPos
, _nColPos
);
425 void TableControl::FillAccessibleStateSet(
426 ::utl::AccessibleStateSetHelper
& rStateSet
,
427 AccessibleTableControlObjType eObjType
) const
431 case TCTYPE_GRIDCONTROL
:
434 rStateSet
.AddState( AccessibleStateType::FOCUSABLE
);
436 if ( m_pImpl
->getSelEngine()->GetSelectionMode() == MULTIPLE_SELECTION
)
437 rStateSet
.AddState( AccessibleStateType::MULTI_SELECTABLE
);
439 if ( HasChildPathFocus() )
440 rStateSet
.AddState( AccessibleStateType::FOCUSED
);
443 rStateSet
.AddState( AccessibleStateType::ACTIVE
);
445 if ( m_pImpl
->getDataWindow().IsEnabled() )
447 rStateSet
.AddState( AccessibleStateType::ENABLED
);
448 rStateSet
.AddState( AccessibleStateType::SENSITIVE
);
451 if ( IsReallyVisible() )
452 rStateSet
.AddState( AccessibleStateType::VISIBLE
);
454 if ( eObjType
== TCTYPE_TABLE
)
455 rStateSet
.AddState( AccessibleStateType::MANAGES_DESCENDANTS
);
458 case TCTYPE_ROWHEADERBAR
:
459 rStateSet
.AddState( AccessibleStateType::VISIBLE
);
460 rStateSet
.AddState( AccessibleStateType::MANAGES_DESCENDANTS
);
463 case TCTYPE_COLUMNHEADERBAR
:
464 rStateSet
.AddState( AccessibleStateType::VISIBLE
);
465 rStateSet
.AddState( AccessibleStateType::MANAGES_DESCENDANTS
);
468 case TCTYPE_TABLECELL
:
470 rStateSet
.AddState( AccessibleStateType::FOCUSABLE
);
471 if ( HasChildPathFocus() )
472 rStateSet
.AddState( AccessibleStateType::FOCUSED
);
473 rStateSet
.AddState( AccessibleStateType::ACTIVE
);
474 rStateSet
.AddState( AccessibleStateType::TRANSIENT
);
475 rStateSet
.AddState( AccessibleStateType::SELECTABLE
);
476 rStateSet
.AddState( AccessibleStateType::VISIBLE
);
477 rStateSet
.AddState( AccessibleStateType::SHOWING
);
478 if ( IsRowSelected( GetCurrentRow() ) )
479 // Hmm? Wouldn't we expect the affected row to be a parameter to this function?
480 rStateSet
.AddState( AccessibleStateType::SELECTED
);
484 case TCTYPE_ROWHEADERCELL
:
485 rStateSet
.AddState( AccessibleStateType::VISIBLE
);
486 rStateSet
.AddState( AccessibleStateType::TRANSIENT
);
489 case TCTYPE_COLUMNHEADERCELL
:
490 rStateSet
.AddState( AccessibleStateType::VISIBLE
);
496 void TableControl::commitCellEventIfAccessibleAlive( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
498 if ( m_pImpl
->isAccessibleAlive() )
499 m_pImpl
->commitCellEvent( i_eventID
, i_newValue
, i_oldValue
);
503 void TableControl::commitTableEventIfAccessibleAlive( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
505 if ( m_pImpl
->isAccessibleAlive() )
506 m_pImpl
->commitTableEvent( i_eventID
, i_newValue
, i_oldValue
);
510 Rectangle
TableControl::GetWindowExtentsRelative( vcl::Window
*pRelativeWindow
) const
512 return Control::GetWindowExtentsRelative( pRelativeWindow
);
516 void TableControl::GrabFocus()
518 Control::GrabFocus();
522 Reference
< XAccessible
> TableControl::GetAccessible( bool bCreate
)
524 return Control::GetAccessible( bCreate
);
528 vcl::Window
* TableControl::GetAccessibleParentWindow() const
530 return Control::GetAccessibleParentWindow();
534 vcl::Window
* TableControl::GetWindowInstance()
540 bool TableControl::HasRowHeader()
542 return GetModel()->hasRowHeaders();
546 bool TableControl::HasColHeader()
548 return GetModel()->hasColumnHeaders();
552 sal_Int32
TableControl::GetAccessibleControlCount() const
554 // TC_TABLE is always defined, no matter whether empty or not
556 if ( GetModel()->hasRowHeaders() )
558 if ( GetModel()->hasColumnHeaders() )
564 bool TableControl::ConvertPointToControlIndex( sal_Int32
& _rnIndex
, const Point
& _rPoint
)
566 sal_Int32 nRow
= m_pImpl
->getRowAtPoint( _rPoint
);
567 sal_Int32 nCol
= m_pImpl
->getColAtPoint( _rPoint
);
568 _rnIndex
= nRow
* GetColumnCount() + nCol
;
573 long TableControl::GetRowCount() const
575 return GetModel()->getRowCount();
579 long TableControl::GetColumnCount() const
581 return GetModel()->getColumnCount();
585 bool TableControl::HasRowHeader() const
587 return GetModel()->hasRowHeaders();
591 bool TableControl::ConvertPointToCellAddress( sal_Int32
& _rnRow
, sal_Int32
& _rnColPos
, const Point
& _rPoint
)
593 _rnRow
= m_pImpl
->getRowAtPoint( _rPoint
);
594 _rnColPos
= m_pImpl
->getColAtPoint( _rPoint
);
599 void TableControl::FillAccessibleStateSetForCell( ::utl::AccessibleStateSetHelper
& _rStateSet
, sal_Int32 _nRow
, sal_uInt16 _nColumnPos
) const
601 if ( IsRowSelected( _nRow
) )
602 _rStateSet
.AddState( AccessibleStateType::SELECTED
);
603 if ( HasChildPathFocus() )
604 _rStateSet
.AddState( AccessibleStateType::FOCUSED
);
605 else // only transient when column is not focused
606 _rStateSet
.AddState( AccessibleStateType::TRANSIENT
);
608 _rStateSet
.AddState( AccessibleStateType::VISIBLE
);
609 _rStateSet
.AddState( AccessibleStateType::SHOWING
);
610 _rStateSet
.AddState( AccessibleStateType::ENABLED
);
611 _rStateSet
.AddState( AccessibleStateType::SENSITIVE
);
612 _rStateSet
.AddState( AccessibleStateType::ACTIVE
);
618 Rectangle
TableControl::GetFieldCharacterBounds(sal_Int32 _nRow
,sal_Int32 _nColumnPos
,sal_Int32 nIndex
)
622 return GetCharacterBounds(nIndex
);
626 sal_Int32
TableControl::GetFieldIndexAtPoint(sal_Int32 _nRow
,sal_Int32 _nColumnPos
,const Point
& _rPoint
)
630 return GetIndexForPoint(_rPoint
);
634 Rectangle
TableControl::calcHeaderRect(bool _bIsColumnBar
, bool _bOnScreen
)
637 return m_pImpl
->calcHeaderRect( !_bIsColumnBar
);
641 Rectangle
TableControl::calcHeaderCellRect( bool _bIsColumnBar
, sal_Int32 nPos
)
643 return m_pImpl
->calcHeaderCellRect( _bIsColumnBar
, nPos
);
647 Rectangle
TableControl::calcTableRect(bool _bOnScreen
)
650 return m_pImpl
->calcTableRect();
654 Rectangle
TableControl::calcCellRect( sal_Int32 _nRowPos
, sal_Int32 _nColPos
)
656 return m_pImpl
->calcCellRect( _nRowPos
, _nColPos
);
660 IMPL_LINK_NOARG(TableControl
, ImplSelectHdl
)
667 void TableControl::Select()
669 ImplCallEventListenersAndHandler( VCLEVENT_TABLEROW_SELECT
, m_pImpl
->getSelectHandler(), this );
671 if ( m_pImpl
->isAccessibleAlive() )
673 m_pImpl
->commitAccessibleEvent( AccessibleEventId::SELECTION_CHANGED
, Any(), Any() );
675 m_pImpl
->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
, Any(), Any() );
676 // TODO: why do we notify this when the *selection* changed? Shouldn't we find a better place for this,
677 // actually, when the active descendant, i.e. the current cell, *really* changed?
681 }} // namespace svt::table
684 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */