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 <controls/table/tablecontrol.hxx>
22 #include "tablecontrol_impl.hxx"
23 #include "tabledatawindow.hxx"
25 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
26 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
28 #include <sal/log.hxx>
29 #include <comphelper/diagnose_ex.hxx>
30 #include <vcl/settings.hxx>
31 #include <vcl/vclevent.hxx>
33 using namespace ::com::sun::star::uno
;
34 using ::com::sun::star::accessibility::XAccessible
;
35 using namespace ::com::sun::star::accessibility
;
41 namespace AccessibleEventId
= ::com::sun::star::accessibility::AccessibleEventId
;
47 TableControl::TableControl( vcl::Window
* _pParent
, WinBits _nStyle
)
48 :Control( _pParent
, _nStyle
)
49 ,m_pImpl( std::make_shared
<TableControl_Impl
>( *this ) )
51 TableDataWindow
& rDataWindow
= m_pImpl
->getDataWindow();
52 rDataWindow
.SetSelectHdl( LINK( this, TableControl
, ImplSelectHdl
) );
54 // by default, use the background as determined by the style settings
55 const Color
aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() );
56 SetBackground( Wallpaper( aWindowColor
) );
57 GetOutDev()->SetFillColor( aWindowColor
);
59 SetCompoundControl( true );
63 TableControl::~TableControl()
68 void TableControl::dispose()
70 CallEventListeners( VclEventId::ObjectDying
);
72 m_pImpl
->setModel( PTableModel() );
73 m_pImpl
->disposeAccessible();
79 void TableControl::GetFocus()
81 if ( !m_pImpl
|| !m_pImpl
->getInputHandler()->GetFocus( *m_pImpl
) )
86 void TableControl::LoseFocus()
88 if ( !m_pImpl
|| !m_pImpl
->getInputHandler()->LoseFocus( *m_pImpl
) )
93 void TableControl::KeyInput( const KeyEvent
& rKEvt
)
95 if ( !m_pImpl
->getInputHandler()->KeyInput( *m_pImpl
, rKEvt
) )
96 Control::KeyInput( rKEvt
);
99 if ( m_pImpl
->isAccessibleAlive() )
101 m_pImpl
->commitCellEvent( AccessibleEventId::STATE_CHANGED
,
102 Any( AccessibleStateType::FOCUSED
),
105 // Huh? What the heck? Why do we unconditionally notify a STATE_CHANGE/FOCUSED after each and every
106 // (handled) key stroke?
108 m_pImpl
->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
,
112 // ditto: Why do we notify this unconditionally? We should find the right place to notify the
113 // ACTIVE_DESCENDANT_CHANGED event.
114 // Also, we should check if STATE_CHANGED/FOCUSED is really necessary: finally, the children are
115 // transient, aren't they?
121 void TableControl::StateChanged( StateChangedType i_nStateChange
)
123 Control::StateChanged( i_nStateChange
);
125 // forward certain settings to the data window
126 switch ( i_nStateChange
)
128 case StateChangedType::ControlFocus
:
129 m_pImpl
->invalidateSelectedRows();
132 case StateChangedType::ControlBackground
:
133 if ( IsControlBackground() )
134 getDataWindow().SetControlBackground( GetControlBackground() );
136 getDataWindow().SetControlBackground();
139 case StateChangedType::ControlForeground
:
140 if ( IsControlForeground() )
141 getDataWindow().SetControlForeground( GetControlForeground() );
143 getDataWindow().SetControlForeground();
146 case StateChangedType::ControlFont
:
147 if ( IsControlFont() )
148 getDataWindow().SetControlFont( GetControlFont() );
150 getDataWindow().SetControlFont();
157 void TableControl::Resize()
164 void TableControl::SetModel( const PTableModel
& _pModel
)
166 m_pImpl
->setModel( _pModel
);
170 PTableModel
TableControl::GetModel() const
172 return m_pImpl
->getModel();
176 sal_Int32
TableControl::GetCurrentRow() const
178 return m_pImpl
->getCurrentRow();
182 sal_Int32
TableControl::GetCurrentColumn() const
184 return m_pImpl
->getCurrentColumn();
188 void TableControl::GoTo( ColPos _nColumn
, RowPos _nRow
)
190 m_pImpl
->goTo( _nColumn
, _nRow
);
194 void TableControl::GoToCell(sal_Int32 _nColPos
, sal_Int32 _nRowPos
)
196 m_pImpl
->goTo( _nColPos
, _nRowPos
);
200 sal_Int32
TableControl::GetSelectedRowCount() const
202 return sal_Int32( m_pImpl
->getSelectedRowCount() );
206 sal_Int32
TableControl::GetSelectedRowIndex( sal_Int32
const i_selectionIndex
) const
208 return m_pImpl
->getSelectedRowIndex( i_selectionIndex
);
212 bool TableControl::IsRowSelected( sal_Int32
const i_rowIndex
) const
214 return m_pImpl
->isRowSelected( i_rowIndex
);
218 void TableControl::SelectRow( sal_Int32
const i_rowIndex
, bool const i_select
)
220 ENSURE_OR_RETURN_VOID( ( i_rowIndex
>= 0 ) && ( i_rowIndex
< m_pImpl
->getModel()->getRowCount() ),
221 "TableControl::SelectRow: invalid row index!" );
225 if ( !m_pImpl
->markRowAsSelected( i_rowIndex
) )
231 m_pImpl
->markRowAsDeselected( i_rowIndex
);
234 m_pImpl
->invalidateRowRange( i_rowIndex
, i_rowIndex
);
239 void TableControl::SelectAllRows( bool const i_select
)
243 if ( !m_pImpl
->markAllRowsAsSelected() )
249 if ( !m_pImpl
->markAllRowsAsDeselected() )
256 // TODO: can't we do better than this, and invalidate only the rows which changed?
261 ITableControl
& TableControl::getTableControlInterface()
267 SelectionEngine
* TableControl::getSelEngine()
269 return m_pImpl
->getSelEngine();
273 vcl::Window
& TableControl::getDataWindow()
275 return m_pImpl
->getDataWindow();
279 Reference
< XAccessible
> TableControl::CreateAccessible()
281 vcl::Window
* pParent
= GetAccessibleParentWindow();
282 ENSURE_OR_RETURN( pParent
, "TableControl::CreateAccessible - parent not found", nullptr );
284 return m_pImpl
->getAccessible( *pParent
);
288 Reference
<XAccessible
> TableControl::CreateAccessibleControl( sal_Int32
)
290 SAL_WARN( "svtools", "TableControl::CreateAccessibleControl: to be overwritten!" );
295 OUString
TableControl::GetAccessibleObjectName( vcl::table::AccessibleTableControlObjType eObjType
, sal_Int32 _nRow
, sal_Int32 _nCol
) const
301 case vcl::table::AccessibleTableControlObjType::GRIDCONTROL
:
302 aRetText
= "Grid control";
304 case vcl::table::AccessibleTableControlObjType::TABLE
:
305 aRetText
= "Grid control";
307 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR
:
308 aRetText
= "RowHeaderBar";
310 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR
:
311 aRetText
= "ColumnHeaderBar";
313 case vcl::table::AccessibleTableControlObjType::TABLECELL
:
314 //the name of the cell consists of column name and row name if defined
315 //if the name is equal to cell content, it'll be read twice
316 if(GetModel()->hasColumnHeaders())
318 aRetText
= GetColumnName(_nCol
) + " , ";
320 if(GetModel()->hasRowHeaders())
322 aRetText
+= GetRowName(_nRow
) + " , ";
324 //aRetText = GetAccessibleCellText(_nRow, _nCol);
326 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL
:
327 aRetText
= GetRowName(_nRow
);
329 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL
:
330 aRetText
= GetColumnName(_nCol
);
333 OSL_FAIL("GridControl::GetAccessibleName: invalid enum!");
339 OUString
TableControl::GetAccessibleObjectDescription( vcl::table::AccessibleTableControlObjType eObjType
) const
344 case vcl::table::AccessibleTableControlObjType::GRIDCONTROL
:
345 aRetText
= "Grid control description";
347 case vcl::table::AccessibleTableControlObjType::TABLE
:
348 aRetText
= "TABLE description";
350 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR
:
351 aRetText
= "ROWHEADERBAR description";
353 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR
:
354 aRetText
= "COLUMNHEADERBAR description";
356 case vcl::table::AccessibleTableControlObjType::TABLECELL
:
357 // the description of the cell consists of column name and row name if defined
358 // if the name is equal to cell content, it'll be read twice
359 if ( GetModel()->hasColumnHeaders() )
361 aRetText
= GetColumnName( GetCurrentColumn() ) + " , ";
363 if ( GetModel()->hasRowHeaders() )
365 aRetText
+= GetRowName( GetCurrentRow() );
368 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL
:
369 aRetText
= "ROWHEADERCELL description";
371 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL
:
372 aRetText
= "COLUMNHEADERCELL description";
379 OUString
TableControl::GetRowName( sal_Int32 _nIndex
) const
382 GetModel()->getRowHeading( _nIndex
) >>= sRowName
;
387 OUString
TableControl::GetColumnName( sal_Int32 _nIndex
) const
389 return GetModel()->getColumnModel(_nIndex
)->getName();
393 OUString
TableControl::GetAccessibleCellText( sal_Int32 _nRowPos
, sal_Int32 _nColPos
) const
395 return m_pImpl
->getCellContentAsString( _nRowPos
, _nColPos
);
399 void TableControl::FillAccessibleStateSet(
400 sal_Int64
& rStateSet
,
401 vcl::table::AccessibleTableControlObjType eObjType
) const
405 case vcl::table::AccessibleTableControlObjType::GRIDCONTROL
:
406 case vcl::table::AccessibleTableControlObjType::TABLE
:
408 rStateSet
|= AccessibleStateType::FOCUSABLE
;
410 if ( m_pImpl
->getSelEngine()->GetSelectionMode() == SelectionMode::Multiple
)
411 rStateSet
|= AccessibleStateType::MULTI_SELECTABLE
;
413 if ( HasChildPathFocus() )
414 rStateSet
|= AccessibleStateType::FOCUSED
;
417 rStateSet
|= AccessibleStateType::ACTIVE
;
419 if ( m_pImpl
->getDataWindow().IsEnabled() )
421 rStateSet
|= AccessibleStateType::ENABLED
;
422 rStateSet
|= AccessibleStateType::SENSITIVE
;
425 if ( IsReallyVisible() )
426 rStateSet
|= AccessibleStateType::VISIBLE
;
428 if ( eObjType
== vcl::table::AccessibleTableControlObjType::TABLE
)
429 rStateSet
|= AccessibleStateType::MANAGES_DESCENDANTS
;
432 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR
:
433 rStateSet
|= AccessibleStateType::VISIBLE
;
434 rStateSet
|= AccessibleStateType::MANAGES_DESCENDANTS
;
437 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR
:
438 rStateSet
|= AccessibleStateType::VISIBLE
;
439 rStateSet
|= AccessibleStateType::MANAGES_DESCENDANTS
;
442 case vcl::table::AccessibleTableControlObjType::TABLECELL
:
444 rStateSet
|= AccessibleStateType::FOCUSABLE
;
445 if ( HasChildPathFocus() )
446 rStateSet
|= AccessibleStateType::FOCUSED
;
447 rStateSet
|= AccessibleStateType::ACTIVE
;
448 rStateSet
|= AccessibleStateType::TRANSIENT
;
449 rStateSet
|= AccessibleStateType::SELECTABLE
;
450 rStateSet
|= AccessibleStateType::VISIBLE
;
451 rStateSet
|= AccessibleStateType::SHOWING
;
452 if ( IsRowSelected( GetCurrentRow() ) )
453 // Hmm? Wouldn't we expect the affected row to be a parameter to this function?
454 rStateSet
|= AccessibleStateType::SELECTED
;
458 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL
:
459 rStateSet
|= AccessibleStateType::VISIBLE
;
460 rStateSet
|= AccessibleStateType::TRANSIENT
;
463 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL
:
464 rStateSet
|= AccessibleStateType::VISIBLE
;
469 void TableControl::commitCellEventIfAccessibleAlive( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
471 if ( m_pImpl
->isAccessibleAlive() )
472 m_pImpl
->commitCellEvent( i_eventID
, i_newValue
, i_oldValue
);
475 void TableControl::commitTableEventIfAccessibleAlive( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
477 if ( m_pImpl
->isAccessibleAlive() )
478 m_pImpl
->commitTableEvent( i_eventID
, i_newValue
, i_oldValue
);
481 AbsoluteScreenPixelRectangle
TableControl::GetWindowExtentsAbsolute() const
483 return Control::GetWindowExtentsAbsolute();
486 tools::Rectangle
TableControl::GetWindowExtentsRelative(const vcl::Window
& rRelativeWindow
) const
488 return Control::GetWindowExtentsRelative( rRelativeWindow
);
491 void TableControl::GrabFocus()
493 Control::GrabFocus();
496 Reference
< XAccessible
> TableControl::GetAccessible()
498 return Control::GetAccessible();
501 vcl::Window
* TableControl::GetAccessibleParentWindow() const
503 return Control::GetAccessibleParentWindow();
506 vcl::Window
* TableControl::GetWindowInstance()
512 bool TableControl::HasRowHeader()
514 return GetModel()->hasRowHeaders();
518 bool TableControl::HasColHeader()
520 return GetModel()->hasColumnHeaders();
524 sal_Int32
TableControl::GetAccessibleControlCount() const
526 // TC_TABLE is always defined, no matter whether empty or not
528 if ( GetModel()->hasRowHeaders() )
530 if ( GetModel()->hasColumnHeaders() )
536 bool TableControl::ConvertPointToControlIndex( sal_Int32
& _rnIndex
, const Point
& _rPoint
)
538 sal_Int32 nRow
= m_pImpl
->getRowAtPoint( _rPoint
);
539 sal_Int32 nCol
= m_pImpl
->getColAtPoint( _rPoint
);
540 _rnIndex
= nRow
* GetColumnCount() + nCol
;
545 sal_Int32
TableControl::GetRowCount() const
547 return GetModel()->getRowCount();
551 sal_Int32
TableControl::GetColumnCount() const
553 return GetModel()->getColumnCount();
557 bool TableControl::ConvertPointToCellAddress( sal_Int32
& _rnRow
, sal_Int32
& _rnColPos
, const Point
& _rPoint
)
559 _rnRow
= m_pImpl
->getRowAtPoint( _rPoint
);
560 _rnColPos
= m_pImpl
->getColAtPoint( _rPoint
);
565 void TableControl::FillAccessibleStateSetForCell( sal_Int64
& _rStateSet
, sal_Int32 _nRow
, sal_uInt16
) const
567 if ( IsRowSelected( _nRow
) )
568 _rStateSet
|= AccessibleStateType::SELECTED
;
569 if ( HasChildPathFocus() )
570 _rStateSet
|= AccessibleStateType::FOCUSED
;
571 else // only transient when column is not focused
572 _rStateSet
|= AccessibleStateType::TRANSIENT
;
574 _rStateSet
|= AccessibleStateType::VISIBLE
;
575 _rStateSet
|= AccessibleStateType::SHOWING
;
576 _rStateSet
|= AccessibleStateType::ENABLED
;
577 _rStateSet
|= AccessibleStateType::SENSITIVE
;
578 _rStateSet
|= AccessibleStateType::ACTIVE
;
582 tools::Rectangle
TableControl::GetFieldCharacterBounds(sal_Int32
,sal_Int32
,sal_Int32 nIndex
)
584 return GetCharacterBounds(nIndex
);
588 sal_Int32
TableControl::GetFieldIndexAtPoint(sal_Int32
,sal_Int32
,const Point
& _rPoint
)
590 return GetIndexForPoint(_rPoint
);
594 tools::Rectangle
TableControl::calcHeaderRect(bool _bIsColumnBar
)
596 return m_pImpl
->calcHeaderRect( !_bIsColumnBar
);
600 tools::Rectangle
TableControl::calcHeaderCellRect( bool _bIsColumnBar
, sal_Int32 nPos
)
602 return m_pImpl
->calcHeaderCellRect( _bIsColumnBar
, nPos
);
606 tools::Rectangle
TableControl::calcTableRect()
608 return m_pImpl
->calcTableRect();
612 tools::Rectangle
TableControl::calcCellRect( sal_Int32 _nRowPos
, sal_Int32 _nColPos
)
614 return m_pImpl
->calcCellRect( _nRowPos
, _nColPos
);
618 IMPL_LINK_NOARG(TableControl
, ImplSelectHdl
, LinkParamNone
*, void)
624 void TableControl::Select()
626 ImplCallEventListenersAndHandler( VclEventId::TableRowSelect
, nullptr );
628 if ( m_pImpl
->isAccessibleAlive() )
630 m_pImpl
->commitAccessibleEvent( AccessibleEventId::SELECTION_CHANGED
);
632 m_pImpl
->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
, Any(), Any() );
633 // TODO: why do we notify this when the *selection* changed? Shouldn't we find a better place for this,
634 // actually, when the active descendant, i.e. the current cell, *really* changed?
638 } // namespace svt::table
641 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */