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 <standard/vclxaccessibleedit.hxx>
22 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
23 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
24 #include <com/sun/star/accessibility/AccessibleRole.hpp>
25 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
26 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
27 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
28 #include <comphelper/accessiblecontexthelper.hxx>
29 #include <comphelper/string.hxx>
30 #include <vcl/svapp.hxx>
31 #include <vcl/window.hxx>
32 #include <vcl/mnemonic.hxx>
33 #include <vcl/settings.hxx>
34 #include <vcl/toolkit/vclmedit.hxx>
35 #include <vcl/textdata.hxx>
36 #include <vcl/txtattr.hxx>
37 #include <vcl/unohelp.hxx>
38 #include <vcl/xtextedt.hxx>
39 #include <sot/exchange.hxx>
40 #include <sot/formats.hxx>
44 using namespace ::com::sun::star
;
45 using namespace ::com::sun::star::uno
;
46 using namespace ::com::sun::star::lang
;
47 using namespace ::com::sun::star::beans
;
48 using namespace ::com::sun::star::accessibility
;
49 using namespace ::comphelper
;
55 VCLXAccessibleEdit::VCLXAccessibleEdit(Edit
* pEdit
)
56 :ImplInheritanceHelper(pEdit
)
58 m_nCaretPosition
= getCaretPosition();
62 void VCLXAccessibleEdit::ProcessWindowEvent( const VclWindowEvent
& rVclWindowEvent
)
64 switch ( rVclWindowEvent
.GetId() )
66 case VclEventId::EditModify
:
68 SetText( implGetText() );
71 case VclEventId::EditCaretChanged
:
73 sal_Int32 nOldCaretPosition
= m_nCaretPosition
;
74 m_nCaretPosition
= getCaretPosition();
76 VclPtr
<vcl::Window
> pWindow
= GetWindow();
77 if (pWindow
&& pWindow
->HasChildPathFocus())
79 if (m_nCaretPosition
!= nOldCaretPosition
)
81 Any aOldValue
, aNewValue
;
82 aOldValue
<<= nOldCaretPosition
;
83 aNewValue
<<= m_nCaretPosition
;
84 NotifyAccessibleEvent( AccessibleEventId::CARET_CHANGED
, aOldValue
, aNewValue
);
89 case VclEventId::EditSelectionChanged
:
91 VclPtr
<vcl::Window
> pWindow
= GetWindow();
92 if (pWindow
&& pWindow
->HasChildPathFocus())
94 NotifyAccessibleEvent( AccessibleEventId::TEXT_SELECTION_CHANGED
, Any(), Any() );
99 VCLXAccessibleTextComponent::ProcessWindowEvent( rVclWindowEvent
);
104 void VCLXAccessibleEdit::FillAccessibleStateSet( sal_Int64
& rStateSet
)
106 VCLXAccessibleTextComponent::FillAccessibleStateSet( rStateSet
);
108 vcl::Window
* pWindow
= GetWindow();
111 rStateSet
|= AccessibleStateType::FOCUSABLE
;
113 if (pWindow
->GetType() == WindowType::MULTILINEEDIT
)
114 rStateSet
|= AccessibleStateType::MULTI_LINE
;
116 rStateSet
|= AccessibleStateType::SINGLE_LINE
;
119 rStateSet
|= AccessibleStateType::EDITABLE
;
124 // OCommonAccessibleText
127 OUString
VCLXAccessibleEdit::implGetText()
131 VclPtr
< Edit
> pEdit
= GetAs
< Edit
>();
134 aText
= removeMnemonicFromString( pEdit
->GetText() );
136 if ( implGetAccessibleRole() == AccessibleRole::PASSWORD_TEXT
)
138 sal_Unicode cEchoChar
= pEdit
->GetEchoChar();
141 OUStringBuffer
sTmp(aText
.getLength());
142 aText
= comphelper::string::padToLength(sTmp
, aText
.getLength(),
143 cEchoChar
).makeStringAndClear();
151 void VCLXAccessibleEdit::implGetSelection( sal_Int32
& nStartIndex
, sal_Int32
& nEndIndex
)
153 Selection aSelection
;
154 VclPtr
<Edit
> pEdit
= GetAs
<Edit
>();
156 aSelection
= pEdit
->GetSelection();
158 nStartIndex
= aSelection
.Min();
159 nEndIndex
= aSelection
.Max();
162 // VCLXAccessibleTextComponent
163 bool VCLXAccessibleEdit::PreferFullTextInTextChangedEvent()
165 // for a combobox subedit, the Orca screen reader announces the new/added text
166 // so always send the whole old and new text and not just
167 // the changed characters, so the whole entry text gets announced
168 return isComboBoxChild();
174 OUString
VCLXAccessibleEdit::getImplementationName()
176 return u
"com.sun.star.comp.toolkit.AccessibleEdit"_ustr
;
180 Sequence
< OUString
> VCLXAccessibleEdit::getSupportedServiceNames()
182 return { u
"com.sun.star.awt.AccessibleEdit"_ustr
};
186 // XAccessibleContext
189 sal_Int64
VCLXAccessibleEdit::getAccessibleChildCount()
191 OExternalLockGuard
aGuard( this );
197 Reference
< XAccessible
> VCLXAccessibleEdit::getAccessibleChild( sal_Int64
)
199 throw IndexOutOfBoundsException();
202 OUString
VCLXAccessibleEdit::getAccessibleName()
204 OExternalLockGuard
aGuard(this);
206 // for combobox edit, return name of the parent
207 if (isComboBoxChild())
208 return getAccessibleParent()->getAccessibleContext()->getAccessibleName();
210 return VCLXAccessibleTextComponent::getAccessibleName();
213 sal_Int16
VCLXAccessibleEdit::getAccessibleRole( )
215 OExternalLockGuard
aGuard( this );
217 return implGetAccessibleRole();
220 sal_Int16
VCLXAccessibleEdit::implGetAccessibleRole( )
223 VclPtr
< Edit
> pEdit
= GetAs
< Edit
>();
224 if ( pEdit
&& ( pEdit
->IsPassword() || pEdit
->GetEchoChar() ) )
225 nRole
= AccessibleRole::PASSWORD_TEXT
;
226 else if ( pEdit
&& ( pEdit
->GetStyle() & WB_READONLY
) )
227 nRole
= AccessibleRole::STATIC
;
229 nRole
= AccessibleRole::TEXT
;
238 sal_Int32
VCLXAccessibleEdit::getAccessibleActionCount( )
240 OExternalLockGuard
aGuard( this );
242 // There is one action: activate
247 sal_Bool
VCLXAccessibleEdit::doAccessibleAction ( sal_Int32 nIndex
)
249 OExternalLockGuard
aGuard( this );
252 throw IndexOutOfBoundsException();
254 bool bDoAction
= false;
255 VclPtr
<vcl::Window
> pWindow
= GetWindow();
258 pWindow
->GrabFocus();
266 OUString
VCLXAccessibleEdit::getAccessibleActionDescription ( sal_Int32 nIndex
)
268 OExternalLockGuard
aGuard( this );
271 throw IndexOutOfBoundsException();
273 return u
"activate"_ustr
;
277 Reference
< XAccessibleKeyBinding
> VCLXAccessibleEdit::getAccessibleActionKeyBinding( sal_Int32 nIndex
)
279 OExternalLockGuard
aGuard( this );
282 throw IndexOutOfBoundsException();
284 return Reference
< XAccessibleKeyBinding
>();
291 sal_Int32
VCLXAccessibleEdit::getCaretPosition( )
293 return getSelectionEnd();
297 sal_Bool
VCLXAccessibleEdit::setCaretPosition( sal_Int32 nIndex
)
299 return setSelection( nIndex
, nIndex
);
303 sal_Unicode
VCLXAccessibleEdit::getCharacter( sal_Int32 nIndex
)
305 return VCLXAccessibleTextComponent::getCharacter( nIndex
);
309 Sequence
< PropertyValue
> VCLXAccessibleEdit::getCharacterAttributes( sal_Int32 nIndex
, const Sequence
< OUString
>& aRequestedAttributes
)
311 OExternalLockGuard
aGuard( this );
312 Sequence
< PropertyValue
> aProperties
= VCLXAccessibleTextComponent::getCharacterAttributes( nIndex
, aRequestedAttributes
);
313 auto aNonConstRange
= asNonConstRange(aProperties
);
315 // Handle multiline edit character properties
316 VclPtr
<VclMultiLineEdit
> pMulitLineEdit
= GetAsDynamic
< VclMultiLineEdit
>();
317 if ( pMulitLineEdit
)
319 ExtTextEngine
* pTextEngine
= pMulitLineEdit
->GetTextEngine();
320 TextPaM
aCursor( 0, nIndex
);
321 const TextAttribFontColor
* pFontColor
= static_cast<const TextAttribFontColor
* >(pTextEngine
->FindAttrib( aCursor
, TEXTATTR_FONTCOLOR
));
324 for (PropertyValue
& aValue
: aNonConstRange
)
326 if (aValue
.Name
== "CharColor")
328 aValue
.Value
<<= pFontColor
->GetColor().GetRGBColor();
335 // Set default character color if it is not set yet to a valid value
336 for (PropertyValue
& aValue
: aNonConstRange
)
338 if (aValue
.Name
== "CharColor")
340 if ( aValue
.Value
== sal_Int32(-1) )
342 OutputDevice
* pDev
= Application::GetDefaultDevice();
345 aValue
.Value
<<= pDev
->GetSettings().GetStyleSettings().GetFieldTextColor();
356 awt::Rectangle
VCLXAccessibleEdit::getCharacterBounds( sal_Int32 nIndex
)
358 OExternalLockGuard
aGuard( this );
360 awt::Rectangle
aBounds( 0, 0, 0, 0 );
361 sal_Int32 nLength
= implGetText().getLength();
363 if ( !implIsValidRange( nIndex
, nIndex
, nLength
) )
364 throw IndexOutOfBoundsException();
366 VclPtr
< Control
> pControl
= GetAs
< Control
>();
369 if ( nIndex
== nLength
)
371 // #108914# calculate virtual bounding rectangle
372 for ( sal_Int32 i
= 0; i
< nLength
; ++i
)
374 tools::Rectangle aRect
= pControl
->GetCharacterBounds( i
);
375 sal_Int32 nHeight
= aRect
.GetHeight();
376 if ( aBounds
.Height
< nHeight
)
378 aBounds
.Y
= aRect
.Top();
379 aBounds
.Height
= nHeight
;
381 if ( i
== nLength
- 1 )
383 aBounds
.X
= aRect
.Right() + 1;
390 aBounds
= vcl::unohelper::ConvertToAWTRect(pControl
->GetCharacterBounds(nIndex
));
398 sal_Int32
VCLXAccessibleEdit::getCharacterCount( )
400 return VCLXAccessibleTextComponent::getCharacterCount();
404 sal_Int32
VCLXAccessibleEdit::getIndexAtPoint( const awt::Point
& aPoint
)
406 return VCLXAccessibleTextComponent::getIndexAtPoint( aPoint
);
410 OUString
VCLXAccessibleEdit::getSelectedText( )
412 return VCLXAccessibleTextComponent::getSelectedText();
416 sal_Int32
VCLXAccessibleEdit::getSelectionStart( )
418 return VCLXAccessibleTextComponent::getSelectionStart();
422 sal_Int32
VCLXAccessibleEdit::getSelectionEnd( )
424 return VCLXAccessibleTextComponent::getSelectionEnd();
428 sal_Bool
VCLXAccessibleEdit::setSelection( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
430 OExternalLockGuard
aGuard( this );
432 bool bReturn
= false;
433 OUString
sText( implGetText() );
435 if ( !implIsValidRange( nStartIndex
, nEndIndex
, sText
.getLength() ) )
436 throw IndexOutOfBoundsException();
438 VclPtr
< Edit
> pEdit
= GetAs
< Edit
>();
439 if (pEdit
&& pEdit
->IsEnabled())
441 pEdit
->SetSelection(Selection(nStartIndex
, nEndIndex
));
449 OUString
VCLXAccessibleEdit::getText( )
451 return VCLXAccessibleTextComponent::getText();
455 OUString
VCLXAccessibleEdit::getTextRange( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
457 return VCLXAccessibleTextComponent::getTextRange( nStartIndex
, nEndIndex
);
461 css::accessibility::TextSegment
VCLXAccessibleEdit::getTextAtIndex( sal_Int32 nIndex
, sal_Int16 aTextType
)
463 OExternalLockGuard
aGuard( this );
464 // Override general text component behavior: MultiLineEdit can have more text portions
465 if ( aTextType
== AccessibleTextType::ATTRIBUTE_RUN
)
467 VclPtr
<VclMultiLineEdit
> pMulitLineEdit
= GetAsDynamic
< VclMultiLineEdit
>();
468 if ( pMulitLineEdit
)
470 ExtTextEngine
* pTextEngine
= pMulitLineEdit
->GetTextEngine();
471 TextPaM
aCursor( 0, nIndex
);
473 pTextEngine
->GetTextPortionRange( aCursor
, aResult
.SegmentStart
, aResult
.SegmentEnd
);
478 return VCLXAccessibleTextComponent::getTextAtIndex( nIndex
, aTextType
);
482 css::accessibility::TextSegment
VCLXAccessibleEdit::getTextBeforeIndex( sal_Int32 nIndex
, sal_Int16 aTextType
)
484 return VCLXAccessibleTextComponent::getTextBeforeIndex( nIndex
, aTextType
);
488 css::accessibility::TextSegment
VCLXAccessibleEdit::getTextBehindIndex( sal_Int32 nIndex
, sal_Int16 aTextType
)
490 return VCLXAccessibleTextComponent::getTextBehindIndex( nIndex
, aTextType
);
494 sal_Bool
VCLXAccessibleEdit::copyText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
496 return VCLXAccessibleTextComponent::copyText( nStartIndex
, nEndIndex
);
499 sal_Bool
VCLXAccessibleEdit::scrollSubstringTo( sal_Int32
, sal_Int32
, AccessibleScrollType
)
504 // XAccessibleEditableText
507 sal_Bool
VCLXAccessibleEdit::cutText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
509 return copyText( nStartIndex
, nEndIndex
) && deleteText( nStartIndex
, nEndIndex
);
513 sal_Bool
VCLXAccessibleEdit::pasteText( sal_Int32 nIndex
)
515 OExternalLockGuard
aGuard( this );
517 bool bReturn
= false;
521 Reference
< datatransfer::clipboard::XClipboard
> xClipboard
= GetWindow()->GetClipboard();
522 if ( xClipboard
.is() )
524 Reference
< datatransfer::XTransferable
> xDataObj
;
526 SolarMutexReleaser aReleaser
;
527 xDataObj
= xClipboard
->getContents();
531 datatransfer::DataFlavor aFlavor
;
532 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
533 if ( xDataObj
->isDataFlavorSupported( aFlavor
) )
535 Any aData
= xDataObj
->getTransferData( aFlavor
);
538 bReturn
= replaceText( nIndex
, nIndex
, sText
);
548 sal_Bool
VCLXAccessibleEdit::deleteText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
550 return replaceText( nStartIndex
, nEndIndex
, OUString() );
554 sal_Bool
VCLXAccessibleEdit::insertText( const OUString
& sText
, sal_Int32 nIndex
)
556 return replaceText( nIndex
, nIndex
, sText
);
560 sal_Bool
VCLXAccessibleEdit::replaceText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
, const OUString
& sReplacement
)
562 OExternalLockGuard
aGuard( this );
564 bool bReturn
= false;
565 OUString
sText( implGetText() );
567 if ( !implIsValidRange( nStartIndex
, nEndIndex
, sText
.getLength() ) )
568 throw IndexOutOfBoundsException();
570 sal_Int32 nMinIndex
= std::min( nStartIndex
, nEndIndex
);
571 sal_Int32 nMaxIndex
= std::max( nStartIndex
, nEndIndex
);
576 VclPtr
<Edit
> pEdit
= GetAs
<Edit
>();
578 pEdit
->SetText(sText
.replaceAt(nMinIndex
, nMaxIndex
- nMinIndex
, sReplacement
));
579 sal_Int32 nIndex
= nMinIndex
+ sReplacement
.getLength();
580 setSelection( nIndex
, nIndex
);
588 sal_Bool
VCLXAccessibleEdit::setAttributes( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
, const Sequence
<PropertyValue
>& )
590 OExternalLockGuard
aGuard( this );
592 if ( !implIsValidRange( nStartIndex
, nEndIndex
, implGetText().getLength() ) )
593 throw IndexOutOfBoundsException();
595 return false; // attributes cannot be set for an edit
599 sal_Bool
VCLXAccessibleEdit::setText( const OUString
& sText
)
601 OExternalLockGuard
aGuard( this );
603 bool bReturn
= false;
607 VclPtr
<Edit
> pEdit
= GetAs
<Edit
>();
609 pEdit
->SetText(sText
);
610 sal_Int32 nSize
= sText
.getLength();
611 pEdit
->SetSelection(Selection(nSize
, nSize
) );
618 bool VCLXAccessibleEdit::isComboBoxChild()
620 Reference
<XAccessible
> xParent
= getAccessibleParent();
623 Reference
<XAccessibleContext
> xParentContext
= xParent
->getAccessibleContext();
624 if (xParentContext
.is() && xParentContext
->getAccessibleRole() == AccessibleRole::COMBO_BOX
)
630 bool VCLXAccessibleEdit::isEditable()
632 VclPtr
<Edit
> pEdit
= GetAs
<Edit
>();
633 return pEdit
&& !pEdit
->IsReadOnly() && pEdit
->IsEnabled();
636 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */