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 <QtAccessibleEventListener.hxx>
21 #include <QtAccessibleRegistry.hxx>
22 #include <QtTools.hxx>
24 #include <sal/log.hxx>
26 #include <com/sun/star/accessibility/AccessibleEventId.hpp>
27 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
28 #include <com/sun/star/accessibility/AccessibleTableModelChange.hpp>
29 #include <com/sun/star/accessibility/AccessibleTableModelChangeType.hpp>
30 #include <com/sun/star/accessibility/TextSegment.hpp>
32 #include <QtGui/QAccessible>
33 #include <QtGui/QAccessibleTextSelectionEvent>
36 using namespace css::accessibility
;
37 using namespace css::lang
;
38 using namespace css::uno
;
40 QtAccessibleEventListener::QtAccessibleEventListener(QtAccessibleWidget
* pAccessibleWidget
)
41 : m_pAccessibleWidget(pAccessibleWidget
)
45 void QtAccessibleEventListener::HandleStateChangedEvent(
46 QAccessibleInterface
* pQAccessibleInterface
,
47 const css::accessibility::AccessibleEventObject
& rEvent
)
49 QAccessible::State aState
;
52 rEvent
.NewValue
>>= nState
;
53 // States in 'QAccessibleStateChangeEvent' indicate what states have changed, so if e.g.
54 // an object loses focus (not just if it gains it), 'focus' state needs to be set to 'true',
55 // so retrieve the old/previous value from the event if necessary.
56 if (nState
== AccessibleStateType::INVALID
)
57 rEvent
.OldValue
>>= nState
;
61 case AccessibleStateType::ACTIVE
:
62 // ignore for now, since it somehow causes Orca to become unresponsive quite quickly
63 // TODO: analyze further and fix root cause
69 case AccessibleStateType::BUSY
:
72 case AccessibleStateType::CHECKED
:
73 aState
.checked
= true;
75 case AccessibleStateType::COLLAPSE
:
76 aState
.collapsed
= true;
78 case AccessibleStateType::DEFAULT
:
79 aState
.defaultButton
= true;
81 case AccessibleStateType::ENABLED
:
82 aState
.disabled
= true;
84 case AccessibleStateType::EDITABLE
:
85 aState
.editable
= true;
87 case AccessibleStateType::EXPANDABLE
:
88 aState
.expandable
= true;
90 case AccessibleStateType::EXPANDED
:
91 aState
.expanded
= true;
93 case AccessibleStateType::FOCUSABLE
:
94 aState
.focusable
= true;
96 case AccessibleStateType::FOCUSED
:
97 aState
.focused
= true;
99 case AccessibleStateType::INVALID
:
100 aState
.invalid
= true;
102 case AccessibleStateType::VISIBLE
:
103 aState
.invisible
= true;
105 case AccessibleStateType::MODAL
:
108 case AccessibleStateType::MOVEABLE
:
109 aState
.movable
= true;
111 case AccessibleStateType::MULTI_LINE
:
112 // comment in Qt's qaccessible.h has this:
113 // "// quint64 singleLine : 1; // we have multi line, this is redundant."
114 case AccessibleStateType::SINGLE_LINE
:
115 aState
.multiLine
= true;
117 case AccessibleStateType::MULTI_SELECTABLE
:
118 aState
.multiSelectable
= true;
120 case AccessibleStateType::OFFSCREEN
:
121 aState
.offscreen
= true;
123 case AccessibleStateType::PRESSED
:
124 aState
.pressed
= true;
126 case AccessibleStateType::RESIZABLE
:
127 aState
.sizeable
= true;
129 case AccessibleStateType::SELECTABLE
:
130 aState
.selectable
= true;
132 case AccessibleStateType::SELECTED
:
133 aState
.selected
= true;
135 case AccessibleStateType::SHOWING
:
137 // Qt does not have an equivalent for the SHOWING state,
138 // but has separate event types
139 QAccessible::Event eEventType
;
140 sal_Int64 nNewState
= 0;
141 if ((rEvent
.NewValue
>>= nNewState
) && nNewState
== AccessibleStateType::SHOWING
)
142 eEventType
= QAccessible::ObjectShow
;
144 eEventType
= QAccessible::ObjectHide
;
145 QAccessible::updateAccessibility(
146 new QAccessibleEvent(pQAccessibleInterface
, eEventType
));
149 // These don't seem to have a matching Qt equivalent
150 case AccessibleStateType::ARMED
:
151 case AccessibleStateType::DEFUNC
:
152 case AccessibleStateType::HORIZONTAL
:
153 case AccessibleStateType::ICONIFIED
:
154 case AccessibleStateType::INDETERMINATE
:
155 case AccessibleStateType::MANAGES_DESCENDANTS
:
156 case AccessibleStateType::OPAQUE
:
157 case AccessibleStateType::SENSITIVE
:
158 case AccessibleStateType::STALE
:
159 case AccessibleStateType::TRANSIENT
:
160 case AccessibleStateType::VERTICAL
:
165 QAccessible::updateAccessibility(
166 new QAccessibleStateChangeEvent(pQAccessibleInterface
, aState
));
169 void QtAccessibleEventListener::notifyEvent(const css::accessibility::AccessibleEventObject
& aEvent
)
171 QAccessibleInterface
* pQAccessibleInterface
= m_pAccessibleWidget
;
173 Reference
<XAccessible
> xChild
;
174 switch (aEvent
.EventId
)
176 case AccessibleEventId::NAME_CHANGED
:
177 QAccessible::updateAccessibility(
178 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::NameChanged
));
180 case AccessibleEventId::DESCRIPTION_CHANGED
:
181 QAccessible::updateAccessibility(
182 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::DescriptionChanged
));
184 case AccessibleEventId::ACTION_CHANGED
:
185 QAccessible::updateAccessibility(
186 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::ActionChanged
));
188 case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
:
190 // Qt has a QAccessible::ActiveDescendantChanged event type, but events of
191 // that type are currently just ignored on Qt side and not forwarded to AT-SPI.
192 // Send a state change event for the focused state of the newly
193 // active descendant instead
194 uno::Reference
<accessibility::XAccessible
> xActiveAccessible
;
195 aEvent
.NewValue
>>= xActiveAccessible
;
196 if (!xActiveAccessible
.is())
199 QObject
* pQtAcc
= QtAccessibleRegistry::getQObject(xActiveAccessible
);
200 QAccessibleInterface
* pInterface
= QAccessible::queryAccessibleInterface(pQtAcc
);
201 QAccessible::State aState
;
202 aState
.focused
= true;
203 QAccessible::updateAccessibility(new QAccessibleStateChangeEvent(pInterface
, aState
));
206 case AccessibleEventId::CARET_CHANGED
:
208 sal_Int32 nNewCursorPos
= 0;
209 aEvent
.NewValue
>>= nNewCursorPos
;
210 QAccessible::updateAccessibility(
211 new QAccessibleTextCursorEvent(pQAccessibleInterface
, nNewCursorPos
));
214 case AccessibleEventId::CHILD
:
216 QAccessible::Event event
= QAccessible::InvalidEvent
;
217 if (aEvent
.OldValue
>>= xChild
)
218 event
= QAccessible::ObjectDestroyed
;
219 if (aEvent
.NewValue
>>= xChild
)
220 event
= QAccessible::ObjectCreated
;
221 if (event
!= QAccessible::InvalidEvent
)
222 QAccessible::updateAccessibility(
223 new QAccessibleEvent(pQAccessibleInterface
, event
));
226 case AccessibleEventId::HYPERTEXT_CHANGED
:
227 QAccessible::updateAccessibility(
228 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::HypertextChanged
));
230 case AccessibleEventId::SELECTION_CHANGED
:
231 QAccessible::updateAccessibility(
232 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::Selection
));
234 case AccessibleEventId::VISIBLE_DATA_CHANGED
:
235 QAccessible::updateAccessibility(
236 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::VisibleDataChanged
));
238 case AccessibleEventId::TEXT_SELECTION_CHANGED
:
240 QAccessibleTextInterface
* pTextInterface
= pQAccessibleInterface
->textInterface();
243 SAL_WARN("vcl.qt", "TEXT_SELECTION_CHANGED event received for object not "
244 "implementing text interface");
247 int nStartOffset
= 0;
249 pTextInterface
->selection(0, &nStartOffset
, &nEndOffset
);
250 QAccessible::updateAccessibility(
251 new QAccessibleTextSelectionEvent(pQAccessibleInterface
, nStartOffset
, nEndOffset
));
254 case AccessibleEventId::TEXT_ATTRIBUTE_CHANGED
:
255 QAccessible::updateAccessibility(
256 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::AttributeChanged
));
258 case AccessibleEventId::TEXT_CHANGED
:
260 TextSegment aDeletedText
;
261 TextSegment aInsertedText
;
262 if (aEvent
.OldValue
>>= aDeletedText
)
264 QAccessible::updateAccessibility(
265 new QAccessibleTextRemoveEvent(pQAccessibleInterface
, aDeletedText
.SegmentStart
,
266 toQString(aDeletedText
.SegmentText
)));
268 if (aEvent
.NewValue
>>= aInsertedText
)
270 QAccessible::updateAccessibility(new QAccessibleTextInsertEvent(
271 pQAccessibleInterface
, aInsertedText
.SegmentStart
,
272 toQString(aInsertedText
.SegmentText
)));
276 case AccessibleEventId::TABLE_CAPTION_CHANGED
:
277 QAccessible::updateAccessibility(
278 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableCaptionChanged
));
280 case AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED
:
281 QAccessible::updateAccessibility(new QAccessibleEvent(
282 pQAccessibleInterface
, QAccessible::TableColumnDescriptionChanged
));
284 case AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED
:
285 QAccessible::updateAccessibility(
286 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableColumnHeaderChanged
));
288 case AccessibleEventId::TABLE_MODEL_CHANGED
:
290 AccessibleTableModelChange aChange
;
291 aEvent
.NewValue
>>= aChange
;
293 QAccessibleTableModelChangeEvent::ModelChangeType nType
;
294 switch (aChange
.Type
)
296 case AccessibleTableModelChangeType::COLUMNS_INSERTED
:
297 nType
= QAccessibleTableModelChangeEvent::ColumnsInserted
;
299 case AccessibleTableModelChangeType::COLUMNS_REMOVED
:
300 nType
= QAccessibleTableModelChangeEvent::ColumnsRemoved
;
302 case AccessibleTableModelChangeType::ROWS_INSERTED
:
303 nType
= QAccessibleTableModelChangeEvent::RowsInserted
;
305 case AccessibleTableModelChangeType::ROWS_REMOVED
:
306 nType
= QAccessibleTableModelChangeEvent::RowsRemoved
;
308 case AccessibleTableModelChangeType::UPDATE
:
309 nType
= QAccessibleTableModelChangeEvent::DataChanged
;
312 assert(false && "Unhandled AccessibleTableModelChangeType");
315 QAccessibleTableModelChangeEvent
* pTableEvent
316 = new QAccessibleTableModelChangeEvent(pQAccessibleInterface
, nType
);
317 pTableEvent
->setFirstRow(aChange
.FirstRow
);
318 pTableEvent
->setLastRow(aChange
.LastRow
);
319 pTableEvent
->setFirstColumn(aChange
.FirstColumn
);
320 pTableEvent
->setLastColumn(aChange
.LastColumn
);
321 QAccessible::updateAccessibility(pTableEvent
);
324 case AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED
:
325 QAccessible::updateAccessibility(new QAccessibleEvent(
326 pQAccessibleInterface
, QAccessible::TableRowDescriptionChanged
));
328 case AccessibleEventId::TABLE_ROW_HEADER_CHANGED
:
329 QAccessible::updateAccessibility(
330 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableRowHeaderChanged
));
332 case AccessibleEventId::TABLE_SUMMARY_CHANGED
:
333 QAccessible::updateAccessibility(
334 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableSummaryChanged
));
336 case AccessibleEventId::SELECTION_CHANGED_ADD
:
337 case AccessibleEventId::SELECTION_CHANGED_REMOVE
:
339 QAccessible::Event eEventType
;
340 if (aEvent
.EventId
== AccessibleEventId::SELECTION_CHANGED_ADD
)
341 eEventType
= QAccessible::SelectionAdd
;
343 eEventType
= QAccessible::SelectionRemove
;
345 uno::Reference
<accessibility::XAccessible
> xChildAcc
;
346 aEvent
.NewValue
>>= xChildAcc
;
350 "Selection add/remove event without the (un)selected accessible set");
353 Reference
<XAccessibleContext
> xContext
= xChildAcc
->getAccessibleContext();
356 SAL_WARN("vcl.qt", "No valid XAccessibleContext for (un)selected accessible.");
360 // Qt expects the event to be sent for the (un)selected child
361 QObject
* pChildObject
= QtAccessibleRegistry::getQObject(xChildAcc
);
362 assert(pChildObject
);
363 QAccessible::updateAccessibility(new QAccessibleEvent(pChildObject
, eEventType
));
366 case AccessibleEventId::SELECTION_CHANGED_WITHIN
:
367 QAccessible::updateAccessibility(
368 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::SelectionWithin
));
370 case AccessibleEventId::PAGE_CHANGED
:
371 QAccessible::updateAccessibility(
372 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::PageChanged
));
374 case AccessibleEventId::SECTION_CHANGED
:
375 QAccessible::updateAccessibility(
376 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::SectionChanged
));
378 case AccessibleEventId::COLUMN_CHANGED
:
379 QAccessible::updateAccessibility(
380 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TextColumnChanged
));
382 case AccessibleEventId::BOUNDRECT_CHANGED
:
383 QAccessible::updateAccessibility(
384 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::LocationChanged
));
386 case AccessibleEventId::STATE_CHANGED
:
387 HandleStateChangedEvent(pQAccessibleInterface
, aEvent
);
389 case AccessibleEventId::VALUE_CHANGED
:
391 QAccessibleValueInterface
* pValueInterface
= pQAccessibleInterface
->valueInterface();
394 const QVariant aValue
= pValueInterface
->currentValue();
395 QAccessible::updateAccessibility(
396 new QAccessibleValueChangeEvent(pQAccessibleInterface
, aValue
));
400 case AccessibleEventId::ROLE_CHANGED
:
401 case AccessibleEventId::INVALIDATE_ALL_CHILDREN
:
402 case AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED
:
403 case AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED
:
404 case AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED
:
405 case AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED
:
406 case AccessibleEventId::LABEL_FOR_RELATION_CHANGED
:
407 case AccessibleEventId::LABELED_BY_RELATION_CHANGED
:
408 case AccessibleEventId::MEMBER_OF_RELATION_CHANGED
:
409 case AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED
:
410 case AccessibleEventId::LISTBOX_ENTRY_EXPANDED
:
411 case AccessibleEventId::LISTBOX_ENTRY_COLLAPSED
:
412 case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS
:
414 SAL_WARN("vcl.qt", "Unmapped AccessibleEventId: " << aEvent
.EventId
);
419 void QtAccessibleEventListener::disposing(const EventObject
& /* Source */)
421 assert(m_pAccessibleWidget
);
422 m_pAccessibleWidget
->invalidate();
425 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */