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
;
36 using namespace ::com::sun::star::lang
;
42 namespace AccessibleEventId
= ::com::sun::star::accessibility::AccessibleEventId
;
48 TableControl::TableControl( vcl::Window
* _pParent
, WinBits _nStyle
)
49 :Control( _pParent
, _nStyle
)
50 ,m_pImpl( std::make_shared
<TableControl_Impl
>( *this ) )
52 TableDataWindow
& rDataWindow
= m_pImpl
->getDataWindow();
53 rDataWindow
.SetSelectHdl( LINK( this, TableControl
, ImplSelectHdl
) );
55 // by default, use the background as determined by the style settings
56 const Color
aWindowColor( GetSettings().GetStyleSettings().GetFieldColor() );
57 SetBackground( Wallpaper( aWindowColor
) );
58 GetOutDev()->SetFillColor( aWindowColor
);
60 SetCompoundControl( true );
64 TableControl::~TableControl()
69 void TableControl::dispose()
71 CallEventListeners( VclEventId::ObjectDying
);
73 m_pImpl
->setModel( PTableModel() );
74 m_pImpl
->disposeAccessible();
80 void TableControl::GetFocus()
82 if ( !m_pImpl
|| !m_pImpl
->getInputHandler()->GetFocus( *m_pImpl
) )
87 void TableControl::LoseFocus()
89 if ( !m_pImpl
|| !m_pImpl
->getInputHandler()->LoseFocus( *m_pImpl
) )
94 void TableControl::KeyInput( const KeyEvent
& rKEvt
)
96 if ( !m_pImpl
->getInputHandler()->KeyInput( *m_pImpl
, rKEvt
) )
97 Control::KeyInput( rKEvt
);
100 if ( m_pImpl
->isAccessibleAlive() )
102 m_pImpl
->commitCellEvent( AccessibleEventId::STATE_CHANGED
,
103 Any( AccessibleStateType::FOCUSED
),
106 // Huh? What the heck? Why do we unconditionally notify a STATE_CHANGE/FOCUSED after each and every
107 // (handled) key stroke?
109 m_pImpl
->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
,
113 // ditto: Why do we notify this unconditionally? We should find the right place to notify the
114 // ACTIVE_DESCENDANT_CHANGED event.
115 // Also, we should check if STATE_CHANGED/FOCUSED is really necessary: finally, the children are
116 // transient, aren't they?
122 void TableControl::StateChanged( StateChangedType i_nStateChange
)
124 Control::StateChanged( i_nStateChange
);
126 // forward certain settings to the data window
127 switch ( i_nStateChange
)
129 case StateChangedType::ControlFocus
:
130 m_pImpl
->invalidateSelectedRows();
133 case StateChangedType::ControlBackground
:
134 if ( IsControlBackground() )
135 getDataWindow().SetControlBackground( GetControlBackground() );
137 getDataWindow().SetControlBackground();
140 case StateChangedType::ControlForeground
:
141 if ( IsControlForeground() )
142 getDataWindow().SetControlForeground( GetControlForeground() );
144 getDataWindow().SetControlForeground();
147 case StateChangedType::ControlFont
:
148 if ( IsControlFont() )
149 getDataWindow().SetControlFont( GetControlFont() );
151 getDataWindow().SetControlFont();
158 void TableControl::Resize()
165 void TableControl::SetModel( const PTableModel
& _pModel
)
167 m_pImpl
->setModel( _pModel
);
171 PTableModel
TableControl::GetModel() const
173 return m_pImpl
->getModel();
177 sal_Int32
TableControl::GetCurrentRow() const
179 return m_pImpl
->getCurrentRow();
183 sal_Int32
TableControl::GetCurrentColumn() const
185 return m_pImpl
->getCurrentColumn();
189 void TableControl::GoTo( ColPos _nColumn
, RowPos _nRow
)
191 m_pImpl
->goTo( _nColumn
, _nRow
);
195 void TableControl::GoToCell(sal_Int32 _nColPos
, sal_Int32 _nRowPos
)
197 m_pImpl
->goTo( _nColPos
, _nRowPos
);
201 sal_Int32
TableControl::GetSelectedRowCount() const
203 return sal_Int32( m_pImpl
->getSelectedRowCount() );
207 sal_Int32
TableControl::GetSelectedRowIndex( sal_Int32
const i_selectionIndex
) const
209 return m_pImpl
->getSelectedRowIndex( i_selectionIndex
);
213 bool TableControl::IsRowSelected( sal_Int32
const i_rowIndex
) const
215 return m_pImpl
->isRowSelected( i_rowIndex
);
219 void TableControl::SelectRow( sal_Int32
const i_rowIndex
, bool const i_select
)
221 ENSURE_OR_RETURN_VOID( ( i_rowIndex
>= 0 ) && ( i_rowIndex
< m_pImpl
->getModel()->getRowCount() ),
222 "TableControl::SelectRow: invalid row index!" );
226 if ( !m_pImpl
->markRowAsSelected( i_rowIndex
) )
232 m_pImpl
->markRowAsDeselected( i_rowIndex
);
235 m_pImpl
->invalidateRowRange( i_rowIndex
, i_rowIndex
);
240 void TableControl::SelectAllRows( bool const i_select
)
244 if ( !m_pImpl
->markAllRowsAsSelected() )
250 if ( !m_pImpl
->markAllRowsAsDeselected() )
257 // TODO: can't we do better than this, and invalidate only the rows which changed?
262 ITableControl
& TableControl::getTableControlInterface()
268 SelectionEngine
* TableControl::getSelEngine()
270 return m_pImpl
->getSelEngine();
274 vcl::Window
& TableControl::getDataWindow()
276 return m_pImpl
->getDataWindow();
280 Reference
< XAccessible
> TableControl::CreateAccessible()
282 vcl::Window
* pParent
= GetAccessibleParentWindow();
283 ENSURE_OR_RETURN( pParent
, "TableControl::CreateAccessible - parent not found", nullptr );
285 return m_pImpl
->getAccessible( *pParent
);
289 Reference
<XAccessible
> TableControl::CreateAccessibleControl( sal_Int32
)
291 SAL_WARN( "svtools", "TableControl::CreateAccessibleControl: to be overwritten!" );
296 OUString
TableControl::GetAccessibleObjectName( vcl::table::AccessibleTableControlObjType eObjType
, sal_Int32 _nRow
, sal_Int32 _nCol
) const
302 case vcl::table::AccessibleTableControlObjType::GRIDCONTROL
:
303 aRetText
= "Grid control";
305 case vcl::table::AccessibleTableControlObjType::TABLE
:
306 aRetText
= "Grid control";
308 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR
:
309 aRetText
= "RowHeaderBar";
311 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR
:
312 aRetText
= "ColumnHeaderBar";
314 case vcl::table::AccessibleTableControlObjType::TABLECELL
:
315 //the name of the cell consists of column name and row name if defined
316 //if the name is equal to cell content, it'll be read twice
317 if(GetModel()->hasColumnHeaders())
319 aRetText
= GetColumnName(_nCol
) + " , ";
321 if(GetModel()->hasRowHeaders())
323 aRetText
+= GetRowName(_nRow
) + " , ";
325 //aRetText = GetAccessibleCellText(_nRow, _nCol);
327 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL
:
328 aRetText
= GetRowName(_nRow
);
330 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL
:
331 aRetText
= GetColumnName(_nCol
);
334 OSL_FAIL("GridControl::GetAccessibleName: invalid enum!");
340 OUString
TableControl::GetAccessibleObjectDescription( vcl::table::AccessibleTableControlObjType eObjType
) const
345 case vcl::table::AccessibleTableControlObjType::GRIDCONTROL
:
346 aRetText
= "Grid control description";
348 case vcl::table::AccessibleTableControlObjType::TABLE
:
349 aRetText
= "TABLE description";
351 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR
:
352 aRetText
= "ROWHEADERBAR description";
354 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR
:
355 aRetText
= "COLUMNHEADERBAR description";
357 case vcl::table::AccessibleTableControlObjType::TABLECELL
:
358 // the description of the cell consists of column name and row name if defined
359 // if the name is equal to cell content, it'll be read twice
360 if ( GetModel()->hasColumnHeaders() )
362 aRetText
= GetColumnName( GetCurrentColumn() ) + " , ";
364 if ( GetModel()->hasRowHeaders() )
366 aRetText
+= GetRowName( GetCurrentRow() );
369 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL
:
370 aRetText
= "ROWHEADERCELL description";
372 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL
:
373 aRetText
= "COLUMNHEADERCELL description";
380 OUString
TableControl::GetRowName( sal_Int32 _nIndex
) const
383 GetModel()->getRowHeading( _nIndex
) >>= sRowName
;
388 OUString
TableControl::GetColumnName( sal_Int32 _nIndex
) const
390 return GetModel()->getColumnModel(_nIndex
)->getName();
394 OUString
TableControl::GetAccessibleCellText( sal_Int32 _nRowPos
, sal_Int32 _nColPos
) const
396 return m_pImpl
->getCellContentAsString( _nRowPos
, _nColPos
);
400 void TableControl::FillAccessibleStateSet(
401 sal_Int64
& rStateSet
,
402 vcl::table::AccessibleTableControlObjType eObjType
) const
406 case vcl::table::AccessibleTableControlObjType::GRIDCONTROL
:
407 case vcl::table::AccessibleTableControlObjType::TABLE
:
409 rStateSet
|= AccessibleStateType::FOCUSABLE
;
411 if ( m_pImpl
->getSelEngine()->GetSelectionMode() == SelectionMode::Multiple
)
412 rStateSet
|= AccessibleStateType::MULTI_SELECTABLE
;
414 if ( HasChildPathFocus() )
415 rStateSet
|= AccessibleStateType::FOCUSED
;
418 rStateSet
|= AccessibleStateType::ACTIVE
;
420 if ( m_pImpl
->getDataWindow().IsEnabled() )
422 rStateSet
|= AccessibleStateType::ENABLED
;
423 rStateSet
|= AccessibleStateType::SENSITIVE
;
426 if ( IsReallyVisible() )
427 rStateSet
|= AccessibleStateType::VISIBLE
;
429 if ( eObjType
== vcl::table::AccessibleTableControlObjType::TABLE
)
430 rStateSet
|= AccessibleStateType::MANAGES_DESCENDANTS
;
433 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR
:
434 rStateSet
|= AccessibleStateType::VISIBLE
;
435 rStateSet
|= AccessibleStateType::MANAGES_DESCENDANTS
;
438 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR
:
439 rStateSet
|= AccessibleStateType::VISIBLE
;
440 rStateSet
|= AccessibleStateType::MANAGES_DESCENDANTS
;
443 case vcl::table::AccessibleTableControlObjType::TABLECELL
:
445 rStateSet
|= AccessibleStateType::FOCUSABLE
;
446 if ( HasChildPathFocus() )
447 rStateSet
|= AccessibleStateType::FOCUSED
;
448 rStateSet
|= AccessibleStateType::ACTIVE
;
449 rStateSet
|= AccessibleStateType::TRANSIENT
;
450 rStateSet
|= AccessibleStateType::SELECTABLE
;
451 rStateSet
|= AccessibleStateType::VISIBLE
;
452 rStateSet
|= AccessibleStateType::SHOWING
;
453 if ( IsRowSelected( GetCurrentRow() ) )
454 // Hmm? Wouldn't we expect the affected row to be a parameter to this function?
455 rStateSet
|= AccessibleStateType::SELECTED
;
459 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL
:
460 rStateSet
|= AccessibleStateType::VISIBLE
;
461 rStateSet
|= AccessibleStateType::TRANSIENT
;
464 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL
:
465 rStateSet
|= AccessibleStateType::VISIBLE
;
470 void TableControl::commitCellEventIfAccessibleAlive( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
472 if ( m_pImpl
->isAccessibleAlive() )
473 m_pImpl
->commitCellEvent( i_eventID
, i_newValue
, i_oldValue
);
476 void TableControl::commitTableEventIfAccessibleAlive( sal_Int16
const i_eventID
, const Any
& i_newValue
, const Any
& i_oldValue
)
478 if ( m_pImpl
->isAccessibleAlive() )
479 m_pImpl
->commitTableEvent( i_eventID
, i_newValue
, i_oldValue
);
482 AbsoluteScreenPixelRectangle
TableControl::GetWindowExtentsAbsolute() const
484 return Control::GetWindowExtentsAbsolute();
487 tools::Rectangle
TableControl::GetWindowExtentsRelative(const vcl::Window
& rRelativeWindow
) const
489 return Control::GetWindowExtentsRelative( rRelativeWindow
);
492 void TableControl::GrabFocus()
494 Control::GrabFocus();
497 Reference
< XAccessible
> TableControl::GetAccessible()
499 return Control::GetAccessible();
502 vcl::Window
* TableControl::GetAccessibleParentWindow() const
504 return Control::GetAccessibleParentWindow();
507 vcl::Window
* TableControl::GetWindowInstance()
513 bool TableControl::HasRowHeader()
515 return GetModel()->hasRowHeaders();
519 bool TableControl::HasColHeader()
521 return GetModel()->hasColumnHeaders();
525 sal_Int32
TableControl::GetAccessibleControlCount() const
527 // TC_TABLE is always defined, no matter whether empty or not
529 if ( GetModel()->hasRowHeaders() )
531 if ( GetModel()->hasColumnHeaders() )
537 bool TableControl::ConvertPointToControlIndex( sal_Int32
& _rnIndex
, const Point
& _rPoint
)
539 sal_Int32 nRow
= m_pImpl
->getRowAtPoint( _rPoint
);
540 sal_Int32 nCol
= m_pImpl
->getColAtPoint( _rPoint
);
541 _rnIndex
= nRow
* GetColumnCount() + nCol
;
546 sal_Int32
TableControl::GetRowCount() const
548 return GetModel()->getRowCount();
552 sal_Int32
TableControl::GetColumnCount() const
554 return GetModel()->getColumnCount();
558 bool TableControl::ConvertPointToCellAddress( sal_Int32
& _rnRow
, sal_Int32
& _rnColPos
, const Point
& _rPoint
)
560 _rnRow
= m_pImpl
->getRowAtPoint( _rPoint
);
561 _rnColPos
= m_pImpl
->getColAtPoint( _rPoint
);
566 void TableControl::FillAccessibleStateSetForCell( sal_Int64
& _rStateSet
, sal_Int32 _nRow
, sal_uInt16
) const
568 if ( IsRowSelected( _nRow
) )
569 _rStateSet
|= AccessibleStateType::SELECTED
;
570 if ( HasChildPathFocus() )
571 _rStateSet
|= AccessibleStateType::FOCUSED
;
572 else // only transient when column is not focused
573 _rStateSet
|= AccessibleStateType::TRANSIENT
;
575 _rStateSet
|= AccessibleStateType::VISIBLE
;
576 _rStateSet
|= AccessibleStateType::SHOWING
;
577 _rStateSet
|= AccessibleStateType::ENABLED
;
578 _rStateSet
|= AccessibleStateType::SENSITIVE
;
579 _rStateSet
|= AccessibleStateType::ACTIVE
;
583 tools::Rectangle
TableControl::GetFieldCharacterBounds(sal_Int32
,sal_Int32
,sal_Int32 nIndex
)
585 return GetCharacterBounds(nIndex
);
589 sal_Int32
TableControl::GetFieldIndexAtPoint(sal_Int32
,sal_Int32
,const Point
& _rPoint
)
591 return GetIndexForPoint(_rPoint
);
595 tools::Rectangle
TableControl::calcHeaderRect(bool _bIsColumnBar
)
597 return m_pImpl
->calcHeaderRect( !_bIsColumnBar
);
601 tools::Rectangle
TableControl::calcHeaderCellRect( bool _bIsColumnBar
, sal_Int32 nPos
)
603 return m_pImpl
->calcHeaderCellRect( _bIsColumnBar
, nPos
);
607 tools::Rectangle
TableControl::calcTableRect()
609 return m_pImpl
->calcTableRect();
613 tools::Rectangle
TableControl::calcCellRect( sal_Int32 _nRowPos
, sal_Int32 _nColPos
)
615 return m_pImpl
->calcCellRect( _nRowPos
, _nColPos
);
619 IMPL_LINK_NOARG(TableControl
, ImplSelectHdl
, LinkParamNone
*, void)
625 void TableControl::Select()
627 ImplCallEventListenersAndHandler( VclEventId::TableRowSelect
, nullptr );
629 if ( m_pImpl
->isAccessibleAlive() )
631 m_pImpl
->commitAccessibleEvent( AccessibleEventId::SELECTION_CHANGED
);
633 m_pImpl
->commitTableEvent( AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
, Any(), Any() );
634 // TODO: why do we notify this when the *selection* changed? Shouldn't we find a better place for this,
635 // actually, when the active descendant, i.e. the current cell, *really* changed?
639 } // namespace svt::table
642 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */