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 <toolkit/awt/vclxwindows.hxx>
23 #include <toolkit/helper/vclunohelper.hxx>
25 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
26 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
27 #include <com/sun/star/accessibility/AccessibleRole.hpp>
28 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
29 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
30 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
31 #include <comphelper/accessiblecontexthelper.hxx>
32 #include <comphelper/string.hxx>
33 #include <vcl/svapp.hxx>
34 #include <vcl/window.hxx>
35 #include <vcl/mnemonic.hxx>
36 #include <vcl/settings.hxx>
37 #include <vcl/toolkit/edit.hxx>
38 #include <vcl/toolkit/vclmedit.hxx>
39 #include <vcl/textdata.hxx>
40 #include <vcl/txtattr.hxx>
41 #include <vcl/xtextedt.hxx>
42 #include <sot/exchange.hxx>
43 #include <sot/formats.hxx>
47 using namespace ::com::sun::star
;
48 using namespace ::com::sun::star::uno
;
49 using namespace ::com::sun::star::lang
;
50 using namespace ::com::sun::star::beans
;
51 using namespace ::com::sun::star::accessibility
;
52 using namespace ::comphelper
;
58 VCLXAccessibleEdit::VCLXAccessibleEdit( VCLXWindow
* pVCLWindow
)
59 :ImplInheritanceHelper( pVCLWindow
)
61 m_nCaretPosition
= getCaretPosition();
65 void VCLXAccessibleEdit::ProcessWindowEvent( const VclWindowEvent
& rVclWindowEvent
)
67 switch ( rVclWindowEvent
.GetId() )
69 case VclEventId::EditModify
:
71 SetText( implGetText() );
74 case VclEventId::EditCaretChanged
:
76 sal_Int32 nOldCaretPosition
= m_nCaretPosition
;
77 m_nCaretPosition
= getCaretPosition();
79 VclPtr
<vcl::Window
> pWindow
= GetWindow();
80 if (pWindow
&& pWindow
->HasChildPathFocus())
82 if (m_nCaretPosition
!= nOldCaretPosition
)
84 Any aOldValue
, aNewValue
;
85 aOldValue
<<= nOldCaretPosition
;
86 aNewValue
<<= m_nCaretPosition
;
87 NotifyAccessibleEvent( AccessibleEventId::CARET_CHANGED
, aOldValue
, aNewValue
);
92 case VclEventId::EditSelectionChanged
:
94 VclPtr
<vcl::Window
> pWindow
= GetWindow();
95 if (pWindow
&& pWindow
->HasChildPathFocus())
97 NotifyAccessibleEvent( AccessibleEventId::TEXT_SELECTION_CHANGED
, Any(), Any() );
102 VCLXAccessibleTextComponent::ProcessWindowEvent( rVclWindowEvent
);
107 void VCLXAccessibleEdit::FillAccessibleStateSet( sal_Int64
& rStateSet
)
109 VCLXAccessibleTextComponent::FillAccessibleStateSet( rStateSet
);
111 VCLXWindow
* pVCLXWindow
= GetVCLXWindow();
114 rStateSet
|= AccessibleStateType::FOCUSABLE
;
116 if (GetWindow() && GetWindow()->GetType() == WindowType::MULTILINEEDIT
)
117 rStateSet
|= AccessibleStateType::MULTI_LINE
;
119 rStateSet
|= AccessibleStateType::SINGLE_LINE
;
122 rStateSet
|= AccessibleStateType::EDITABLE
;
127 // OCommonAccessibleText
130 OUString
VCLXAccessibleEdit::implGetText()
134 VclPtr
< Edit
> pEdit
= GetAs
< Edit
>();
137 aText
= removeMnemonicFromString( pEdit
->GetText() );
139 if ( implGetAccessibleRole() == AccessibleRole::PASSWORD_TEXT
)
141 sal_Unicode cEchoChar
= pEdit
->GetEchoChar();
144 OUStringBuffer
sTmp(aText
.getLength());
145 aText
= comphelper::string::padToLength(sTmp
, aText
.getLength(),
146 cEchoChar
).makeStringAndClear();
154 void VCLXAccessibleEdit::implGetSelection( sal_Int32
& nStartIndex
, sal_Int32
& nEndIndex
)
156 Selection aSelection
;
157 VclPtr
<Edit
> pEdit
= GetAs
<Edit
>();
159 aSelection
= pEdit
->GetSelection();
161 nStartIndex
= aSelection
.Min();
162 nEndIndex
= aSelection
.Max();
165 // VCLXAccessibleTextComponent
166 bool VCLXAccessibleEdit::PreferFullTextInTextChangedEvent()
168 // for a combobox subedit, the Orca screen reader announces the new/added text
169 // so always send the whole old and new text and not just
170 // the changed characters, so the whole entry text gets announced
171 Reference
<XAccessible
> xParent
= getAccessibleParent();
174 Reference
<XAccessibleContext
> xParentContext
= xParent
->getAccessibleContext();
175 if (xParentContext
.is() && xParentContext
->getAccessibleRole() == AccessibleRole::COMBO_BOX
)
185 OUString
VCLXAccessibleEdit::getImplementationName()
187 return u
"com.sun.star.comp.toolkit.AccessibleEdit"_ustr
;
191 Sequence
< OUString
> VCLXAccessibleEdit::getSupportedServiceNames()
193 return { u
"com.sun.star.awt.AccessibleEdit"_ustr
};
197 // XAccessibleContext
200 sal_Int64
VCLXAccessibleEdit::getAccessibleChildCount()
202 OExternalLockGuard
aGuard( this );
208 Reference
< XAccessible
> VCLXAccessibleEdit::getAccessibleChild( sal_Int64
)
210 throw IndexOutOfBoundsException();
214 sal_Int16
VCLXAccessibleEdit::getAccessibleRole( )
216 OExternalLockGuard
aGuard( this );
218 return implGetAccessibleRole();
221 sal_Int16
VCLXAccessibleEdit::implGetAccessibleRole( )
224 VclPtr
< Edit
> pEdit
= GetAs
< Edit
>();
225 if ( pEdit
&& ( pEdit
->IsPassword() || pEdit
->GetEchoChar() ) )
226 nRole
= AccessibleRole::PASSWORD_TEXT
;
227 else if ( pEdit
&& ( pEdit
->GetStyle() & WB_READONLY
) )
228 nRole
= AccessibleRole::STATIC
;
230 nRole
= AccessibleRole::TEXT
;
239 sal_Int32
VCLXAccessibleEdit::getAccessibleActionCount( )
241 OExternalLockGuard
aGuard( this );
243 // There is one action: activate
248 sal_Bool
VCLXAccessibleEdit::doAccessibleAction ( sal_Int32 nIndex
)
250 OExternalLockGuard
aGuard( this );
253 throw IndexOutOfBoundsException();
255 bool bDoAction
= false;
256 VclPtr
<vcl::Window
> pWindow
= GetWindow();
259 pWindow
->GrabFocus();
267 OUString
VCLXAccessibleEdit::getAccessibleActionDescription ( sal_Int32 nIndex
)
269 OExternalLockGuard
aGuard( this );
272 throw IndexOutOfBoundsException();
274 return u
"activate"_ustr
;
278 Reference
< XAccessibleKeyBinding
> VCLXAccessibleEdit::getAccessibleActionKeyBinding( sal_Int32 nIndex
)
280 OExternalLockGuard
aGuard( this );
283 throw IndexOutOfBoundsException();
285 return Reference
< XAccessibleKeyBinding
>();
292 sal_Int32
VCLXAccessibleEdit::getCaretPosition( )
294 return getSelectionEnd();
298 sal_Bool
VCLXAccessibleEdit::setCaretPosition( sal_Int32 nIndex
)
300 return setSelection( nIndex
, nIndex
);
304 sal_Unicode
VCLXAccessibleEdit::getCharacter( sal_Int32 nIndex
)
306 return VCLXAccessibleTextComponent::getCharacter( nIndex
);
310 Sequence
< PropertyValue
> VCLXAccessibleEdit::getCharacterAttributes( sal_Int32 nIndex
, const Sequence
< OUString
>& aRequestedAttributes
)
312 OExternalLockGuard
aGuard( this );
313 Sequence
< PropertyValue
> aProperties
= VCLXAccessibleTextComponent::getCharacterAttributes( nIndex
, aRequestedAttributes
);
314 auto aNonConstRange
= asNonConstRange(aProperties
);
316 // Handle multiline edit character properties
317 VclPtr
<VclMultiLineEdit
> pMulitLineEdit
= GetAsDynamic
< VclMultiLineEdit
>();
318 if ( pMulitLineEdit
)
320 ExtTextEngine
* pTextEngine
= pMulitLineEdit
->GetTextEngine();
321 TextPaM
aCursor( 0, nIndex
);
322 const TextAttribFontColor
* pFontColor
= static_cast<const TextAttribFontColor
* >(pTextEngine
->FindAttrib( aCursor
, TEXTATTR_FONTCOLOR
));
325 for (PropertyValue
& aValue
: aNonConstRange
)
327 if (aValue
.Name
== "CharColor")
329 aValue
.Value
<<= pFontColor
->GetColor().GetRGBColor();
336 // Set default character color if it is not set yet to a valid value
337 for (PropertyValue
& aValue
: aNonConstRange
)
339 if (aValue
.Name
== "CharColor")
341 if ( aValue
.Value
== sal_Int32(-1) )
343 OutputDevice
* pDev
= Application::GetDefaultDevice();
346 aValue
.Value
<<= pDev
->GetSettings().GetStyleSettings().GetFieldTextColor();
357 awt::Rectangle
VCLXAccessibleEdit::getCharacterBounds( sal_Int32 nIndex
)
359 OExternalLockGuard
aGuard( this );
361 awt::Rectangle
aBounds( 0, 0, 0, 0 );
362 sal_Int32 nLength
= implGetText().getLength();
364 if ( !implIsValidRange( nIndex
, nIndex
, nLength
) )
365 throw IndexOutOfBoundsException();
367 VclPtr
< Control
> pControl
= GetAs
< Control
>();
370 if ( nIndex
== nLength
)
372 // #108914# calculate virtual bounding rectangle
373 for ( sal_Int32 i
= 0; i
< nLength
; ++i
)
375 tools::Rectangle aRect
= pControl
->GetCharacterBounds( i
);
376 sal_Int32 nHeight
= aRect
.GetHeight();
377 if ( aBounds
.Height
< nHeight
)
379 aBounds
.Y
= aRect
.Top();
380 aBounds
.Height
= nHeight
;
382 if ( i
== nLength
- 1 )
384 aBounds
.X
= aRect
.Right() + 1;
391 aBounds
= VCLUnoHelper::ConvertToAWTRect(pControl
->GetCharacterBounds(nIndex
));
399 sal_Int32
VCLXAccessibleEdit::getCharacterCount( )
401 return VCLXAccessibleTextComponent::getCharacterCount();
405 sal_Int32
VCLXAccessibleEdit::getIndexAtPoint( const awt::Point
& aPoint
)
407 return VCLXAccessibleTextComponent::getIndexAtPoint( aPoint
);
411 OUString
VCLXAccessibleEdit::getSelectedText( )
413 return VCLXAccessibleTextComponent::getSelectedText();
417 sal_Int32
VCLXAccessibleEdit::getSelectionStart( )
419 return VCLXAccessibleTextComponent::getSelectionStart();
423 sal_Int32
VCLXAccessibleEdit::getSelectionEnd( )
425 return VCLXAccessibleTextComponent::getSelectionEnd();
429 sal_Bool
VCLXAccessibleEdit::setSelection( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
431 OExternalLockGuard
aGuard( this );
433 bool bReturn
= false;
434 OUString
sText( implGetText() );
436 if ( !implIsValidRange( nStartIndex
, nEndIndex
, sText
.getLength() ) )
437 throw IndexOutOfBoundsException();
439 VclPtr
< Edit
> pEdit
= GetAs
< Edit
>();
440 if (pEdit
&& pEdit
->IsEnabled())
442 pEdit
->SetSelection(Selection(nStartIndex
, nEndIndex
));
450 OUString
VCLXAccessibleEdit::getText( )
452 return VCLXAccessibleTextComponent::getText();
456 OUString
VCLXAccessibleEdit::getTextRange( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
458 return VCLXAccessibleTextComponent::getTextRange( nStartIndex
, nEndIndex
);
462 css::accessibility::TextSegment
VCLXAccessibleEdit::getTextAtIndex( sal_Int32 nIndex
, sal_Int16 aTextType
)
464 OExternalLockGuard
aGuard( this );
465 // Override general text component behavior: MultiLineEdit can have more text portions
466 if ( aTextType
== AccessibleTextType::ATTRIBUTE_RUN
)
468 VclPtr
<VclMultiLineEdit
> pMulitLineEdit
= GetAsDynamic
< VclMultiLineEdit
>();
469 if ( pMulitLineEdit
)
471 ExtTextEngine
* pTextEngine
= pMulitLineEdit
->GetTextEngine();
472 TextPaM
aCursor( 0, nIndex
);
474 pTextEngine
->GetTextPortionRange( aCursor
, aResult
.SegmentStart
, aResult
.SegmentEnd
);
479 return VCLXAccessibleTextComponent::getTextAtIndex( nIndex
, aTextType
);
483 css::accessibility::TextSegment
VCLXAccessibleEdit::getTextBeforeIndex( sal_Int32 nIndex
, sal_Int16 aTextType
)
485 return VCLXAccessibleTextComponent::getTextBeforeIndex( nIndex
, aTextType
);
489 css::accessibility::TextSegment
VCLXAccessibleEdit::getTextBehindIndex( sal_Int32 nIndex
, sal_Int16 aTextType
)
491 return VCLXAccessibleTextComponent::getTextBehindIndex( nIndex
, aTextType
);
495 sal_Bool
VCLXAccessibleEdit::copyText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
497 return VCLXAccessibleTextComponent::copyText( nStartIndex
, nEndIndex
);
500 sal_Bool
VCLXAccessibleEdit::scrollSubstringTo( sal_Int32
, sal_Int32
, AccessibleScrollType
)
505 // XAccessibleEditableText
508 sal_Bool
VCLXAccessibleEdit::cutText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
510 return copyText( nStartIndex
, nEndIndex
) && deleteText( nStartIndex
, nEndIndex
);
514 sal_Bool
VCLXAccessibleEdit::pasteText( sal_Int32 nIndex
)
516 OExternalLockGuard
aGuard( this );
518 bool bReturn
= false;
522 Reference
< datatransfer::clipboard::XClipboard
> xClipboard
= GetWindow()->GetClipboard();
523 if ( xClipboard
.is() )
525 Reference
< datatransfer::XTransferable
> xDataObj
;
527 SolarMutexReleaser aReleaser
;
528 xDataObj
= xClipboard
->getContents();
532 datatransfer::DataFlavor aFlavor
;
533 SotExchange::GetFormatDataFlavor( SotClipboardFormatId::STRING
, aFlavor
);
534 if ( xDataObj
->isDataFlavorSupported( aFlavor
) )
536 Any aData
= xDataObj
->getTransferData( aFlavor
);
539 bReturn
= replaceText( nIndex
, nIndex
, sText
);
549 sal_Bool
VCLXAccessibleEdit::deleteText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
)
551 return replaceText( nStartIndex
, nEndIndex
, OUString() );
555 sal_Bool
VCLXAccessibleEdit::insertText( const OUString
& sText
, sal_Int32 nIndex
)
557 return replaceText( nIndex
, nIndex
, sText
);
561 sal_Bool
VCLXAccessibleEdit::replaceText( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
, const OUString
& sReplacement
)
563 OExternalLockGuard
aGuard( this );
565 bool bReturn
= false;
566 OUString
sText( implGetText() );
568 if ( !implIsValidRange( nStartIndex
, nEndIndex
, sText
.getLength() ) )
569 throw IndexOutOfBoundsException();
571 sal_Int32 nMinIndex
= std::min( nStartIndex
, nEndIndex
);
572 sal_Int32 nMaxIndex
= std::max( nStartIndex
, nEndIndex
);
577 VclPtr
<Edit
> pEdit
= GetAs
<Edit
>();
579 pEdit
->SetText(sText
.replaceAt(nMinIndex
, nMaxIndex
- nMinIndex
, sReplacement
));
580 sal_Int32 nIndex
= nMinIndex
+ sReplacement
.getLength();
581 setSelection( nIndex
, nIndex
);
589 sal_Bool
VCLXAccessibleEdit::setAttributes( sal_Int32 nStartIndex
, sal_Int32 nEndIndex
, const Sequence
<PropertyValue
>& )
591 OExternalLockGuard
aGuard( this );
593 if ( !implIsValidRange( nStartIndex
, nEndIndex
, implGetText().getLength() ) )
594 throw IndexOutOfBoundsException();
596 return false; // attributes cannot be set for an edit
600 sal_Bool
VCLXAccessibleEdit::setText( const OUString
& sText
)
602 OExternalLockGuard
aGuard( this );
604 bool bReturn
= false;
608 VclPtr
<Edit
> pEdit
= GetAs
<Edit
>();
610 pEdit
->SetText(sText
);
611 sal_Int32 nSize
= sText
.getLength();
612 pEdit
->SetSelection(Selection(nSize
, nSize
) );
619 bool VCLXAccessibleEdit::isEditable()
621 VclPtr
<Edit
> pEdit
= GetAs
<Edit
>();
622 return pEdit
&& !pEdit
->IsReadOnly() && pEdit
->IsEnabled();
625 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */