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
:
64 case AccessibleStateType::BUSY
:
67 case AccessibleStateType::CHECKED
:
68 aState
.checked
= true;
70 case AccessibleStateType::COLLAPSE
:
71 aState
.collapsed
= true;
73 case AccessibleStateType::DEFAULT
:
74 aState
.defaultButton
= true;
76 case AccessibleStateType::ENABLED
:
77 aState
.disabled
= true;
79 case AccessibleStateType::EDITABLE
:
80 aState
.editable
= true;
82 case AccessibleStateType::EXPANDABLE
:
83 aState
.expandable
= true;
85 case AccessibleStateType::EXPANDED
:
86 aState
.expanded
= true;
88 case AccessibleStateType::FOCUSABLE
:
89 aState
.focusable
= true;
91 case AccessibleStateType::FOCUSED
:
92 aState
.focused
= true;
94 case AccessibleStateType::INVALID
:
95 aState
.invalid
= true;
97 case AccessibleStateType::VISIBLE
:
98 aState
.invisible
= true;
100 case AccessibleStateType::MODAL
:
103 case AccessibleStateType::MOVEABLE
:
104 aState
.movable
= true;
106 case AccessibleStateType::MULTI_LINE
:
107 // comment in Qt's qaccessible.h has this:
108 // "// quint64 singleLine : 1; // we have multi line, this is redundant."
109 case AccessibleStateType::SINGLE_LINE
:
110 aState
.multiLine
= true;
112 case AccessibleStateType::MULTI_SELECTABLE
:
113 aState
.multiSelectable
= true;
115 case AccessibleStateType::OFFSCREEN
:
116 aState
.offscreen
= true;
118 case AccessibleStateType::PRESSED
:
119 aState
.pressed
= true;
121 case AccessibleStateType::RESIZABLE
:
122 aState
.sizeable
= true;
124 case AccessibleStateType::SELECTABLE
:
125 aState
.selectable
= true;
127 case AccessibleStateType::SELECTED
:
128 aState
.selected
= true;
130 case AccessibleStateType::SHOWING
:
132 // Qt does not have an equivalent for the SHOWING state,
133 // but has separate event types
134 QAccessible::Event eEventType
;
135 sal_Int64 nNewState
= 0;
136 if ((rEvent
.NewValue
>>= nNewState
) && nNewState
== AccessibleStateType::SHOWING
)
137 eEventType
= QAccessible::ObjectShow
;
139 eEventType
= QAccessible::ObjectHide
;
140 QAccessible::updateAccessibility(
141 new QAccessibleEvent(pQAccessibleInterface
, eEventType
));
144 // These don't seem to have a matching Qt equivalent
145 case AccessibleStateType::ARMED
:
146 case AccessibleStateType::DEFUNC
:
147 case AccessibleStateType::HORIZONTAL
:
148 case AccessibleStateType::ICONIFIED
:
149 case AccessibleStateType::INDETERMINATE
:
150 case AccessibleStateType::MANAGES_DESCENDANTS
:
151 case AccessibleStateType::OPAQUE
:
152 case AccessibleStateType::SENSITIVE
:
153 case AccessibleStateType::STALE
:
154 case AccessibleStateType::TRANSIENT
:
155 case AccessibleStateType::VERTICAL
:
160 QAccessible::updateAccessibility(
161 new QAccessibleStateChangeEvent(pQAccessibleInterface
, aState
));
164 void QtAccessibleEventListener::notifyEvent(const css::accessibility::AccessibleEventObject
& aEvent
)
166 QAccessibleInterface
* pQAccessibleInterface
= m_pAccessibleWidget
;
168 switch (aEvent
.EventId
)
170 case AccessibleEventId::NAME_CHANGED
:
171 QAccessible::updateAccessibility(
172 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::NameChanged
));
174 case AccessibleEventId::DESCRIPTION_CHANGED
:
175 QAccessible::updateAccessibility(
176 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::DescriptionChanged
));
178 case AccessibleEventId::ACTION_CHANGED
:
179 QAccessible::updateAccessibility(
180 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::ActionChanged
));
182 case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED
:
184 // Qt has a QAccessible::ActiveDescendantChanged event type, but events of
185 // that type are currently just ignored on Qt side and not forwarded to AT-SPI.
186 // Send a state change event for the focused state of the newly
187 // active descendant instead
188 uno::Reference
<accessibility::XAccessible
> xActiveAccessible
;
189 aEvent
.NewValue
>>= xActiveAccessible
;
190 if (!xActiveAccessible
.is())
193 QObject
* pQtAcc
= QtAccessibleRegistry::getQObject(xActiveAccessible
);
194 QAccessibleInterface
* pInterface
= QAccessible::queryAccessibleInterface(pQtAcc
);
195 QAccessible::State aState
;
196 aState
.focused
= true;
197 QAccessible::updateAccessibility(new QAccessibleStateChangeEvent(pInterface
, aState
));
200 case AccessibleEventId::CARET_CHANGED
:
202 sal_Int32 nNewCursorPos
= 0;
203 aEvent
.NewValue
>>= nNewCursorPos
;
204 QAccessible::updateAccessibility(
205 new QAccessibleTextCursorEvent(pQAccessibleInterface
, nNewCursorPos
));
208 case AccessibleEventId::CHILD
:
210 Reference
<XAccessible
> xChild
;
211 if (aEvent
.NewValue
>>= xChild
)
213 QAccessible::updateAccessibility(new QAccessibleEvent(
214 QtAccessibleRegistry::getQObject(xChild
), QAccessible::ObjectCreated
));
217 if (aEvent
.OldValue
>>= xChild
)
219 // Forwarding as QAccessible::ObjectDestroyed event currently results in crashes on close
220 // e.g. after using the character font color popup in the Writer toolbar, which
221 // needs further investigation, so don't send the event for now.
223 QAccessible::updateAccessibility(
224 new QAccessibleEvent(QtAccessibleRegistry::getQObject(xChild), QAccessible::ObjectDestroyed));
227 "Not forwarding AccessibleEventId::CHILD event for removed child "
228 "since it may cause crashes.");
232 "Ignoring invalid AccessibleEventId::CHILD event without any child set.");
235 case AccessibleEventId::HYPERTEXT_CHANGED
:
236 QAccessible::updateAccessibility(
237 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::HypertextChanged
));
239 case AccessibleEventId::SELECTION_CHANGED
:
240 QAccessible::updateAccessibility(
241 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::Selection
));
243 case AccessibleEventId::VISIBLE_DATA_CHANGED
:
244 QAccessible::updateAccessibility(
245 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::VisibleDataChanged
));
247 case AccessibleEventId::TEXT_SELECTION_CHANGED
:
249 QAccessibleTextInterface
* pTextInterface
= pQAccessibleInterface
->textInterface();
252 SAL_WARN("vcl.qt", "TEXT_SELECTION_CHANGED event received for object not "
253 "implementing text interface");
256 int nStartOffset
= 0;
258 pTextInterface
->selection(0, &nStartOffset
, &nEndOffset
);
259 QAccessible::updateAccessibility(
260 new QAccessibleTextSelectionEvent(pQAccessibleInterface
, nStartOffset
, nEndOffset
));
263 case AccessibleEventId::TEXT_ATTRIBUTE_CHANGED
:
264 QAccessible::updateAccessibility(
265 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::AttributeChanged
));
267 case AccessibleEventId::TEXT_CHANGED
:
269 TextSegment aDeletedText
;
270 TextSegment aInsertedText
;
271 if (aEvent
.OldValue
>>= aDeletedText
)
273 QAccessible::updateAccessibility(
274 new QAccessibleTextRemoveEvent(pQAccessibleInterface
, aDeletedText
.SegmentStart
,
275 toQString(aDeletedText
.SegmentText
)));
277 if (aEvent
.NewValue
>>= aInsertedText
)
279 QAccessible::updateAccessibility(new QAccessibleTextInsertEvent(
280 pQAccessibleInterface
, aInsertedText
.SegmentStart
,
281 toQString(aInsertedText
.SegmentText
)));
285 case AccessibleEventId::TABLE_CAPTION_CHANGED
:
286 QAccessible::updateAccessibility(
287 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableCaptionChanged
));
289 case AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED
:
290 QAccessible::updateAccessibility(new QAccessibleEvent(
291 pQAccessibleInterface
, QAccessible::TableColumnDescriptionChanged
));
293 case AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED
:
294 QAccessible::updateAccessibility(
295 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableColumnHeaderChanged
));
297 case AccessibleEventId::TABLE_MODEL_CHANGED
:
299 AccessibleTableModelChange aChange
;
300 aEvent
.NewValue
>>= aChange
;
302 QAccessibleTableModelChangeEvent::ModelChangeType nType
;
303 switch (aChange
.Type
)
305 case AccessibleTableModelChangeType::COLUMNS_INSERTED
:
306 nType
= QAccessibleTableModelChangeEvent::ColumnsInserted
;
308 case AccessibleTableModelChangeType::COLUMNS_REMOVED
:
309 nType
= QAccessibleTableModelChangeEvent::ColumnsRemoved
;
311 case AccessibleTableModelChangeType::ROWS_INSERTED
:
312 nType
= QAccessibleTableModelChangeEvent::RowsInserted
;
314 case AccessibleTableModelChangeType::ROWS_REMOVED
:
315 nType
= QAccessibleTableModelChangeEvent::RowsRemoved
;
317 case AccessibleTableModelChangeType::UPDATE
:
318 nType
= QAccessibleTableModelChangeEvent::DataChanged
;
321 assert(false && "Unhandled AccessibleTableModelChangeType");
324 QAccessibleTableModelChangeEvent
* pTableEvent
325 = new QAccessibleTableModelChangeEvent(pQAccessibleInterface
, nType
);
326 pTableEvent
->setFirstRow(aChange
.FirstRow
);
327 pTableEvent
->setLastRow(aChange
.LastRow
);
328 pTableEvent
->setFirstColumn(aChange
.FirstColumn
);
329 pTableEvent
->setLastColumn(aChange
.LastColumn
);
330 QAccessible::updateAccessibility(pTableEvent
);
333 case AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED
:
334 QAccessible::updateAccessibility(new QAccessibleEvent(
335 pQAccessibleInterface
, QAccessible::TableRowDescriptionChanged
));
337 case AccessibleEventId::TABLE_ROW_HEADER_CHANGED
:
338 QAccessible::updateAccessibility(
339 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableRowHeaderChanged
));
341 case AccessibleEventId::TABLE_SUMMARY_CHANGED
:
342 QAccessible::updateAccessibility(
343 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableSummaryChanged
));
345 case AccessibleEventId::SELECTION_CHANGED_ADD
:
346 case AccessibleEventId::SELECTION_CHANGED_REMOVE
:
348 QAccessible::Event eEventType
;
349 if (aEvent
.EventId
== AccessibleEventId::SELECTION_CHANGED_ADD
)
350 eEventType
= QAccessible::SelectionAdd
;
352 eEventType
= QAccessible::SelectionRemove
;
354 uno::Reference
<accessibility::XAccessible
> xChildAcc
;
355 aEvent
.NewValue
>>= xChildAcc
;
359 "Selection add/remove event without the (un)selected accessible set");
362 Reference
<XAccessibleContext
> xContext
= xChildAcc
->getAccessibleContext();
365 SAL_WARN("vcl.qt", "No valid XAccessibleContext for (un)selected accessible.");
369 // Qt expects the event to be sent for the (un)selected child
370 QObject
* pChildObject
= QtAccessibleRegistry::getQObject(xChildAcc
);
371 assert(pChildObject
);
372 QAccessible::updateAccessibility(new QAccessibleEvent(pChildObject
, eEventType
));
375 case AccessibleEventId::SELECTION_CHANGED_WITHIN
:
376 QAccessible::updateAccessibility(
377 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::SelectionWithin
));
379 case AccessibleEventId::PAGE_CHANGED
:
380 QAccessible::updateAccessibility(
381 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::PageChanged
));
383 case AccessibleEventId::SECTION_CHANGED
:
384 QAccessible::updateAccessibility(
385 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::SectionChanged
));
387 case AccessibleEventId::COLUMN_CHANGED
:
388 QAccessible::updateAccessibility(
389 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TextColumnChanged
));
391 case AccessibleEventId::BOUNDRECT_CHANGED
:
392 QAccessible::updateAccessibility(
393 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::LocationChanged
));
395 case AccessibleEventId::STATE_CHANGED
:
396 HandleStateChangedEvent(pQAccessibleInterface
, aEvent
);
398 case AccessibleEventId::VALUE_CHANGED
:
400 QAccessibleValueInterface
* pValueInterface
= pQAccessibleInterface
->valueInterface();
403 const QVariant aValue
= pValueInterface
->currentValue();
404 QAccessible::updateAccessibility(
405 new QAccessibleValueChangeEvent(pQAccessibleInterface
, aValue
));
409 case AccessibleEventId::ROLE_CHANGED
:
410 case AccessibleEventId::INVALIDATE_ALL_CHILDREN
:
411 case AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED
:
412 case AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED
:
413 case AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED
:
414 case AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED
:
415 case AccessibleEventId::LABEL_FOR_RELATION_CHANGED
:
416 case AccessibleEventId::LABELED_BY_RELATION_CHANGED
:
417 case AccessibleEventId::MEMBER_OF_RELATION_CHANGED
:
418 case AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED
:
419 case AccessibleEventId::LISTBOX_ENTRY_EXPANDED
:
420 case AccessibleEventId::LISTBOX_ENTRY_COLLAPSED
:
421 case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS
:
423 SAL_WARN("vcl.qt", "Unmapped AccessibleEventId: " << aEvent
.EventId
);
428 void QtAccessibleEventListener::disposing(const EventObject
& /* Source */)
430 assert(m_pAccessibleWidget
);
431 m_pAccessibleWidget
->invalidate();
434 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */