/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <accessibility/accessibletablistbox.hxx>
#include <comphelper/types.hxx>
#include <vcl/toolkit/svtabbx.hxx>
#include <vcl/headbar.hxx>
#include <vcl/toolkit/svlbitm.hxx>
#include <vcl/toolkit/treelistentry.hxx>
#include <com/sun/star/accessibility/AccessibleStateType.hpp>
#include <com/sun/star/accessibility/XAccessible.hpp>
#include <rtl/ustrbuf.hxx>
#include <sal/log.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.hxx>
#include <osl/diagnose.h>
#include <strings.hrc>
#include <svdata.hxx>
#include <memory>
#include <tools/json_writer.hxx>
#include <comphelper/propertyvalue.hxx>
#include <vcl/accessibility/AccessibleBrowseBoxCheckBoxCell.hxx>
#include <vcl/accessibility/AccessibleBrowseBoxHeaderBar.hxx>
#include <vcl/accessibility/AccessibleBrowseBoxHeaderCell.hxx>
#include <vcl/accessibility/AccessibleBrowseBoxTableCell.hxx>
#include <vcl/filter/PngImageWriter.hxx>
#include <comphelper/base64.hxx>
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::accessibility;
namespace {
OString lcl_extractPngString(
const BitmapEx& rImage)
{
SvMemoryStream aOStm(65535, 65535);
// Use fastest compression "1"
css::uno::Sequence<css::beans::PropertyValue> aFilterData{
comphelper::makePropertyValue(u
"Compression" _ustr, sal_Int32(1)),
};
vcl::PngImageWriter aPNGWriter(aOStm);
aPNGWriter.setParameters(aFilterData);
if (aPNGWriter.write(rImage))
{
css::uno::Sequence<sal_Int8> aSeq(
static_cast <sal_Int8
const *>(aOStm.GetData()),
aOStm.Tell());
OStringBuffer aBuffer(
"data:image/png;base64," );
::comphelper::Base64::encode(aBuffer, aSeq);
return aBuffer.makeStringAndClear();
}
return "" _ostr;
}
}
static void lcl_DumpEntryAndSiblings(tools::JsonWriter& rJsonWriter,
SvTreeListEntry* pEntry,
SvTabListBox* pTabListBox,
bool bCheckButtons)
{
while (pEntry)
{
auto aNode = rJsonWriter.startStruct();
// DEPRECATED
// simple listbox value
const SvLBoxItem* pIt = pEntry->GetFirstItem(SvLBoxItemType::String);
if (pIt)
rJsonWriter.put(
"text" ,
static_cast <
const SvLBoxString*>(pIt)->GetText());
// column based data
{
auto aColumns = rJsonWriter.startArray(
"columns" );
for (size_t i = 0; i < pEntry->ItemCount(); i++)
{
SvLBoxItem& rItem = pEntry->GetItem(i);
if (rItem.GetType() == SvLBoxItemType::String)
{
const SvLBoxString* pStringItem =
dynamic_cast <
const SvLBoxString*>(&rItem);
if (pStringItem)
{
auto aColumn = rJsonWriter.startStruct();
rJsonWriter.put(
"text" , pStringItem->GetText());
}
}
else if (rItem.GetType() == SvLBoxItemType::ContextBmp)
{
const SvLBoxContextBmp* pBmpItem =
dynamic_cast <
const SvLBoxContextBmp*>(&rItem);
if (pBmpItem)
{
const OUString aCollapsed = pBmpItem->GetBitmap1().GetStock();
const OUString aExpanded = pBmpItem->GetBitmap2().GetStock();
// send identifier only, we will use svg icon
if (!o3tl::trim(aCollapsed).empty() || !o3tl::trim(aExpanded).empty())
{
auto aColumn = rJsonWriter.startStruct();
if (!o3tl::trim(aCollapsed).empty())
rJsonWriter.put(
"collapsed" , aCollapsed);
if (!o3tl::trim(aExpanded).empty())
rJsonWriter.put(
"expanded" , aExpanded);
}
// custom bitmap - send png
else
{
BitmapEx aCollapsedImage = pBmpItem->GetBitmap1().GetBitmapEx();
BitmapEx aExpandedImage = pBmpItem->GetBitmap2().GetBitmapEx();
bool bHasCollapsed = !aCollapsedImage.IsEmpty() && !aCollapsedImage.GetSizePixel().IsEm
pty();
bool bHasExpanded = !aExpandedImage.IsEmpty() && !aExpandedImage.GetSizePixel().IsEmpty();
if (bHasCollapsed || bHasExpanded)
{
auto aColumn = rJsonWriter.startStruct();
if (bHasCollapsed)
rJsonWriter.put("collapsedimage" , lcl_extractPngString(aCollapsedImage));
if (bHasExpanded)
rJsonWriter.put("collapsedimage" , lcl_extractPngString(aExpandedImage));
}
}
}
}
}
}
// SalInstanceTreeView does not use the flag CHILDREN_ON_DEMAND
// and it creates a dummy child
const SvTreeListEntries& rChildren = pEntry->GetChildEntries();
if (rChildren.size() == 1)
{
auto & rChild = rChildren[0];
if (const SvLBoxItem* pChild = rChild->GetFirstItem(SvLBoxItemType::String))
{
if (static_cast <const SvLBoxString*>(pChild)->GetText() == "" )
rJsonWriter.put("ondemand" , true );
}
}
if (rChildren.size() > 0 && !pTabListBox->IsExpanded(pEntry))
{
rJsonWriter.put("collapsed" , true );
}
if (bCheckButtons)
{
SvButtonState eCheckState = pTabListBox->GetCheckButtonState(pEntry);
if (eCheckState == SvButtonState::Unchecked)
rJsonWriter.put("state" , false );
else if (eCheckState == SvButtonState::Checked)
rJsonWriter.put("state" , true );
rJsonWriter.put("enabled" , pTabListBox->GetCheckButtonEnabled(pEntry));
}
if (pTabListBox->IsSelected(pEntry))
rJsonWriter.put("selected" , true );
rJsonWriter.put("row" , pTabListBox->GetModel()->GetAbsPos(pEntry));
SvTreeListEntry* pChild = pTabListBox->FirstChild(pEntry);
if (pChild)
{
auto childrenNode = rJsonWriter.startArray("children" );
lcl_DumpEntryAndSiblings(rJsonWriter, pChild, pTabListBox, bCheckButtons);
}
pEntry = pEntry->NextSibling();
}
}
void SvTabListBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
SvTreeListBox::DumpAsPropertyTree(rJsonWriter);
rJsonWriter.put("singleclickactivate" , GetActivateOnSingleClick());
bool bCheckButtons = static_cast <int >(nTreeFlags & SvTreeFlags::CHKBTN);
bool isRadioButton = false ;
if (pCheckButtonData)
{
isRadioButton = pCheckButtonData -> IsRadio();
}
OUString checkboxtype;
if (bCheckButtons)
{
checkboxtype = "checkbox" ;
if (isRadioButton)
{
checkboxtype = "radio" ;
}
}
rJsonWriter.put("checkboxtype" , checkboxtype);
auto entriesNode = rJsonWriter.startArray("entries" );
lcl_DumpEntryAndSiblings(rJsonWriter, First(), this , bCheckButtons);
}
// SvTreeListBox callback
void SvTabListBox::SetTabs()
{
SvTreeListBox::SetTabs();
if ( mvTabList.empty() )
return ;
// The tree listbox has now inserted its tabs into the list. Now we
// fluff up the list with additional tabs and adjust the rightmost tab
// of the tree listbox.
// the 1st column (index 1 or 2 depending on button flags) is always set
// editable by SvTreeListBox::SetTabs(),
// which prevents setting a different column to editable as the first
// one with the flag is picked in SvTreeListBox::ImplEditEntry()
assert(aTabs.back()->nFlags & SvLBoxTabFlags::EDITABLE);
if (!(mvTabList[0].nFlags & SvLBoxTabFlags::EDITABLE))
{
aTabs.back()->nFlags &= ~SvLBoxTabFlags::EDITABLE;
}
// append all other tabs to the list
for ( sal_uInt16 nCurTab = 1; nCurTab < sal_uInt16(mvTabList.size()); nCurTab++ )
{
SvLBoxTab& rTab = mvTabList[nCurTab];
AddTab( rTab.GetPos(), rTab.nFlags );
}
}
void SvTabListBox::InitEntry(SvTreeListEntry* pEntry, const OUString& rStr,
const Image& rColl, const Image& rExp)
{
SvTreeListBox::InitEntry(pEntry, rStr, rColl, rExp);
sal_Int32 nIndex = 0;
// TODO: verify if nTabCount is always >0 here!
const sal_uInt16 nCount = mvTabList.size() - 1;
for ( sal_uInt16 nToken = 0; nToken < nCount; nToken++ )
{
const std::u16string_view aToken = GetToken(aCurEntry, nIndex);
pEntry->AddItem(std::make_unique<SvLBoxString>(OUString(aToken)));
}
}
SvTabListBox::SvTabListBox( vcl::Window* pParent, WinBits nBits )
: SvTreeListBox( pParent, nBits )
{
SetHighlightRange(); // select full width
}
SvTabListBox::~SvTabListBox()
{
disposeOnce();
}
void SvTabListBox::dispose()
{
mvTabList.clear();
SvTreeListBox::dispose();
}
void SvTabListBox::SetTabs(const std::vector<tools::Long >& rTabPositions, MapUnit eMapUnit)
{
assert(!rTabPositions.empty());
mvTabList.resize(rTabPositions.size());
MapMode aMMSource( eMapUnit );
MapMode aMMDest( MapUnit::MapPixel );
for ( sal_uInt16 nIdx = 0; nIdx < sal_uInt16(mvTabList.size()); nIdx++)
{
Size aSize(rTabPositions.at(nIdx), 0);
aSize = LogicToLogic( aSize, &aMMSource, &aMMDest );
tools::Long nNewTab = aSize.Width();
mvTabList[nIdx].SetPos( nNewTab );
}
SvTreeListBox::nTreeFlags |= SvTreeFlags::RECALCTABS;
if ( IsUpdateMode() )
Invalidate();
}
SvTreeListEntry* SvTabListBox::InsertEntry( const OUString& rText, SvTreeListEntry* pParent,
bool /*bChildrenOnDemand*/,
sal_uInt32 nPos, void * pUserData )
{
return InsertEntryToColumn( rText, pParent, nPos, 0xffff, pUserData );
}
SvTreeListEntry* SvTabListBox::InsertEntryToColumn(const OUString& rStr,SvTreeListEntry* pParent,sal_uInt32 nPos,sal_uInt16 nCol,
void * pUser )
{
OUString aStr;
if ( nCol != 0xffff )
{
while ( nCol )
{
aStr += "\t" ;
nCol--;
}
}
aStr += rStr;
OUString aFirstStr( aStr );
sal_Int32 nEnd = aFirstStr.indexOf( '\t' );
if ( nEnd != -1 )
{
aFirstStr = aFirstStr.copy(0, nEnd);
aCurEntry = aStr.copy(++nEnd);
}
else
aCurEntry.clear();
return SvTreeListBox::InsertEntry( aFirstStr, pParent, false , nPos, pUser );
}
OUString SvTabListBox::GetEntryText( SvTreeListEntry* pEntry ) const
{
return GetEntryText( pEntry, 0xffff );
}
OUString SvTabListBox::GetEntryText( const SvTreeListEntry* pEntry, sal_uInt16 nCol )
{
DBG_ASSERT(pEntry,"GetEntryText:Invalid Entry" );
OUStringBuffer aResult;
if ( pEntry )
{
sal_uInt16 nCount = pEntry->ItemCount();
sal_uInt16 nCur = 0;
while ( nCur < nCount )
{
const SvLBoxItem& rStr = pEntry->GetItem( nCur );
if (rStr.GetType() == SvLBoxItemType::String)
{
if ( nCol == 0xffff )
{
if (!aResult.isEmpty())
aResult.append("\t" );
aResult.append(static_cast <const SvLBoxString&>(rStr).GetText());
}
else
{
if ( nCol == 0 )
return static_cast <const SvLBoxString&>(rStr).GetText();
nCol--;
}
}
nCur++;
}
}
return aResult.makeStringAndClear();
}
OUString SvTabListBox::GetEntryText( sal_uInt32 nPos, sal_uInt16 nCol ) const
{
SvTreeListEntry* pEntry = GetEntryOnPos( nPos );
return GetEntryText( pEntry, nCol );
}
OUString SvTabListBox::GetCellText( sal_uInt32 nPos, sal_uInt16 nCol ) const
{
SvTreeListEntry* pEntry = GetEntryOnPos( nPos );
DBG_ASSERT( pEntry, "SvTabListBox::GetCellText(): Invalid Entry" );
OUString aResult;
if (pEntry && pEntry->ItemCount() > o3tl::make_unsigned(nCol+1))
{
const SvLBoxItem& rStr = pEntry->GetItem( nCol + 1 );
if (rStr.GetType() == SvLBoxItemType::String)
aResult = static_cast <const SvLBoxString&>(rStr).GetText();
}
return aResult;
}
sal_uInt32 SvTabListBox::GetEntryPos( const SvTreeListEntry* pEntry ) const
{
sal_uInt32 nPos = 0;
SvTreeListEntry* pTmpEntry = First();
while ( pTmpEntry )
{
if ( pTmpEntry == pEntry )
return nPos;
pTmpEntry = Next( pTmpEntry );
++nPos;
}
return 0xffffffff;
}
// static
std::u16string_view SvTabListBox::GetToken( std::u16string_view sStr, sal_Int32& nIndex )
{
return o3tl::getToken(sStr, 0, '\t' , nIndex);
}
OUString SvTabListBox::GetTabEntryText( sal_uInt32 nPos, sal_uInt16 nCol ) const
{
SvTreeListEntry* pEntry = GetEntryOnPos( nPos );
DBG_ASSERT( pEntry, "GetTabEntryText(): Invalid entry " );
OUStringBuffer aResult;
if ( pEntry )
{
sal_uInt16 nCount = pEntry->ItemCount();
sal_uInt16 nCur = 0;
while ( nCur < nCount )
{
const SvLBoxItem& rBoxItem = pEntry->GetItem( nCur );
if (rBoxItem.GetType() == SvLBoxItemType::String)
{
if ( nCol == 0xffff )
{
if (!aResult.isEmpty())
aResult.append("\t" );
aResult.append(static_cast <const SvLBoxString&>(rBoxItem).GetText());
}
else
{
if ( nCol == 0 )
{
OUString sRet = static_cast <const SvLBoxString&>(rBoxItem).GetText();
if ( sRet.isEmpty() )
sRet = VclResId( STR_SVT_ACC_EMPTY_FIELD );
return sRet;
}
--nCol;
}
}
++nCur;
}
}
return aResult.makeStringAndClear();
}
SvTreeListEntry* SvTabListBox::GetEntryOnPos( sal_uInt32 _nEntryPos ) const
{
SvTreeListEntry* pEntry = nullptr;
sal_uInt32 i, nPos = 0, nCount = GetLevelChildCount( nullptr );
for ( i = 0; i < nCount; ++i )
{
SvTreeListEntry* pParent = GetEntry(i);
if ( nPos == _nEntryPos )
{
pEntry = pParent;
break ;
}
else
{
nPos++;
pEntry = GetChildOnPos( pParent, _nEntryPos, nPos );
if ( pEntry )
break ;
}
}
return pEntry;
}
SvTreeListEntry* SvTabListBox::GetChildOnPos( SvTreeListEntry* _pParent, sal_uInt32 _nEntryPos, sal_uInt32& _rPos ) const
{
sal_uInt32 i, nCount = GetLevelChildCount( _pParent );
for ( i = 0; i < nCount; ++i )
{
SvTreeListEntry* pParent = GetEntry( _pParent, i );
if ( _rPos == _nEntryPos )
return pParent;
else
{
_rPos++;
SvTreeListEntry* pEntry = GetChildOnPos( pParent, _nEntryPos, _rPos );
if ( pEntry )
return pEntry;
}
}
return nullptr;
}
void SvTabListBox::SetTabAlignCenter(sal_uInt16 nTab)
{
DBG_ASSERT(nTab<mvTabList.size(),"GetTabPos:Invalid Tab" );
if ( nTab >= mvTabList.size() )
return ;
SvLBoxTab& rTab = mvTabList[ nTab ];
SvLBoxTabFlags nFlags = rTab.nFlags;
nFlags &= ~SvLBoxTabFlags::ADJUST_FLAGS;
// see SvLBoxTab::CalcOffset for force, which only matters for centering
nFlags |= SvLBoxTabFlags::ADJUST_CENTER | SvLBoxTabFlags::FORCE;
rTab.nFlags = nFlags;
SvTreeListBox::nTreeFlags |= SvTreeFlags::RECALCTABS;
if ( IsUpdateMode() )
Invalidate();
}
void SvTabListBox::SetTabEditable(sal_uInt16 nTab, bool bEditable)
{
DBG_ASSERT(nTab<mvTabList.size(),"GetTabPos:Invalid Tab" );
if ( nTab >= mvTabList.size() )
return ;
SvLBoxTab& rTab = mvTabList[ nTab ];
if (bEditable)
rTab.nFlags |= SvLBoxTabFlags::EDITABLE;
else
rTab.nFlags &= ~SvLBoxTabFlags::EDITABLE;
}
tools::Long SvTabListBox::GetLogicTab( sal_uInt16 nTab )
{
if ( SvTreeListBox::nTreeFlags & SvTreeFlags::RECALCTABS )
SetTabs();
DBG_ASSERT(nTab<mvTabList.size(),"GetTabPos:Invalid Tab" );
return aTabs[ nTab ]->GetPos();
}
SvHeaderTabListBox::SvHeaderTabListBox(vcl::Window* pParent, WinBits nWinStyle, HeaderBar* pHeaderBar)
: SvTabListBox(pParent, nWinStyle)
, m_bFirstPaint(true )
{
assert(pHeaderBar);
m_xHeaderBar = pHeaderBar;
SetScrolledHdl(LINK(this , SvHeaderTabListBox, ScrollHdl_Impl));
m_xHeaderBar->SetCreateAccessibleHdl(LINK(this , SvHeaderTabListBox, CreateAccessibleHdl_Impl));
}
SvHeaderTabListBox::~SvHeaderTabListBox()
{
disposeOnce();
}
void SvHeaderTabListBox::dispose()
{
for (rtl::Reference<AccessibleBrowseBoxHeaderCell>& rxChild : m_aAccessibleChildren)
comphelper::disposeComponent(rxChild);
m_aAccessibleChildren.clear();
m_xAccessible.clear();
m_xHeaderBar.reset();
SvTabListBox::dispose();
}
void SvHeaderTabListBox::Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect )
{
if (m_bFirstPaint)
{
m_bFirstPaint = false ;
}
SvTabListBox::Paint(rRenderContext, rRect);
}
HeaderBar* SvHeaderTabListBox::GetHeaderBar()
{
return m_xHeaderBar;
}
bool SvHeaderTabListBox::IsItemChecked( SvTreeListEntry* pEntry, sal_uInt16 nCol )
{
SvButtonState eState = SvButtonState::Unchecked;
SvLBoxButton& rItem = static_cast <SvLBoxButton&>( pEntry->GetItem( nCol + 1 ) );
if (rItem.GetType() == SvLBoxItemType::Button)
{
SvItemStateFlags nButtonFlags = rItem.GetButtonFlags();
eState = SvLBoxButtonData::ConvertToButtonState( nButtonFlags );
}
return ( eState == SvButtonState::Checked );
}
SvTreeListEntry* SvHeaderTabListBox::InsertEntryToColumn(
const OUString& rStr, SvTreeListEntry* pParent, sal_uInt32 nPos, sal_uInt16 nCol, void * pUserData )
{
SvTreeListEntry* pEntry = SvTabListBox::InsertEntryToColumn( rStr, pParent, nPos, nCol, pUserData );
RecalculateAccessibleChildren();
return pEntry;
}
sal_uInt32 SvHeaderTabListBox::Insert(
SvTreeListEntry* pEnt, SvTreeListEntry* pPar, sal_uInt32 nPos )
{
sal_uInt32 n = SvTabListBox::Insert( pEnt, pPar, nPos );
RecalculateAccessibleChildren();
return n;
}
sal_uInt32 SvHeaderTabListBox::Insert( SvTreeListEntry* pEntry, sal_uInt32 nRootPos )
{
sal_uInt32 nPos = SvTabListBox::Insert( pEntry, nRootPos );
RecalculateAccessibleChildren();
return nPos;
}
void SvHeaderTabListBox::DumpAsPropertyTree(tools::JsonWriter& rJsonWriter)
{
SvTabListBox::DumpAsPropertyTree(rJsonWriter);
auto aHeaders = rJsonWriter.startArray("headers" );
HeaderBar* pHeaderBar = GetHeaderBar();
for (sal_uInt16 i = 0; i < pHeaderBar->GetItemCount(); i++)
{
auto aNode = rJsonWriter.startStruct();
sal_uInt16 nItemId = pHeaderBar->GetItemId(i);
rJsonWriter.put("text" , pHeaderBar->GetItemText(nItemId));
rJsonWriter.put("sortable" , !!(pHeaderBar->GetItemBits(nItemId) & HeaderBarItemBits::CLICKABLE));
}
}
IMPL_LINK_NOARG(SvHeaderTabListBox, ScrollHdl_Impl, SvTreeListBox*, void )
{
m_xHeaderBar->SetOffset(-GetXOffset());
}
IMPL_LINK_NOARG(SvHeaderTabListBox, CreateAccessibleHdl_Impl, HeaderBar*, void )
{
css::uno::Reference< XAccessible > xAccParent = m_xHeaderBar->GetAccessibleParent();
if ( xAccParent.is() )
{
Reference<XAccessible> xAccessible = new AccessibleBrowseBoxHeaderBar(
xAccParent, *this , AccessibleBrowseBoxObjType::ColumnHeaderBar);
m_xHeaderBar->SetAccessible(xAccessible);
}
}
void SvHeaderTabListBox::RecalculateAccessibleChildren()
{
if ( !m_aAccessibleChildren.empty() )
{
sal_uInt32 nCount = ( GetRowCount() + 1 ) * GetColumnCount();
if ( m_aAccessibleChildren.size() < nCount )
m_aAccessibleChildren.resize( nCount );
else
{
DBG_ASSERT( m_aAccessibleChildren.size() == nCount, "wrong children count" );
}
}
}
bool SvHeaderTabListBox::IsCellCheckBox( sal_Int32 _nRow, sal_uInt16 _nColumn, TriState& _rState ) const
{
bool bRet = false ;
SvTreeListEntry* pEntry = GetEntryOnPos( _nRow );
if ( pEntry )
{
sal_uInt16 nItemCount = pEntry->ItemCount();
if ( nItemCount > ( _nColumn + 1 ) )
{
SvLBoxItem& rItem = pEntry->GetItem( _nColumn + 1 );
if (rItem.GetType() == SvLBoxItemType::Button)
{
bRet = true ;
_rState = ( ( static_cast <SvLBoxButton&>(rItem).GetButtonFlags() & SvItemStateFlags::UNCHECKED ) == SvItemStateFlags::NONE )
? TRISTATE_TRUE : TRISTATE_FALSE;
}
}
else
{
SAL_WARN( "svtools.contnr" , "SvHeaderTabListBox::IsCellCheckBox(): column out of range" );
}
}
return bRet;
}
sal_Int32 SvHeaderTabListBox::GetRowCount() const
{
return GetEntryCount();
}
sal_uInt16 SvHeaderTabListBox::GetColumnCount() const
{
return m_xHeaderBar->GetItemCount();
}
sal_Int32 SvHeaderTabListBox::GetCurrRow() const
{
sal_Int32 nRet = -1;
SvTreeListEntry* pEntry = GetCurEntry();
if ( pEntry )
{
sal_uInt32 nCount = GetEntryCount();
for ( sal_uInt32 i = 0; i < nCount; ++i )
{
if ( pEntry == GetEntryOnPos(i) )
{
nRet = i;
break ;
}
}
}
return nRet;
}
sal_uInt16 SvHeaderTabListBox::GetCurrColumn() const
{
return 0;
}
OUString SvHeaderTabListBox::GetRowDescription( sal_Int32 _nRow ) const
{
return GetEntryText( _nRow );
}
OUString SvHeaderTabListBox::GetColumnDescription( sal_uInt16 _nColumn ) const
{
return m_xHeaderBar->GetItemText(m_xHeaderBar->GetItemId(_nColumn ));
}
bool SvHeaderTabListBox::HasRowHeader() const
{
return false ;
}
bool SvHeaderTabListBox::GoToCell( sal_Int32 /*_nRow*/, sal_uInt16 /*_nColumn*/ )
{
return false ;
}
void SvHeaderTabListBox::SetNoSelection()
{
SvTreeListBox::SelectAll(false );
}
void SvHeaderTabListBox::SelectAll()
{
SvTreeListBox::SelectAll(true );
}
void SvHeaderTabListBox::SelectRow( sal_Int32 _nRow, bool _bSelect, bool )
{
Select( GetEntryOnPos( _nRow ), _bSelect );
}
void SvHeaderTabListBox::SelectColumn( sal_uInt16, bool )
{
}
sal_Int32 SvHeaderTabListBox::GetSelectedRowCount() const
{
return GetSelectionCount();
}
sal_Int32 SvHeaderTabListBox::GetSelectedColumnCount() const
{
return 0;
}
bool SvHeaderTabListBox::IsRowSelected( sal_Int32 _nRow ) const
{
SvTreeListEntry* pEntry = GetEntryOnPos( _nRow );
return ( pEntry && IsSelected( pEntry ) );
}
bool SvHeaderTabListBox::IsColumnSelected( sal_Int32 ) const
{
return false ;
}
void SvHeaderTabListBox::GetAllSelectedRows(css::uno::Sequence<sal_Int32 >& rRowIndices) const
{
const sal_Int32 nCount = GetSelectedRowCount();
rRowIndices.realloc(nCount);
auto pRows = rRowIndices.getArray();
SvTreeListEntry* pEntry = FirstSelected();
sal_Int32 nIndex = 0;
while (nIndex < nCount && pEntry)
{
pRows[nIndex] = GetEntryPos(pEntry);
pEntry = NextSelected( pEntry );
++nIndex;
}
assert(nIndex == nCount && "Mismatch between GetSelectedRowCount() and count of selected rows when iterating." );
}
void SvHeaderTabListBox::GetAllSelectedColumns( css::uno::Sequence< sal_Int32 >& ) const
{
}
bool SvHeaderTabListBox::IsCellVisible( sal_Int32, sal_uInt16 ) const
{
return true ;
}
OUString SvHeaderTabListBox::GetAccessibleCellText( sal_Int32 _nRow, sal_uInt16 _nColumnPos ) const
{
return GetTabEntryText(_nRow, _nColumnPos);
}
tools::Rectangle SvHeaderTabListBox::calcHeaderRect(bool _bIsColumnBar)
{
tools::Rectangle aRect;
if ( _bIsColumnBar )
{
vcl::Window* pParent = m_xHeaderBar->GetAccessibleParentWindow();
assert(pParent);
aRect = m_xHeaderBar->GetWindowExtentsRelative(*pParent);
}
return aRect;
}
tools::Rectangle SvHeaderTabListBox::calcTableRect()
{
tools::Rectangle aScreenRect(GetWindowExtentsAbsolute());
return tools::Rectangle(Point(0, 0), aScreenRect.GetSize());
}
tools::Rectangle SvHeaderTabListBox::calcFieldRectPixel(sal_Int32 _nRow, sal_uInt16 _nColumn, bool _bIsHeader)
{
DBG_ASSERT( !_bIsHeader || 0 == _nRow, "invalid parameters" );
tools::Rectangle aRect;
SvTreeListEntry* pEntry = GetEntryOnPos(_nRow );
if ( pEntry )
{
aRect = _bIsHeader ? calcHeaderRect(true ) : GetBoundingRect(pEntry);
Point aTopLeft = aRect.TopLeft();
DBG_ASSERT(m_xHeaderBar->GetItemCount() > _nColumn, "invalid column" );
tools::Rectangle aItemRect = m_xHeaderBar->GetItemRect(m_xHeaderBar->GetItemId(_nColumn));
aTopLeft.setX( aItemRect.Left() );
Size aSize = aItemRect.GetSize();
aRect = tools::Rectangle( aTopLeft, aSize );
}
return aRect;
}
Reference< XAccessible > SvHeaderTabListBox::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos )
{
OSL_ENSURE(m_xAccessible.is(), "Invalid call: Accessible is null" );
rtl::Reference< AccessibleBrowseBoxCell > xChild;
TriState eState = TRISTATE_INDET;
bool bIsCheckBox = IsCellCheckBox( _nRow, _nColumnPos, eState );
if ( bIsCheckBox )
xChild = new AccessibleCheckBoxCell(m_xAccessible->getTable(), *this , _nRow, _nColumnPos,
eState, false );
else
xChild = new AccessibleBrowseBoxTableCell(m_xAccessible->getTable(), *this , _nRow,
_nColumnPos);
return xChild;
}
Reference< XAccessible > SvHeaderTabListBox::CreateAccessibleRowHeader( sal_Int32 )
{
Reference< XAccessible > xHeader;
return xHeader;
}
Reference< XAccessible > SvHeaderTabListBox::CreateAccessibleColumnHeader( sal_uInt16 _nColumn )
{
// first call? -> initial list
if ( m_aAccessibleChildren.empty() )
{
const sal_uInt16 nColumnCount = GetColumnCount();
m_aAccessibleChildren.resize( nColumnCount );
}
// get header
rtl::Reference< AccessibleBrowseBoxHeaderCell > xChild = m_aAccessibleChildren[ _nColumn ];
// already exists?
if (!xChild.is() && m_xAccessible.is())
{
// no -> create new header cell
xChild = new AccessibleBrowseBoxHeaderCell(_nColumn, m_xAccessible->getHeaderBar(), *this ,
AccessibleBrowseBoxObjType::ColumnHeaderCell);
// insert into list
m_aAccessibleChildren[ _nColumn ] = xChild;
}
return xChild;
}
sal_Int32 SvHeaderTabListBox::GetAccessibleControlCount() const
{
return -1;
}
Reference< XAccessible > SvHeaderTabListBox::CreateAccessibleControl( sal_Int32 )
{
Reference< XAccessible > xControl;
return xControl;
}
bool SvHeaderTabListBox::ConvertPointToControlIndex( sal_Int32&, const Point& )
{
return false ;
}
bool SvHeaderTabListBox::ConvertPointToCellAddress( sal_Int32&, sal_uInt16&, const Point& )
{
return false ;
}
bool SvHeaderTabListBox::ConvertPointToRowHeader( sal_Int32&, const Point& )
{
return false ;
}
bool SvHeaderTabListBox::ConvertPointToColumnHeader( sal_uInt16&, const Point& )
{
return false ;
}
OUString SvHeaderTabListBox::GetAccessibleObjectName( AccessibleBrowseBoxObjType _eType, sal_Int32 _nPos ) const
{
OUString aRetText;
switch ( _eType )
{
case AccessibleBrowseBoxObjType::BrowseBox:
case AccessibleBrowseBoxObjType::Table:
case AccessibleBrowseBoxObjType::ColumnHeaderBar:
// should be empty now (see #i63983)
aRetText.clear();
break ;
case AccessibleBrowseBoxObjType::TableCell:
{
// here we need a valid pos, we can not handle -1
if ( _nPos >= 0 )
{
sal_uInt16 nColumnCount = GetColumnCount();
if (nColumnCount > 0)
{
sal_Int32 nRow = _nPos / nColumnCount;
sal_uInt16 nColumn = static_cast < sal_uInt16 >( _nPos % nColumnCount );
aRetText = GetCellText( nRow, nColumn );
}
}
break ;
}
case AccessibleBrowseBoxObjType::CheckBoxCell:
{
break ; // checkbox cells have no name
}
case AccessibleBrowseBoxObjType::ColumnHeaderCell:
{
aRetText = m_xHeaderBar->GetItemText(m_xHeaderBar->GetItemId(static_cast <sal_uInt16>(_nPos)));
break ;
}
case AccessibleBrowseBoxObjType::RowHeaderBar:
case AccessibleBrowseBoxObjType::RowHeaderCell:
aRetText = "error" ;
break ;
default :
OSL_FAIL("BrowseBox::GetAccessibleName: invalid enum!" );
}
return aRetText;
}
OUString SvHeaderTabListBox::GetAccessibleObjectDescription( AccessibleBrowseBoxObjType _eType, sal_Int32 _nPos ) const
{
OUString aRetText;
if ( _eType == AccessibleBrowseBoxObjType::TableCell && _nPos != -1 )
{
sal_uInt16 nColumnCount = GetColumnCount();
if (nColumnCount > 0)
{
sal_Int32 nRow = _nPos / nColumnCount;
sal_uInt16 nColumn = static_cast < sal_uInt16 >( _nPos % nColumnCount );
OUString aText( VclResId(STR_SVT_ACC_DESC_TABLISTBOX) );
aText = aText.replaceFirst( "%1" , OUString::number( nRow ) );
OUString sColHeader = m_xHeaderBar->GetItemText(m_xHeaderBar->GetItemId(nColumn));
if ( sColHeader.isEmpty() )
sColHeader = OUString::number( nColumn );
aText = aText.replaceFirst( "%2" , sColHeader );
aRetText = aText;
}
}
return aRetText;
}
void SvHeaderTabListBox::FillAccessibleStateSet( sal_Int64& _rStateSet, AccessibleBrowseBoxObjType _eType ) const
{
switch ( _eType )
{
case AccessibleBrowseBoxObjType::BrowseBox:
case AccessibleBrowseBoxObjType::Table:
{
_rStateSet |= AccessibleStateType::FOCUSABLE;
if ( HasFocus() )
_rStateSet |= AccessibleStateType::FOCUSED;
if ( IsActive() )
_rStateSet |= AccessibleStateType::ACTIVE;
if ( IsEnabled() )
{
_rStateSet |= AccessibleStateType::ENABLED;
_rStateSet |= AccessibleStateType::SENSITIVE;
}
if ( IsReallyVisible() )
_rStateSet |= AccessibleStateType::VISIBLE;
if ( _eType == AccessibleBrowseBoxObjType::Table )
{
_rStateSet |= AccessibleStateType::MANAGES_DESCENDANTS;
_rStateSet |= AccessibleStateType::MULTI_SELECTABLE;
}
break ;
}
case AccessibleBrowseBoxObjType::ColumnHeaderBar:
{
sal_Int32 nCurRow = GetCurrRow();
sal_uInt16 nCurColumn = GetCurrColumn();
if ( IsCellVisible( nCurRow, nCurColumn ) )
_rStateSet |= AccessibleStateType::VISIBLE;
if ( IsEnabled() )
_rStateSet |= AccessibleStateType::ENABLED;
_rStateSet |= AccessibleStateType::TRANSIENT;
break ;
}
case AccessibleBrowseBoxObjType::RowHeaderCell:
case AccessibleBrowseBoxObjType::ColumnHeaderCell:
{
_rStateSet |= AccessibleStateType::VISIBLE;
_rStateSet |= AccessibleStateType::FOCUSABLE;
_rStateSet |= AccessibleStateType::TRANSIENT;
if ( IsEnabled() )
_rStateSet |= AccessibleStateType::ENABLED;
break ;
}
default :
break ;
}
}
void SvHeaderTabListBox::FillAccessibleStateSetForCell( sal_Int64& _rStateSet, sal_Int32 _nRow, sal_uInt16 _nColumn ) const
{
_rStateSet |= AccessibleStateType::FOCUSABLE;
_rStateSet |= AccessibleStateType::SELECTABLE;
_rStateSet |= AccessibleStateType::TRANSIENT;
if ( IsCellVisible( _nRow, _nColumn ) )
{
_rStateSet |= AccessibleStateType::VISIBLE;
_rStateSet |= AccessibleStateType::ENABLED;
}
if ( IsRowSelected( _nRow ) )
{
_rStateSet |= AccessibleStateType::ACTIVE;
if (HasChildPathFocus())
_rStateSet |= AccessibleStateType::FOCUSED;
_rStateSet |= AccessibleStateType::SELECTED;
}
if ( IsEnabled() )
_rStateSet |= AccessibleStateType::ENABLED;
}
void SvHeaderTabListBox::GrabTableFocus()
{
GrabFocus();
}
bool SvHeaderTabListBox::GetGlyphBoundRects( const Point& rOrigin, const OUString& rStr, int nIndex, int nLen, std::vector< tools::Rectangle >& rVector )
{
return GetOutDev()->GetGlyphBoundRects( rOrigin, rStr, nIndex, nLen, rVector );
}
tools::Rectangle SvHeaderTabListBox::GetWindowExtentsRelative(const vcl::Window& rRelativeWindow) const
{
return Control::GetWindowExtentsRelative( rRelativeWindow );
}
void SvHeaderTabListBox::GrabFocus()
{
Control::GrabFocus();
}
Reference< XAccessible > SvHeaderTabListBox::GetAccessible()
{
return Control::GetAccessible();
}
vcl::Window* SvHeaderTabListBox::GetAccessibleParentWindow() const
{
return Control::GetAccessibleParentWindow();
}
vcl::Window* SvHeaderTabListBox::GetWindowInstance()
{
return this ;
}
Reference< XAccessible > SvHeaderTabListBox::CreateAccessible()
{
if (m_xAccessible.is())
return m_xAccessible;
Reference< XAccessible > xAccParent = GetAccessibleParent();
if ( xAccParent.is() )
{
m_xAccessible = new AccessibleTabListBox(xAccParent, *this );
return m_xAccessible;
}
return nullptr;
}
tools::Rectangle SvHeaderTabListBox::GetFieldCharacterBounds(sal_Int32,sal_Int32,sal_Int32)
{
return tools::Rectangle();
}
sal_Int32 SvHeaderTabListBox::GetFieldIndexAtPoint(sal_Int32 _nRow,sal_Int32 _nColumnPos,const Point& _rPoint)
{
OUString sText = GetAccessibleCellText( _nRow, static_cast < sal_uInt16 >( _nColumnPos ) );
std::vector< tools::Rectangle > aRects;
if ( GetGlyphBoundRects(Point(0,0), sText, 0, sText.getLength(), aRects) )
{
sal_Int32 nPos = 0;
for (auto const & rectangle : aRects)
{
if ( rectangle.Contains(_rPoint) )
return nPos;
++nPos;
}
}
return -1;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 C=81 H=98 G=89
¤ Dauer der Verarbeitung: 0.10 Sekunden
¤
*© Formatika GbR, Deutschland