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 QAccessible::updateAccessibility(new QAccessibleEvent(
220 QtAccessibleRegistry::getQObject(xChild
), QAccessible::ObjectDestroyed
));
224 "Ignoring invalid AccessibleEventId::CHILD event without any child set.");
227 case AccessibleEventId::HYPERTEXT_CHANGED
:
228 QAccessible::updateAccessibility(
229 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::HypertextChanged
));
231 case AccessibleEventId::SELECTION_CHANGED
:
232 QAccessible::updateAccessibility(
233 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::Selection
));
235 case AccessibleEventId::VISIBLE_DATA_CHANGED
:
236 QAccessible::updateAccessibility(
237 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::VisibleDataChanged
));
239 case AccessibleEventId::TEXT_SELECTION_CHANGED
:
241 QAccessibleTextInterface
* pTextInterface
= pQAccessibleInterface
->textInterface();
244 SAL_WARN("vcl.qt", "TEXT_SELECTION_CHANGED event received for object not "
245 "implementing text interface");
248 int nStartOffset
= 0;
250 pTextInterface
->selection(0, &nStartOffset
, &nEndOffset
);
251 QAccessible::updateAccessibility(
252 new QAccessibleTextSelectionEvent(pQAccessibleInterface
, nStartOffset
, nEndOffset
));
255 case AccessibleEventId::TEXT_ATTRIBUTE_CHANGED
:
256 QAccessible::updateAccessibility(
257 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::AttributeChanged
));
259 case AccessibleEventId::TEXT_CHANGED
:
261 TextSegment aDeletedText
;
262 TextSegment aInsertedText
;
263 if (aEvent
.OldValue
>>= aDeletedText
)
265 QAccessible::updateAccessibility(
266 new QAccessibleTextRemoveEvent(pQAccessibleInterface
, aDeletedText
.SegmentStart
,
267 toQString(aDeletedText
.SegmentText
)));
269 if (aEvent
.NewValue
>>= aInsertedText
)
271 QAccessible::updateAccessibility(new QAccessibleTextInsertEvent(
272 pQAccessibleInterface
, aInsertedText
.SegmentStart
,
273 toQString(aInsertedText
.SegmentText
)));
277 case AccessibleEventId::TABLE_CAPTION_CHANGED
:
278 QAccessible::updateAccessibility(
279 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableCaptionChanged
));
281 case AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED
:
282 QAccessible::updateAccessibility(new QAccessibleEvent(
283 pQAccessibleInterface
, QAccessible::TableColumnDescriptionChanged
));
285 case AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED
:
286 QAccessible::updateAccessibility(
287 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableColumnHeaderChanged
));
289 case AccessibleEventId::TABLE_MODEL_CHANGED
:
291 AccessibleTableModelChange aChange
;
292 aEvent
.NewValue
>>= aChange
;
294 QAccessibleTableModelChangeEvent::ModelChangeType nType
;
295 switch (aChange
.Type
)
297 case AccessibleTableModelChangeType::COLUMNS_INSERTED
:
298 nType
= QAccessibleTableModelChangeEvent::ColumnsInserted
;
300 case AccessibleTableModelChangeType::COLUMNS_REMOVED
:
301 nType
= QAccessibleTableModelChangeEvent::ColumnsRemoved
;
303 case AccessibleTableModelChangeType::ROWS_INSERTED
:
304 nType
= QAccessibleTableModelChangeEvent::RowsInserted
;
306 case AccessibleTableModelChangeType::ROWS_REMOVED
:
307 nType
= QAccessibleTableModelChangeEvent::RowsRemoved
;
309 case AccessibleTableModelChangeType::UPDATE
:
310 nType
= QAccessibleTableModelChangeEvent::DataChanged
;
313 assert(false && "Unhandled AccessibleTableModelChangeType");
316 QAccessibleTableModelChangeEvent
* pTableEvent
317 = new QAccessibleTableModelChangeEvent(pQAccessibleInterface
, nType
);
318 pTableEvent
->setFirstRow(aChange
.FirstRow
);
319 pTableEvent
->setLastRow(aChange
.LastRow
);
320 pTableEvent
->setFirstColumn(aChange
.FirstColumn
);
321 pTableEvent
->setLastColumn(aChange
.LastColumn
);
322 QAccessible::updateAccessibility(pTableEvent
);
325 case AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED
:
326 QAccessible::updateAccessibility(new QAccessibleEvent(
327 pQAccessibleInterface
, QAccessible::TableRowDescriptionChanged
));
329 case AccessibleEventId::TABLE_ROW_HEADER_CHANGED
:
330 QAccessible::updateAccessibility(
331 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableRowHeaderChanged
));
333 case AccessibleEventId::TABLE_SUMMARY_CHANGED
:
334 QAccessible::updateAccessibility(
335 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TableSummaryChanged
));
337 case AccessibleEventId::SELECTION_CHANGED_ADD
:
338 case AccessibleEventId::SELECTION_CHANGED_REMOVE
:
340 QAccessible::Event eEventType
;
341 if (aEvent
.EventId
== AccessibleEventId::SELECTION_CHANGED_ADD
)
342 eEventType
= QAccessible::SelectionAdd
;
344 eEventType
= QAccessible::SelectionRemove
;
346 uno::Reference
<accessibility::XAccessible
> xChildAcc
;
347 aEvent
.NewValue
>>= xChildAcc
;
351 "Selection add/remove event without the (un)selected accessible set");
354 Reference
<XAccessibleContext
> xContext
= xChildAcc
->getAccessibleContext();
357 SAL_WARN("vcl.qt", "No valid XAccessibleContext for (un)selected accessible.");
361 // Qt expects the event to be sent for the (un)selected child
362 QObject
* pChildObject
= QtAccessibleRegistry::getQObject(xChildAcc
);
363 assert(pChildObject
);
364 QAccessible::updateAccessibility(new QAccessibleEvent(pChildObject
, eEventType
));
367 case AccessibleEventId::SELECTION_CHANGED_WITHIN
:
368 QAccessible::updateAccessibility(
369 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::SelectionWithin
));
371 case AccessibleEventId::PAGE_CHANGED
:
372 QAccessible::updateAccessibility(
373 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::PageChanged
));
375 case AccessibleEventId::SECTION_CHANGED
:
376 QAccessible::updateAccessibility(
377 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::SectionChanged
));
379 case AccessibleEventId::COLUMN_CHANGED
:
380 QAccessible::updateAccessibility(
381 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::TextColumnChanged
));
383 case AccessibleEventId::BOUNDRECT_CHANGED
:
384 QAccessible::updateAccessibility(
385 new QAccessibleEvent(pQAccessibleInterface
, QAccessible::LocationChanged
));
387 case AccessibleEventId::STATE_CHANGED
:
388 HandleStateChangedEvent(pQAccessibleInterface
, aEvent
);
390 case AccessibleEventId::VALUE_CHANGED
:
392 QAccessibleValueInterface
* pValueInterface
= pQAccessibleInterface
->valueInterface();
395 const QVariant aValue
= pValueInterface
->currentValue();
396 QAccessible::updateAccessibility(
397 new QAccessibleValueChangeEvent(pQAccessibleInterface
, aValue
));
401 case AccessibleEventId::ROLE_CHANGED
:
402 case AccessibleEventId::INVALIDATE_ALL_CHILDREN
:
403 case AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED
:
404 case AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED
:
405 case AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED
:
406 case AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED
:
407 case AccessibleEventId::LABEL_FOR_RELATION_CHANGED
:
408 case AccessibleEventId::LABELED_BY_RELATION_CHANGED
:
409 case AccessibleEventId::MEMBER_OF_RELATION_CHANGED
:
410 case AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED
:
411 case AccessibleEventId::LISTBOX_ENTRY_EXPANDED
:
412 case AccessibleEventId::LISTBOX_ENTRY_COLLAPSED
:
413 case AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS
:
415 SAL_WARN("vcl.qt", "Unmapped AccessibleEventId: " << aEvent
.EventId
);
420 void QtAccessibleEventListener::disposing(const EventObject
& /* Source */)
422 assert(m_pAccessibleWidget
);
423 m_pAccessibleWidget
->invalidate();
426 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */