Avoid potential negative array index access to cached text.
[LibreOffice.git] / toolkit / source / controls / table / tablecontrol.cxx
blob42b314569e823c7ad63b91a85b6796202f6d3ede
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 .
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;
38 namespace svt::table
42 namespace AccessibleEventId = ::com::sun::star::accessibility::AccessibleEventId;
45 //= TableControl
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()
66 disposeOnce();
69 void TableControl::dispose()
71 CallEventListeners( VclEventId::ObjectDying );
73 m_pImpl->setModel( PTableModel() );
74 m_pImpl->disposeAccessible();
75 m_pImpl.reset();
76 Control::dispose();
80 void TableControl::GetFocus()
82 if ( !m_pImpl || !m_pImpl->getInputHandler()->GetFocus( *m_pImpl ) )
83 Control::GetFocus();
87 void TableControl::LoseFocus()
89 if ( !m_pImpl || !m_pImpl->getInputHandler()->LoseFocus( *m_pImpl ) )
90 Control::LoseFocus();
94 void TableControl::KeyInput( const KeyEvent& rKEvt )
96 if ( !m_pImpl->getInputHandler()->KeyInput( *m_pImpl, rKEvt ) )
97 Control::KeyInput( rKEvt );
98 else
100 if ( m_pImpl->isAccessibleAlive() )
102 m_pImpl->commitCellEvent( AccessibleEventId::STATE_CHANGED,
103 Any( AccessibleStateType::FOCUSED ),
104 Any()
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,
110 Any(),
111 Any()
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();
131 break;
133 case StateChangedType::ControlBackground:
134 if ( IsControlBackground() )
135 getDataWindow().SetControlBackground( GetControlBackground() );
136 else
137 getDataWindow().SetControlBackground();
138 break;
140 case StateChangedType::ControlForeground:
141 if ( IsControlForeground() )
142 getDataWindow().SetControlForeground( GetControlForeground() );
143 else
144 getDataWindow().SetControlForeground();
145 break;
147 case StateChangedType::ControlFont:
148 if ( IsControlFont() )
149 getDataWindow().SetControlFont( GetControlFont() );
150 else
151 getDataWindow().SetControlFont();
152 break;
153 default:;
158 void TableControl::Resize()
160 Control::Resize();
161 m_pImpl->onResize();
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!" );
224 if ( i_select )
226 if ( !m_pImpl->markRowAsSelected( i_rowIndex ) )
227 // nothing to do
228 return;
230 else
232 m_pImpl->markRowAsDeselected( i_rowIndex );
235 m_pImpl->invalidateRowRange( i_rowIndex, i_rowIndex );
236 Select();
240 void TableControl::SelectAllRows( bool const i_select )
242 if ( i_select )
244 if ( !m_pImpl->markAllRowsAsSelected() )
245 // nothing to do
246 return;
248 else
250 if ( !m_pImpl->markAllRowsAsDeselected() )
251 // nothing to do
252 return;
256 Invalidate();
257 // TODO: can't we do better than this, and invalidate only the rows which changed?
258 Select();
262 ITableControl& TableControl::getTableControlInterface()
264 return *m_pImpl;
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!" );
292 return nullptr;
296 OUString TableControl::GetAccessibleObjectName( vcl::table::AccessibleTableControlObjType eObjType, sal_Int32 _nRow, sal_Int32 _nCol) const
298 OUString aRetText;
299 //Window* pWin;
300 switch( eObjType )
302 case vcl::table::AccessibleTableControlObjType::GRIDCONTROL:
303 aRetText = "Grid control";
304 break;
305 case vcl::table::AccessibleTableControlObjType::TABLE:
306 aRetText = "Grid control";
307 break;
308 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR:
309 aRetText = "RowHeaderBar";
310 break;
311 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR:
312 aRetText = "ColumnHeaderBar";
313 break;
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);
326 break;
327 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL:
328 aRetText = GetRowName(_nRow);
329 break;
330 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL:
331 aRetText = GetColumnName(_nCol);
332 break;
333 default:
334 OSL_FAIL("GridControl::GetAccessibleName: invalid enum!");
336 return aRetText;
340 OUString TableControl::GetAccessibleObjectDescription( vcl::table::AccessibleTableControlObjType eObjType ) const
342 OUString aRetText;
343 switch( eObjType )
345 case vcl::table::AccessibleTableControlObjType::GRIDCONTROL:
346 aRetText = "Grid control description";
347 break;
348 case vcl::table::AccessibleTableControlObjType::TABLE:
349 aRetText = "TABLE description";
350 break;
351 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR:
352 aRetText = "ROWHEADERBAR description";
353 break;
354 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR:
355 aRetText = "COLUMNHEADERBAR description";
356 break;
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() );
368 break;
369 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL:
370 aRetText = "ROWHEADERCELL description";
371 break;
372 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL:
373 aRetText = "COLUMNHEADERCELL description";
374 break;
376 return aRetText;
380 OUString TableControl::GetRowName( sal_Int32 _nIndex) const
382 OUString sRowName;
383 GetModel()->getRowHeading( _nIndex ) >>= sRowName;
384 return 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
404 switch( eObjType )
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;
417 if ( IsActive() )
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;
431 break;
433 case vcl::table::AccessibleTableControlObjType::ROWHEADERBAR:
434 rStateSet |= AccessibleStateType::VISIBLE;
435 rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
436 break;
438 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERBAR:
439 rStateSet |= AccessibleStateType::VISIBLE;
440 rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
441 break;
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;
457 break;
459 case vcl::table::AccessibleTableControlObjType::ROWHEADERCELL:
460 rStateSet |= AccessibleStateType::VISIBLE;
461 rStateSet |= AccessibleStateType::TRANSIENT;
462 break;
464 case vcl::table::AccessibleTableControlObjType::COLUMNHEADERCELL:
465 rStateSet |= AccessibleStateType::VISIBLE;
466 break;
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()
509 return this;
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
528 sal_Int32 count = 1;
529 if ( GetModel()->hasRowHeaders() )
530 ++count;
531 if ( GetModel()->hasColumnHeaders() )
532 ++count;
533 return count;
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;
542 return nRow >= 0;
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 );
562 return _rnRow >= 0;
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)
621 Select();
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: */