bump product version to 7.6.3.2-android
[LibreOffice.git] / vcl / qt5 / QtAccessibleEventListener.cxx
blob951286a6c1c1ba609c5bf59b44f64c944a8ba7ef
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
35 using namespace css;
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;
51 sal_Int64 nState = 0;
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;
59 switch (nState)
61 case AccessibleStateType::ACTIVE:
62 aState.active = true;
63 break;
64 case AccessibleStateType::BUSY:
65 aState.busy = true;
66 break;
67 case AccessibleStateType::CHECKED:
68 aState.checked = true;
69 break;
70 case AccessibleStateType::COLLAPSE:
71 aState.collapsed = true;
72 break;
73 case AccessibleStateType::DEFAULT:
74 aState.defaultButton = true;
75 break;
76 case AccessibleStateType::ENABLED:
77 aState.disabled = true;
78 break;
79 case AccessibleStateType::EDITABLE:
80 aState.editable = true;
81 break;
82 case AccessibleStateType::EXPANDABLE:
83 aState.expandable = true;
84 break;
85 case AccessibleStateType::EXPANDED:
86 aState.expanded = true;
87 break;
88 case AccessibleStateType::FOCUSABLE:
89 aState.focusable = true;
90 break;
91 case AccessibleStateType::FOCUSED:
92 aState.focused = true;
93 break;
94 case AccessibleStateType::INVALID:
95 aState.invalid = true;
96 break;
97 case AccessibleStateType::VISIBLE:
98 aState.invisible = true;
99 break;
100 case AccessibleStateType::MODAL:
101 aState.modal = true;
102 break;
103 case AccessibleStateType::MOVEABLE:
104 aState.movable = true;
105 break;
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;
111 break;
112 case AccessibleStateType::MULTI_SELECTABLE:
113 aState.multiSelectable = true;
114 break;
115 case AccessibleStateType::OFFSCREEN:
116 aState.offscreen = true;
117 break;
118 case AccessibleStateType::PRESSED:
119 aState.pressed = true;
120 break;
121 case AccessibleStateType::RESIZABLE:
122 aState.sizeable = true;
123 break;
124 case AccessibleStateType::SELECTABLE:
125 aState.selectable = true;
126 break;
127 case AccessibleStateType::SELECTED:
128 aState.selected = true;
129 break;
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;
138 else
139 eEventType = QAccessible::ObjectHide;
140 QAccessible::updateAccessibility(
141 new QAccessibleEvent(pQAccessibleInterface, eEventType));
142 break;
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:
156 default:
157 return;
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));
173 return;
174 case AccessibleEventId::DESCRIPTION_CHANGED:
175 QAccessible::updateAccessibility(
176 new QAccessibleEvent(pQAccessibleInterface, QAccessible::DescriptionChanged));
177 return;
178 case AccessibleEventId::ACTION_CHANGED:
179 QAccessible::updateAccessibility(
180 new QAccessibleEvent(pQAccessibleInterface, QAccessible::ActionChanged));
181 return;
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())
191 return;
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));
198 return;
200 case AccessibleEventId::CARET_CHANGED:
202 sal_Int32 nNewCursorPos = 0;
203 aEvent.NewValue >>= nNewCursorPos;
204 QAccessible::updateAccessibility(
205 new QAccessibleTextCursorEvent(pQAccessibleInterface, nNewCursorPos));
206 return;
208 case AccessibleEventId::CHILD:
210 Reference<XAccessible> xChild;
211 if (aEvent.NewValue >>= xChild)
213 QAccessible::updateAccessibility(new QAccessibleEvent(
214 QtAccessibleRegistry::getQObject(xChild), QAccessible::ObjectCreated));
215 return;
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));
226 SAL_WARN("vcl.qt",
227 "Not forwarding AccessibleEventId::CHILD event for removed child "
228 "since it may cause crashes.");
229 return;
231 SAL_WARN("vcl.qt",
232 "Ignoring invalid AccessibleEventId::CHILD event without any child set.");
233 return;
235 case AccessibleEventId::HYPERTEXT_CHANGED:
236 QAccessible::updateAccessibility(
237 new QAccessibleEvent(pQAccessibleInterface, QAccessible::HypertextChanged));
238 return;
239 case AccessibleEventId::SELECTION_CHANGED:
240 QAccessible::updateAccessibility(
241 new QAccessibleEvent(pQAccessibleInterface, QAccessible::Selection));
242 return;
243 case AccessibleEventId::VISIBLE_DATA_CHANGED:
244 QAccessible::updateAccessibility(
245 new QAccessibleEvent(pQAccessibleInterface, QAccessible::VisibleDataChanged));
246 return;
247 case AccessibleEventId::TEXT_SELECTION_CHANGED:
249 QAccessibleTextInterface* pTextInterface = pQAccessibleInterface->textInterface();
250 if (!pTextInterface)
252 SAL_WARN("vcl.qt", "TEXT_SELECTION_CHANGED event received for object not "
253 "implementing text interface");
254 return;
256 int nStartOffset = 0;
257 int nEndOffset = 0;
258 pTextInterface->selection(0, &nStartOffset, &nEndOffset);
259 QAccessible::updateAccessibility(
260 new QAccessibleTextSelectionEvent(pQAccessibleInterface, nStartOffset, nEndOffset));
261 return;
263 case AccessibleEventId::TEXT_ATTRIBUTE_CHANGED:
264 QAccessible::updateAccessibility(
265 new QAccessibleEvent(pQAccessibleInterface, QAccessible::AttributeChanged));
266 return;
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)));
283 return;
285 case AccessibleEventId::TABLE_CAPTION_CHANGED:
286 QAccessible::updateAccessibility(
287 new QAccessibleEvent(pQAccessibleInterface, QAccessible::TableCaptionChanged));
288 return;
289 case AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED:
290 QAccessible::updateAccessibility(new QAccessibleEvent(
291 pQAccessibleInterface, QAccessible::TableColumnDescriptionChanged));
292 return;
293 case AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED:
294 QAccessible::updateAccessibility(
295 new QAccessibleEvent(pQAccessibleInterface, QAccessible::TableColumnHeaderChanged));
296 return;
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;
307 break;
308 case AccessibleTableModelChangeType::COLUMNS_REMOVED:
309 nType = QAccessibleTableModelChangeEvent::ColumnsRemoved;
310 break;
311 case AccessibleTableModelChangeType::ROWS_INSERTED:
312 nType = QAccessibleTableModelChangeEvent::RowsInserted;
313 break;
314 case AccessibleTableModelChangeType::ROWS_REMOVED:
315 nType = QAccessibleTableModelChangeEvent::RowsRemoved;
316 break;
317 case AccessibleTableModelChangeType::UPDATE:
318 nType = QAccessibleTableModelChangeEvent::DataChanged;
319 break;
320 default:
321 assert(false && "Unhandled AccessibleTableModelChangeType");
322 return;
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);
331 return;
333 case AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED:
334 QAccessible::updateAccessibility(new QAccessibleEvent(
335 pQAccessibleInterface, QAccessible::TableRowDescriptionChanged));
336 return;
337 case AccessibleEventId::TABLE_ROW_HEADER_CHANGED:
338 QAccessible::updateAccessibility(
339 new QAccessibleEvent(pQAccessibleInterface, QAccessible::TableRowHeaderChanged));
340 return;
341 case AccessibleEventId::TABLE_SUMMARY_CHANGED:
342 QAccessible::updateAccessibility(
343 new QAccessibleEvent(pQAccessibleInterface, QAccessible::TableSummaryChanged));
344 return;
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;
351 else
352 eEventType = QAccessible::SelectionRemove;
354 uno::Reference<accessibility::XAccessible> xChildAcc;
355 aEvent.NewValue >>= xChildAcc;
356 if (!xChildAcc.is())
358 SAL_WARN("vcl.qt",
359 "Selection add/remove event without the (un)selected accessible set");
360 return;
362 Reference<XAccessibleContext> xContext = xChildAcc->getAccessibleContext();
363 if (!xContext.is())
365 SAL_WARN("vcl.qt", "No valid XAccessibleContext for (un)selected accessible.");
366 return;
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));
373 return;
375 case AccessibleEventId::SELECTION_CHANGED_WITHIN:
376 QAccessible::updateAccessibility(
377 new QAccessibleEvent(pQAccessibleInterface, QAccessible::SelectionWithin));
378 return;
379 case AccessibleEventId::PAGE_CHANGED:
380 QAccessible::updateAccessibility(
381 new QAccessibleEvent(pQAccessibleInterface, QAccessible::PageChanged));
382 return;
383 case AccessibleEventId::SECTION_CHANGED:
384 QAccessible::updateAccessibility(
385 new QAccessibleEvent(pQAccessibleInterface, QAccessible::SectionChanged));
386 return;
387 case AccessibleEventId::COLUMN_CHANGED:
388 QAccessible::updateAccessibility(
389 new QAccessibleEvent(pQAccessibleInterface, QAccessible::TextColumnChanged));
390 return;
391 case AccessibleEventId::BOUNDRECT_CHANGED:
392 QAccessible::updateAccessibility(
393 new QAccessibleEvent(pQAccessibleInterface, QAccessible::LocationChanged));
394 return;
395 case AccessibleEventId::STATE_CHANGED:
396 HandleStateChangedEvent(pQAccessibleInterface, aEvent);
397 return;
398 case AccessibleEventId::VALUE_CHANGED:
400 QAccessibleValueInterface* pValueInterface = pQAccessibleInterface->valueInterface();
401 if (pValueInterface)
403 const QVariant aValue = pValueInterface->currentValue();
404 QAccessible::updateAccessibility(
405 new QAccessibleValueChangeEvent(pQAccessibleInterface, aValue));
407 return;
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:
422 default:
423 SAL_WARN("vcl.qt", "Unmapped AccessibleEventId: " << aEvent.EventId);
424 return;
428 void QtAccessibleEventListener::disposing(const EventObject& /* Source */)
430 assert(m_pAccessibleWidget);
431 m_pAccessibleWidget->invalidate();
434 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */