882 lines
29 KiB
C++
882 lines
29 KiB
C++
/* -*- 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 "atkwrapper.hxx"
|
|
#include "atktextattributes.hxx"
|
|
#include <algorithm>
|
|
|
|
#include <osl/diagnose.h>
|
|
#include <rtl/character.hxx>
|
|
|
|
#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
|
|
#include <com/sun/star/accessibility/AccessibleTextType.hpp>
|
|
#include <com/sun/star/accessibility/TextSegment.hpp>
|
|
#include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp>
|
|
#include <com/sun/star/accessibility/XAccessibleText.hpp>
|
|
#include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
|
|
#include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp>
|
|
#include <com/sun/star/lang/NoSupportException.hpp>
|
|
#include <com/sun/star/text/TextMarkupType.hpp>
|
|
|
|
using namespace ::com::sun::star;
|
|
|
|
static sal_Int16
|
|
text_type_from_boundary(AtkTextBoundary boundary_type)
|
|
{
|
|
switch(boundary_type)
|
|
{
|
|
case ATK_TEXT_BOUNDARY_CHAR:
|
|
return accessibility::AccessibleTextType::CHARACTER;
|
|
case ATK_TEXT_BOUNDARY_WORD_START:
|
|
case ATK_TEXT_BOUNDARY_WORD_END:
|
|
return accessibility::AccessibleTextType::WORD;
|
|
case ATK_TEXT_BOUNDARY_SENTENCE_START:
|
|
case ATK_TEXT_BOUNDARY_SENTENCE_END:
|
|
return accessibility::AccessibleTextType::SENTENCE;
|
|
case ATK_TEXT_BOUNDARY_LINE_START:
|
|
case ATK_TEXT_BOUNDARY_LINE_END:
|
|
return accessibility::AccessibleTextType::LINE;
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
#if ATK_CHECK_VERSION(2,32,0)
|
|
static accessibility::AccessibleScrollType
|
|
scroll_type_from_scroll_type(AtkScrollType type)
|
|
{
|
|
switch(type)
|
|
{
|
|
case ATK_SCROLL_TOP_LEFT:
|
|
return accessibility::AccessibleScrollType_SCROLL_TOP_LEFT;
|
|
case ATK_SCROLL_BOTTOM_RIGHT:
|
|
return accessibility::AccessibleScrollType_SCROLL_BOTTOM_RIGHT;
|
|
case ATK_SCROLL_TOP_EDGE:
|
|
return accessibility::AccessibleScrollType_SCROLL_TOP_EDGE;
|
|
case ATK_SCROLL_BOTTOM_EDGE:
|
|
return accessibility::AccessibleScrollType_SCROLL_BOTTOM_EDGE;
|
|
case ATK_SCROLL_LEFT_EDGE:
|
|
return accessibility::AccessibleScrollType_SCROLL_LEFT_EDGE;
|
|
case ATK_SCROLL_RIGHT_EDGE:
|
|
return accessibility::AccessibleScrollType_SCROLL_RIGHT_EDGE;
|
|
case ATK_SCROLL_ANYWHERE:
|
|
return accessibility::AccessibleScrollType_SCROLL_ANYWHERE;
|
|
default:
|
|
throw lang::NoSupportException();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/*****************************************************************************/
|
|
|
|
static gchar *
|
|
adjust_boundaries( css::uno::Reference<css::accessibility::XAccessibleText> const & pText,
|
|
accessibility::TextSegment const & rTextSegment,
|
|
AtkTextBoundary boundary_type,
|
|
gint * start_offset, gint * end_offset )
|
|
{
|
|
accessibility::TextSegment aTextSegment;
|
|
OUString aString;
|
|
gint start = 0, end = 0;
|
|
|
|
if( !rTextSegment.SegmentText.isEmpty() )
|
|
{
|
|
switch(boundary_type)
|
|
{
|
|
case ATK_TEXT_BOUNDARY_CHAR:
|
|
if ((rTextSegment.SegmentEnd - rTextSegment.SegmentStart) == 1
|
|
&& rtl::isSurrogate(rTextSegment.SegmentText[0]))
|
|
return nullptr;
|
|
[[fallthrough]];
|
|
case ATK_TEXT_BOUNDARY_LINE_START:
|
|
case ATK_TEXT_BOUNDARY_LINE_END:
|
|
case ATK_TEXT_BOUNDARY_SENTENCE_START:
|
|
start = rTextSegment.SegmentStart;
|
|
end = rTextSegment.SegmentEnd;
|
|
aString = rTextSegment.SegmentText;
|
|
break;
|
|
|
|
// the OOo break iterator behaves as SENTENCE_START
|
|
case ATK_TEXT_BOUNDARY_SENTENCE_END:
|
|
start = rTextSegment.SegmentStart;
|
|
end = rTextSegment.SegmentEnd;
|
|
|
|
if( start > 0 )
|
|
--start;
|
|
if( end > 0 && end < pText->getCharacterCount() - 1 )
|
|
--end;
|
|
|
|
aString = pText->getTextRange(start, end);
|
|
break;
|
|
|
|
case ATK_TEXT_BOUNDARY_WORD_START:
|
|
start = rTextSegment.SegmentStart;
|
|
|
|
// Determine the start index of the next segment
|
|
aTextSegment = pText->getTextBehindIndex(rTextSegment.SegmentEnd,
|
|
text_type_from_boundary(boundary_type));
|
|
if( !aTextSegment.SegmentText.isEmpty() )
|
|
end = aTextSegment.SegmentStart;
|
|
else
|
|
end = pText->getCharacterCount();
|
|
|
|
aString = pText->getTextRange(start, end);
|
|
break;
|
|
|
|
case ATK_TEXT_BOUNDARY_WORD_END:
|
|
end = rTextSegment.SegmentEnd;
|
|
|
|
// Determine the end index of the previous segment
|
|
aTextSegment = pText->getTextBeforeIndex(rTextSegment.SegmentStart,
|
|
text_type_from_boundary(boundary_type));
|
|
if( !aTextSegment.SegmentText.isEmpty() )
|
|
start = aTextSegment.SegmentEnd;
|
|
else
|
|
start = 0;
|
|
|
|
aString = pText->getTextRange(start, end);
|
|
break;
|
|
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
*start_offset = start;
|
|
*end_offset = end;
|
|
|
|
return OUStringToGChar(aString);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/// @throws uno::RuntimeException
|
|
static css::uno::Reference<css::accessibility::XAccessibleText>
|
|
getText( AtkText *pText )
|
|
{
|
|
AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
|
|
if( pWrap )
|
|
{
|
|
if( !pWrap->mpText.is() )
|
|
{
|
|
pWrap->mpText.set(pWrap->mpContext, css::uno::UNO_QUERY);
|
|
}
|
|
|
|
return pWrap->mpText;
|
|
}
|
|
|
|
return css::uno::Reference<css::accessibility::XAccessibleText>();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/// @throws uno::RuntimeException
|
|
static css::uno::Reference<css::accessibility::XAccessibleTextMarkup>
|
|
getTextMarkup( AtkText *pText )
|
|
{
|
|
AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
|
|
if( pWrap )
|
|
{
|
|
if( !pWrap->mpTextMarkup.is() )
|
|
{
|
|
pWrap->mpTextMarkup.set(pWrap->mpContext, css::uno::UNO_QUERY);
|
|
}
|
|
|
|
return pWrap->mpTextMarkup;
|
|
}
|
|
|
|
return css::uno::Reference<css::accessibility::XAccessibleTextMarkup>();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/// @throws uno::RuntimeException
|
|
static css::uno::Reference<css::accessibility::XAccessibleTextAttributes>
|
|
getTextAttributes( AtkText *pText )
|
|
{
|
|
AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
|
|
if( pWrap )
|
|
{
|
|
if( !pWrap->mpTextAttributes.is() )
|
|
{
|
|
pWrap->mpTextAttributes.set(pWrap->mpContext, css::uno::UNO_QUERY);
|
|
}
|
|
|
|
return pWrap->mpTextAttributes;
|
|
}
|
|
|
|
return css::uno::Reference<css::accessibility::XAccessibleTextAttributes>();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
/// @throws uno::RuntimeException
|
|
static css::uno::Reference<css::accessibility::XAccessibleMultiLineText>
|
|
getMultiLineText( AtkText *pText )
|
|
{
|
|
AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
|
|
if( pWrap )
|
|
{
|
|
if( !pWrap->mpMultiLineText.is() )
|
|
{
|
|
pWrap->mpMultiLineText.set(pWrap->mpContext, css::uno::UNO_QUERY);
|
|
}
|
|
|
|
return pWrap->mpMultiLineText;
|
|
}
|
|
|
|
return css::uno::Reference<css::accessibility::XAccessibleMultiLineText>();
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
extern "C" {
|
|
|
|
static gchar *
|
|
text_wrapper_get_text (AtkText *text,
|
|
gint start_offset,
|
|
gint end_offset)
|
|
{
|
|
gchar * ret = nullptr;
|
|
|
|
g_return_val_if_fail( (end_offset == -1) || (end_offset >= start_offset), nullptr );
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
{
|
|
OUString aText;
|
|
sal_Int32 n = pText->getCharacterCount();
|
|
|
|
if( start_offset < n )
|
|
{
|
|
if( -1 == end_offset )
|
|
aText = pText->getTextRange(start_offset, n - start_offset);
|
|
else
|
|
aText = pText->getTextRange(start_offset, end_offset);
|
|
}
|
|
|
|
ret = g_strdup( OUStringToOString(aText, RTL_TEXTENCODING_UTF8 ).getStr() );
|
|
}
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in getText()" );
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static gchar *
|
|
text_wrapper_get_text_after_offset (AtkText *text,
|
|
gint offset,
|
|
AtkTextBoundary boundary_type,
|
|
gint *start_offset,
|
|
gint *end_offset)
|
|
{
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
{
|
|
accessibility::TextSegment aTextSegment = pText->getTextBehindIndex(offset, text_type_from_boundary(boundary_type));
|
|
return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
|
|
}
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in get_text_after_offset()" );
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static gchar *
|
|
text_wrapper_get_text_at_offset (AtkText *text,
|
|
gint offset,
|
|
AtkTextBoundary boundary_type,
|
|
gint *start_offset,
|
|
gint *end_offset)
|
|
{
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
{
|
|
/* If the user presses the 'End' key, the caret will be placed behind the last character,
|
|
* which is the same index as the first character of the next line. In atk the magic offset
|
|
* '-2' is used to cover this special case.
|
|
*/
|
|
if (
|
|
-2 == offset &&
|
|
(ATK_TEXT_BOUNDARY_LINE_START == boundary_type ||
|
|
ATK_TEXT_BOUNDARY_LINE_END == boundary_type)
|
|
)
|
|
{
|
|
css::uno::Reference<
|
|
css::accessibility::XAccessibleMultiLineText> pMultiLineText
|
|
= getMultiLineText( text );
|
|
if( pMultiLineText.is() )
|
|
{
|
|
accessibility::TextSegment aTextSegment = pMultiLineText->getTextAtLineWithCaret();
|
|
return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
|
|
}
|
|
}
|
|
|
|
accessibility::TextSegment aTextSegment = pText->getTextAtIndex(offset, text_type_from_boundary(boundary_type));
|
|
return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
|
|
}
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in get_text_at_offset()" );
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static gunichar
|
|
text_wrapper_get_character_at_offset (AtkText *text,
|
|
gint offset)
|
|
{
|
|
gint start, end;
|
|
gunichar uc = 0xFFFFFFFF;
|
|
|
|
gchar * char_as_string =
|
|
text_wrapper_get_text_at_offset(text, offset, ATK_TEXT_BOUNDARY_CHAR,
|
|
&start, &end);
|
|
if( char_as_string )
|
|
{
|
|
uc = g_utf8_get_char( char_as_string );
|
|
g_free( char_as_string );
|
|
}
|
|
|
|
return uc;
|
|
}
|
|
|
|
static gchar *
|
|
text_wrapper_get_text_before_offset (AtkText *text,
|
|
gint offset,
|
|
AtkTextBoundary boundary_type,
|
|
gint *start_offset,
|
|
gint *end_offset)
|
|
{
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
{
|
|
accessibility::TextSegment aTextSegment = pText->getTextBeforeIndex(offset, text_type_from_boundary(boundary_type));
|
|
return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
|
|
}
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in text_before_offset()" );
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static gint
|
|
text_wrapper_get_caret_offset (AtkText *text)
|
|
{
|
|
gint offset = -1;
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
offset = pText->getCaretPosition();
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in getCaretPosition()" );
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
static gboolean
|
|
text_wrapper_set_caret_offset (AtkText *text,
|
|
gint offset)
|
|
{
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
return pText->setCaretPosition( offset );
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in setCaretPosition()" );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
// #i92232#
|
|
static AtkAttributeSet*
|
|
handle_text_markup_as_run_attribute( css::uno::Reference<css::accessibility::XAccessibleTextMarkup> const & pTextMarkup,
|
|
const gint nTextMarkupType,
|
|
const gint offset,
|
|
AtkAttributeSet* pSet,
|
|
gint *start_offset,
|
|
gint *end_offset )
|
|
{
|
|
const gint nTextMarkupCount( pTextMarkup->getTextMarkupCount( nTextMarkupType ) );
|
|
for ( gint nTextMarkupIndex = 0;
|
|
nTextMarkupIndex < nTextMarkupCount;
|
|
++nTextMarkupIndex )
|
|
{
|
|
accessibility::TextSegment aTextSegment =
|
|
pTextMarkup->getTextMarkup( nTextMarkupIndex, nTextMarkupType );
|
|
const gint nStartOffsetTextMarkup = aTextSegment.SegmentStart;
|
|
const gint nEndOffsetTextMarkup = aTextSegment.SegmentEnd;
|
|
if ( nStartOffsetTextMarkup <= offset )
|
|
{
|
|
if ( offset < nEndOffsetTextMarkup )
|
|
{
|
|
// text markup at <offset>
|
|
*start_offset = ::std::max( *start_offset,
|
|
nStartOffsetTextMarkup );
|
|
*end_offset = ::std::min( *end_offset,
|
|
nEndOffsetTextMarkup );
|
|
switch ( nTextMarkupType )
|
|
{
|
|
case css::text::TextMarkupType::SPELLCHECK:
|
|
{
|
|
pSet = attribute_set_prepend_misspelled( pSet );
|
|
}
|
|
break;
|
|
case css::text::TextMarkupType::TRACK_CHANGE_INSERTION:
|
|
{
|
|
pSet = attribute_set_prepend_tracked_change_insertion( pSet );
|
|
}
|
|
break;
|
|
case css::text::TextMarkupType::TRACK_CHANGE_DELETION:
|
|
{
|
|
pSet = attribute_set_prepend_tracked_change_deletion( pSet );
|
|
}
|
|
break;
|
|
case css::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE:
|
|
{
|
|
pSet = attribute_set_prepend_tracked_change_formatchange( pSet );
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
OSL_ASSERT( false );
|
|
}
|
|
}
|
|
break; // no further iteration needed.
|
|
}
|
|
else
|
|
{
|
|
*start_offset = ::std::max( *start_offset,
|
|
nEndOffsetTextMarkup );
|
|
// continue iteration.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*end_offset = ::std::min( *end_offset,
|
|
nStartOffsetTextMarkup );
|
|
break; // no further iteration.
|
|
}
|
|
}
|
|
|
|
return pSet;
|
|
}
|
|
|
|
static AtkAttributeSet *
|
|
text_wrapper_get_run_attributes( AtkText *text,
|
|
gint offset,
|
|
gint *start_offset,
|
|
gint *end_offset)
|
|
{
|
|
AtkAttributeSet *pSet = nullptr;
|
|
|
|
try {
|
|
bool bOffsetsAreValid = false;
|
|
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is())
|
|
{
|
|
uno::Sequence< beans::PropertyValue > aAttributeList;
|
|
|
|
css::uno::Reference<css::accessibility::XAccessibleTextAttributes>
|
|
pTextAttributes = getTextAttributes( text );
|
|
if(pTextAttributes.is()) // Text attributes are available for paragraphs only
|
|
{
|
|
aAttributeList = pTextAttributes->getRunAttributes( offset, uno::Sequence< OUString > () );
|
|
}
|
|
else // For other text objects use character attributes
|
|
{
|
|
aAttributeList = pText->getCharacterAttributes( offset, uno::Sequence< OUString > () );
|
|
}
|
|
|
|
pSet = attribute_set_new_from_property_values( aAttributeList, true, text );
|
|
// #i100938#
|
|
// - always provide start_offset and end_offset
|
|
{
|
|
accessibility::TextSegment aTextSegment =
|
|
pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN);
|
|
|
|
*start_offset = aTextSegment.SegmentStart;
|
|
// #i100938#
|
|
// Do _not_ increment the end_offset provide by <accessibility::TextSegment> instance
|
|
*end_offset = aTextSegment.SegmentEnd;
|
|
bOffsetsAreValid = true;
|
|
}
|
|
}
|
|
|
|
// Special handling for misspelled text
|
|
// #i92232#
|
|
// - add special handling for tracked changes and refactor the
|
|
// corresponding code for handling misspelled text.
|
|
css::uno::Reference<css::accessibility::XAccessibleTextMarkup>
|
|
pTextMarkup = getTextMarkup( text );
|
|
if( pTextMarkup.is() )
|
|
{
|
|
// Get attribute run here if it hasn't been done before
|
|
if (!bOffsetsAreValid && pText.is())
|
|
{
|
|
accessibility::TextSegment aAttributeTextSegment =
|
|
pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN);
|
|
*start_offset = aAttributeTextSegment.SegmentStart;
|
|
*end_offset = aAttributeTextSegment.SegmentEnd;
|
|
}
|
|
// handle misspelled text
|
|
pSet = handle_text_markup_as_run_attribute(
|
|
pTextMarkup,
|
|
css::text::TextMarkupType::SPELLCHECK,
|
|
offset, pSet, start_offset, end_offset );
|
|
// handle tracked changes
|
|
pSet = handle_text_markup_as_run_attribute(
|
|
pTextMarkup,
|
|
css::text::TextMarkupType::TRACK_CHANGE_INSERTION,
|
|
offset, pSet, start_offset, end_offset );
|
|
pSet = handle_text_markup_as_run_attribute(
|
|
pTextMarkup,
|
|
css::text::TextMarkupType::TRACK_CHANGE_DELETION,
|
|
offset, pSet, start_offset, end_offset );
|
|
pSet = handle_text_markup_as_run_attribute(
|
|
pTextMarkup,
|
|
css::text::TextMarkupType::TRACK_CHANGE_FORMATCHANGE,
|
|
offset, pSet, start_offset, end_offset );
|
|
}
|
|
}
|
|
catch(const uno::Exception&){
|
|
|
|
g_warning( "Exception in get_run_attributes()" );
|
|
|
|
if( pSet )
|
|
{
|
|
atk_attribute_set_free( pSet );
|
|
pSet = nullptr;
|
|
}
|
|
}
|
|
|
|
return pSet;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static AtkAttributeSet *
|
|
text_wrapper_get_default_attributes( AtkText *text )
|
|
{
|
|
AtkAttributeSet *pSet = nullptr;
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleTextAttributes>
|
|
pTextAttributes = getTextAttributes( text );
|
|
if( pTextAttributes.is() )
|
|
{
|
|
uno::Sequence< beans::PropertyValue > aAttributeList =
|
|
pTextAttributes->getDefaultAttributes( uno::Sequence< OUString > () );
|
|
|
|
pSet = attribute_set_new_from_property_values( aAttributeList, false, text );
|
|
}
|
|
}
|
|
catch(const uno::Exception&) {
|
|
|
|
g_warning( "Exception in get_default_attributes()" );
|
|
|
|
if( pSet )
|
|
{
|
|
atk_attribute_set_free( pSet );
|
|
pSet = nullptr;
|
|
}
|
|
}
|
|
|
|
return pSet;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
|
|
static void
|
|
text_wrapper_get_character_extents( AtkText *text,
|
|
gint offset,
|
|
gint *x,
|
|
gint *y,
|
|
gint *width,
|
|
gint *height,
|
|
AtkCoordType coords )
|
|
{
|
|
*x = *y = *width = *height = -1;
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
{
|
|
awt::Rectangle aRect = pText->getCharacterBounds( offset );
|
|
|
|
gint origin_x = 0;
|
|
gint origin_y = 0;
|
|
|
|
if (coords == ATK_XY_SCREEN || coords == ATK_XY_WINDOW)
|
|
{
|
|
g_return_if_fail( ATK_IS_COMPONENT( text ) );
|
|
gint nWidth = -1;
|
|
gint nHeight = -1;
|
|
atk_component_get_extents(ATK_COMPONENT(text), &origin_x, &origin_y, &nWidth, &nHeight, coords);
|
|
}
|
|
|
|
*x = aRect.X + origin_x;
|
|
*y = aRect.Y + origin_y;
|
|
*width = aRect.Width;
|
|
*height = aRect.Height;
|
|
}
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in getCharacterBounds" );
|
|
}
|
|
}
|
|
|
|
static gint
|
|
text_wrapper_get_character_count (AtkText *text)
|
|
{
|
|
gint rv = 0;
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
rv = pText->getCharacterCount();
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in getCharacterCount" );
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static gint
|
|
text_wrapper_get_offset_at_point (AtkText *text,
|
|
gint x,
|
|
gint y,
|
|
AtkCoordType coords)
|
|
{
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
{
|
|
gint origin_x = 0;
|
|
gint origin_y = 0;
|
|
|
|
if (coords == ATK_XY_SCREEN || coords == ATK_XY_WINDOW)
|
|
{
|
|
g_return_val_if_fail( ATK_IS_COMPONENT( text ), -1 );
|
|
gint nWidth = -1;
|
|
gint nHeight = -1;
|
|
atk_component_get_extents(ATK_COMPONENT(text), &origin_x, &origin_y, &nWidth, &nHeight, coords);
|
|
}
|
|
|
|
return pText->getIndexAtPoint( awt::Point(x - origin_x, y - origin_y) );
|
|
}
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in getIndexAtPoint" );
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
// FIXME: the whole series of selections API is problematic ...
|
|
|
|
static gint
|
|
text_wrapper_get_n_selections (AtkText *text)
|
|
{
|
|
gint rv = 0;
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
rv = ( pText->getSelectionEnd() > pText->getSelectionStart() ) ? 1 : 0;
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in getSelectionEnd() or getSelectionStart()" );
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
static gchar *
|
|
text_wrapper_get_selection (AtkText *text,
|
|
gint selection_num,
|
|
gint *start_offset,
|
|
gint *end_offset)
|
|
{
|
|
g_return_val_if_fail( selection_num == 0, FALSE );
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
{
|
|
*start_offset = pText->getSelectionStart();
|
|
*end_offset = pText->getSelectionEnd();
|
|
|
|
return OUStringToGChar( pText->getSelectedText() );
|
|
}
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in getSelectionEnd(), getSelectionStart() or getSelectedText()" );
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
static gboolean
|
|
text_wrapper_add_selection (AtkText *text,
|
|
gint start_offset,
|
|
gint end_offset)
|
|
{
|
|
// FIXME: can we try to be more compatible by expanding an
|
|
// existing adjacent selection ?
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
return pText->setSelection( start_offset, end_offset ); // ?
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in setSelection()" );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
text_wrapper_remove_selection (AtkText *text,
|
|
gint selection_num)
|
|
{
|
|
g_return_val_if_fail( selection_num == 0, FALSE );
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
return pText->setSelection( 0, 0 ); // ?
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in setSelection()" );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
text_wrapper_set_selection (AtkText *text,
|
|
gint selection_num,
|
|
gint start_offset,
|
|
gint end_offset)
|
|
{
|
|
g_return_val_if_fail( selection_num == 0, FALSE );
|
|
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
if( pText.is() )
|
|
return pText->setSelection( start_offset, end_offset );
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in setSelection()" );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
#if ATK_CHECK_VERSION(2,32,0)
|
|
static gboolean
|
|
text_wrapper_scroll_substring_to(AtkText *text,
|
|
gint start_offset,
|
|
gint end_offset,
|
|
AtkScrollType scroll_type)
|
|
{
|
|
try {
|
|
css::uno::Reference<css::accessibility::XAccessibleText> pText
|
|
= getText( text );
|
|
|
|
if( pText.is() )
|
|
return pText->scrollSubstringTo( start_offset, end_offset,
|
|
scroll_type_from_scroll_type( scroll_type ) );
|
|
}
|
|
catch(const uno::Exception&) {
|
|
g_warning( "Exception in scrollSubstringTo()" );
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
} // extern "C"
|
|
|
|
void
|
|
textIfaceInit (gpointer iface_, gpointer)
|
|
{
|
|
auto const iface = static_cast<AtkTextIface *>(iface_);
|
|
g_return_if_fail (iface != nullptr);
|
|
|
|
iface->get_text = text_wrapper_get_text;
|
|
iface->get_character_at_offset = text_wrapper_get_character_at_offset;
|
|
iface->get_text_before_offset = text_wrapper_get_text_before_offset;
|
|
iface->get_text_at_offset = text_wrapper_get_text_at_offset;
|
|
iface->get_text_after_offset = text_wrapper_get_text_after_offset;
|
|
iface->get_caret_offset = text_wrapper_get_caret_offset;
|
|
iface->set_caret_offset = text_wrapper_set_caret_offset;
|
|
iface->get_character_count = text_wrapper_get_character_count;
|
|
iface->get_n_selections = text_wrapper_get_n_selections;
|
|
iface->get_selection = text_wrapper_get_selection;
|
|
iface->add_selection = text_wrapper_add_selection;
|
|
iface->remove_selection = text_wrapper_remove_selection;
|
|
iface->set_selection = text_wrapper_set_selection;
|
|
iface->get_run_attributes = text_wrapper_get_run_attributes;
|
|
iface->get_default_attributes = text_wrapper_get_default_attributes;
|
|
iface->get_character_extents = text_wrapper_get_character_extents;
|
|
iface->get_offset_at_point = text_wrapper_get_offset_at_point;
|
|
#if ATK_CHECK_VERSION(2,32,0)
|
|
iface->scroll_substring_to = text_wrapper_scroll_substring_to;
|
|
#endif
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|