LibreOffice Module test (master) 1
AccessibilityTools.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21
22#include <com/sun/star/accessibility/AccessibleEventId.hpp>
23#include <com/sun/star/accessibility/AccessibleRelationType.hpp>
24#include <com/sun/star/accessibility/AccessibleRole.hpp>
25#include <com/sun/star/accessibility/AccessibleStateType.hpp>
26#include <com/sun/star/accessibility/XAccessible.hpp>
27#include <com/sun/star/accessibility/XAccessibleAction.hpp>
28#include <com/sun/star/accessibility/XAccessibleContext.hpp>
29#include <com/sun/star/awt/KeyModifier.hpp>
30
31#include <rtl/ustrbuf.hxx>
32#include <sal/log.hxx>
34#include <vcl/scheduler.hxx>
35#include <vcl/timer.hxx>
36#include <vcl/window.hxx>
37#include <o3tl/string_view.hxx>
38
39using namespace css;
40
41uno::Reference<accessibility::XAccessibleContext>
43 const uno::Reference<accessibility::XAccessibleContext>& xCtx,
44 const std::function<bool(const uno::Reference<accessibility::XAccessibleContext>&)>& cPredicate)
45{
46 if (cPredicate(xCtx))
47 {
48 return xCtx;
49 }
50 else
51 {
52 sal_Int64 count = xCtx->getAccessibleChildCount();
53
54 for (sal_Int64 i = 0; i < count && i < AccessibilityTools::MAX_CHILDREN; i++)
55 {
56 uno::Reference<accessibility::XAccessibleContext> xCtx2
57 = getAccessibleObjectForPredicate(xCtx->getAccessibleChild(i), cPredicate);
58 if (xCtx2.is())
59 return xCtx2;
60 }
61 }
62 return nullptr;
63}
64
65uno::Reference<accessibility::XAccessibleContext>
67 const uno::Reference<accessibility::XAccessible>& xAcc,
68 const std::function<bool(const uno::Reference<accessibility::XAccessibleContext>&)>& cPredicate)
69{
70 return getAccessibleObjectForPredicate(xAcc->getAccessibleContext(), cPredicate);
71}
72
73uno::Reference<accessibility::XAccessibleContext> AccessibilityTools::getAccessibleObjectForRole(
74 const uno::Reference<accessibility::XAccessibleContext>& xCtx, sal_Int16 role)
75{
77 xCtx, [&role](const uno::Reference<accessibility::XAccessibleContext>& xObjCtx) {
78 return (xObjCtx->getAccessibleRole() == role
79 && xObjCtx->getAccessibleStateSet()
80 & accessibility::AccessibleStateType::SHOWING);
81 });
82}
83
84css::uno::Reference<css::accessibility::XAccessibleContext>
86 const css::uno::Reference<css::accessibility::XAccessible>& xacc, sal_Int16 role)
87{
88 return getAccessibleObjectForRole(xacc->getAccessibleContext(), role);
89}
90
91/* this is basically the same as getAccessibleObjectForPredicate() but specialized for efficiency,
92 * and because the template version will not work with getAccessibleObjectForPredicate() anyway */
93css::uno::Reference<css::accessibility::XAccessibleContext>
95 const css::uno::Reference<css::accessibility::XAccessibleContext>& xCtx, const sal_Int16 role,
96 std::u16string_view name)
97{
98 if (xCtx->getAccessibleRole() == role && nameEquals(xCtx, name))
99 return xCtx;
100
101 auto nChildren = xCtx->getAccessibleChildCount();
102 for (decltype(nChildren) i = 0; i < nChildren && i < AccessibilityTools::MAX_CHILDREN; i++)
103 {
104 if (auto xMatchChild = getAccessibleObjectForName(xCtx->getAccessibleChild(i), role, name))
105 return xMatchChild;
106 }
107
108 return nullptr;
109}
110
111bool AccessibilityTools::equals(const uno::Reference<accessibility::XAccessible>& xacc1,
112 const uno::Reference<accessibility::XAccessible>& xacc2)
113{
114 if (!xacc1.is() || !xacc2.is())
115 return xacc1.is() == xacc2.is();
116 return equals(xacc1->getAccessibleContext(), xacc2->getAccessibleContext());
117}
118
119bool AccessibilityTools::equals(const uno::Reference<accessibility::XAccessibleContext>& xctx1,
120 const uno::Reference<accessibility::XAccessibleContext>& xctx2)
121{
122 if (!xctx1.is() || !xctx2.is())
123 return xctx1.is() == xctx2.is();
124
125 if (xctx1->getAccessibleRole() != xctx2->getAccessibleRole())
126 return false;
127
128 if (xctx1->getAccessibleName() != xctx2->getAccessibleName())
129 return false;
130
131 if (xctx1->getAccessibleDescription() != xctx2->getAccessibleDescription())
132 return false;
133
134 if (xctx1->getAccessibleChildCount() != xctx2->getAccessibleChildCount())
135 return false;
136
137 /* this one was not in the Java version */
138 if (xctx1->getAccessibleIndexInParent() != xctx2->getAccessibleIndexInParent())
139 return false;
140
141 /* because in Writer at least some children only are referenced by their relations to others
142 * objects, we need to account for that as their index in parent is incorrect (so not
143 * necessarily unique) */
144 auto relset1 = xctx1->getAccessibleRelationSet();
145 auto relset2 = xctx2->getAccessibleRelationSet();
146 if (relset1.is() != relset2.is())
147 return false;
148 else if (relset1.is())
149 {
150 auto relCount1 = relset1->getRelationCount();
151 auto relCount2 = relset2->getRelationCount();
152 if (relCount1 != relCount2)
153 return false;
154
155 for (sal_Int32 i = 0; i < relCount1; ++i)
156 {
157 if (relset1->getRelation(i) != relset2->getRelation(i))
158 return false;
159 }
160 }
161
162 return equals(xctx1->getAccessibleParent(), xctx2->getAccessibleParent());
163}
164
165bool AccessibilityTools::nameEquals(const uno::Reference<accessibility::XAccessibleContext>& xCtx,
166 const std::u16string_view name)
167{
168 auto ctxName = xCtx->getAccessibleName();
169 std::u16string_view rest;
170
171 if (!o3tl::starts_with(ctxName, name, &rest))
172 return false;
173 if (rest == u"")
174 return true;
175
176#if defined(_WIN32)
177 // see OAccessibleMenuItemComponent::GetAccessibleName():
178 // on Win32, ignore a \tSHORTCUT suffix on a menu item
179 switch (xCtx->getAccessibleRole())
180 {
181 case accessibility::AccessibleRole::MENU_ITEM:
182 case accessibility::AccessibleRole::RADIO_MENU_ITEM:
183 case accessibility::AccessibleRole::CHECK_MENU_ITEM:
184 return rest[0] == '\t';
185
186 default:
187 break;
188 }
189#endif
190
191#if OSL_DEBUG_LEVEL > 0
192 // see VCLXAccessibleComponent::getAccessibleName()
193 auto pVCLXAccessibleComponent = dynamic_cast<VCLXAccessibleComponent*>(xCtx.get());
194 if (pVCLXAccessibleComponent)
195 {
196 auto windowType = pVCLXAccessibleComponent->GetWindow()->GetType();
197 if (rest
198 == Concat2View(u" (Type = " + OUString::number(static_cast<sal_Int32>(windowType))
199 + ")"))
200 return true;
201 }
202#endif
203 return false;
204}
205
206static OUString unknownName(const sal_Int64 value)
207{
208 return "unknown (" + OUString::number(value) + ")";
209}
210
211OUString AccessibilityTools::getRoleName(const sal_Int16 role)
212{
213 switch (role)
214 {
215 case accessibility::AccessibleRole::UNKNOWN:
216 return "UNKNOWN";
217 case accessibility::AccessibleRole::ALERT:
218 return "ALERT";
219 case accessibility::AccessibleRole::BUTTON_DROPDOWN:
220 return "BUTTON_DROPDOWN";
221 case accessibility::AccessibleRole::BUTTON_MENU:
222 return "BUTTON_MENU";
223 case accessibility::AccessibleRole::CANVAS:
224 return "CANVAS";
225 case accessibility::AccessibleRole::CAPTION:
226 return "CAPTION";
227 case accessibility::AccessibleRole::CHART:
228 return "CHART";
229 case accessibility::AccessibleRole::CHECK_BOX:
230 return "CHECK_BOX";
231 case accessibility::AccessibleRole::CHECK_MENU_ITEM:
232 return "CHECK_MENU_ITEM";
233 case accessibility::AccessibleRole::COLOR_CHOOSER:
234 return "COLOR_CHOOSER";
235 case accessibility::AccessibleRole::COLUMN_HEADER:
236 return "COLUMN_HEADER";
237 case accessibility::AccessibleRole::COMBO_BOX:
238 return "COMBO_BOX";
239 case accessibility::AccessibleRole::COMMENT:
240 return "COMMENT";
241 case accessibility::AccessibleRole::COMMENT_END:
242 return "COMMENT_END";
243 case accessibility::AccessibleRole::DATE_EDITOR:
244 return "DATE_EDITOR";
245 case accessibility::AccessibleRole::DESKTOP_ICON:
246 return "DESKTOP_ICON";
247 case accessibility::AccessibleRole::DESKTOP_PANE:
248 return "DESKTOP_PANE";
249 case accessibility::AccessibleRole::DIALOG:
250 return "DIALOG";
251 case accessibility::AccessibleRole::DIRECTORY_PANE:
252 return "DIRECTORY_PANE";
253 case accessibility::AccessibleRole::DOCUMENT:
254 return "DOCUMENT";
255 case accessibility::AccessibleRole::DOCUMENT_PRESENTATION:
256 return "DOCUMENT_PRESENTATION";
257 case accessibility::AccessibleRole::DOCUMENT_SPREADSHEET:
258 return "DOCUMENT_SPREADSHEET";
259 case accessibility::AccessibleRole::DOCUMENT_TEXT:
260 return "DOCUMENT_TEXT";
261 case accessibility::AccessibleRole::EDIT_BAR:
262 return "EDIT_BAR";
263 case accessibility::AccessibleRole::EMBEDDED_OBJECT:
264 return "EMBEDDED_OBJECT";
265 case accessibility::AccessibleRole::END_NOTE:
266 return "END_NOTE";
267 case accessibility::AccessibleRole::FILE_CHOOSER:
268 return "FILE_CHOOSER";
269 case accessibility::AccessibleRole::FILLER:
270 return "FILLER";
271 case accessibility::AccessibleRole::FONT_CHOOSER:
272 return "FONT_CHOOSER";
273 case accessibility::AccessibleRole::FOOTER:
274 return "FOOTER";
275 case accessibility::AccessibleRole::FOOTNOTE:
276 return "FOOTNOTE";
277 case accessibility::AccessibleRole::FORM:
278 return "FORM";
279 case accessibility::AccessibleRole::FRAME:
280 return "FRAME";
281 case accessibility::AccessibleRole::GLASS_PANE:
282 return "GLASS_PANE";
283 case accessibility::AccessibleRole::GRAPHIC:
284 return "GRAPHIC";
285 case accessibility::AccessibleRole::GROUP_BOX:
286 return "GROUP_BOX";
287 case accessibility::AccessibleRole::HEADER:
288 return "HEADER";
289 case accessibility::AccessibleRole::HEADING:
290 return "HEADING";
291 case accessibility::AccessibleRole::HYPER_LINK:
292 return "HYPER_LINK";
293 case accessibility::AccessibleRole::ICON:
294 return "ICON";
295 case accessibility::AccessibleRole::IMAGE_MAP:
296 return "IMAGE_MAP";
297 case accessibility::AccessibleRole::INTERNAL_FRAME:
298 return "INTERNAL_FRAME";
299 case accessibility::AccessibleRole::LABEL:
300 return "LABEL";
301 case accessibility::AccessibleRole::LAYERED_PANE:
302 return "LAYERED_PANE";
303 case accessibility::AccessibleRole::LIST:
304 return "LIST";
305 case accessibility::AccessibleRole::LIST_ITEM:
306 return "LIST_ITEM";
307 case accessibility::AccessibleRole::MENU:
308 return "MENU";
309 case accessibility::AccessibleRole::MENU_BAR:
310 return "MENU_BAR";
311 case accessibility::AccessibleRole::MENU_ITEM:
312 return "MENU_ITEM";
313 case accessibility::AccessibleRole::NOTE:
314 return "NOTE";
315 case accessibility::AccessibleRole::OPTION_PANE:
316 return "OPTION_PANE";
317 case accessibility::AccessibleRole::PAGE:
318 return "PAGE";
319 case accessibility::AccessibleRole::PAGE_TAB:
320 return "PAGE_TAB";
321 case accessibility::AccessibleRole::PAGE_TAB_LIST:
322 return "PAGE_TAB_LIST";
323 case accessibility::AccessibleRole::PANEL:
324 return "PANEL";
325 case accessibility::AccessibleRole::PARAGRAPH:
326 return "PARAGRAPH";
327 case accessibility::AccessibleRole::PASSWORD_TEXT:
328 return "PASSWORD_TEXT";
329 case accessibility::AccessibleRole::POPUP_MENU:
330 return "POPUP_MENU";
331 case accessibility::AccessibleRole::PROGRESS_BAR:
332 return "PROGRESS_BAR";
333 case accessibility::AccessibleRole::PUSH_BUTTON:
334 return "PUSH_BUTTON";
335 case accessibility::AccessibleRole::RADIO_BUTTON:
336 return "RADIO_BUTTON";
337 case accessibility::AccessibleRole::RADIO_MENU_ITEM:
338 return "RADIO_MENU_ITEM";
339 case accessibility::AccessibleRole::ROOT_PANE:
340 return "ROOT_PANE";
341 case accessibility::AccessibleRole::ROW_HEADER:
342 return "ROW_HEADER";
343 case accessibility::AccessibleRole::RULER:
344 return "RULER";
345 case accessibility::AccessibleRole::SCROLL_BAR:
346 return "SCROLL_BAR";
347 case accessibility::AccessibleRole::SCROLL_PANE:
348 return "SCROLL_PANE";
349 case accessibility::AccessibleRole::SECTION:
350 return "SECTION";
351 case accessibility::AccessibleRole::SEPARATOR:
352 return "SEPARATOR";
353 case accessibility::AccessibleRole::SHAPE:
354 return "SHAPE";
355 case accessibility::AccessibleRole::SLIDER:
356 return "SLIDER";
357 case accessibility::AccessibleRole::SPIN_BOX:
358 return "SPIN_BOX";
359 case accessibility::AccessibleRole::SPLIT_PANE:
360 return "SPLIT_PANE";
361 case accessibility::AccessibleRole::STATIC:
362 return "STATIC";
363 case accessibility::AccessibleRole::STATUS_BAR:
364 return "STATUS_BAR";
365 case accessibility::AccessibleRole::TABLE:
366 return "TABLE";
367 case accessibility::AccessibleRole::TABLE_CELL:
368 return "TABLE_CELL";
369 case accessibility::AccessibleRole::TEXT:
370 return "TEXT";
371 case accessibility::AccessibleRole::TEXT_FRAME:
372 return "TEXT_FRAME";
373 case accessibility::AccessibleRole::TOGGLE_BUTTON:
374 return "TOGGLE_BUTTON";
375 case accessibility::AccessibleRole::TOOL_BAR:
376 return "TOOL_BAR";
377 case accessibility::AccessibleRole::TOOL_TIP:
378 return "TOOL_TIP";
379 case accessibility::AccessibleRole::TREE:
380 return "TREE";
381 case accessibility::AccessibleRole::TREE_ITEM:
382 return "TREE_ITEM";
383 case accessibility::AccessibleRole::TREE_TABLE:
384 return "TREE_TABLE";
385 case accessibility::AccessibleRole::VIEW_PORT:
386 return "VIEW_PORT";
387 case accessibility::AccessibleRole::WINDOW:
388 return "WINDOW";
389 };
390 return unknownName(role);
391}
392
393OUString AccessibilityTools::debugAccessibleStateSet(const sal_Int64 nCombinedState)
394{
395 OUString combinedName;
396
397 for (int i = 0; i < 63; i++)
398 {
399 sal_Int64 state = sal_Int64(1) << i;
400 if (!(state & nCombinedState))
401 continue;
402 OUString name;
403 switch (state)
404 {
405 case accessibility::AccessibleStateType::ACTIVE:
406 name = "ACTIVE";
407 break;
408 case accessibility::AccessibleStateType::ARMED:
409 name = "ARMED";
410 break;
411 case accessibility::AccessibleStateType::BUSY:
412 name = "BUSY";
413 break;
414 case accessibility::AccessibleStateType::CHECKED:
415 name = "CHECKED";
416 break;
417 case accessibility::AccessibleStateType::COLLAPSE:
418 name = "COLLAPSE";
419 break;
420 case accessibility::AccessibleStateType::DEFAULT:
421 name = "DEFAULT";
422 break;
423 case accessibility::AccessibleStateType::DEFUNC:
424 name = "DEFUNC";
425 break;
426 case accessibility::AccessibleStateType::EDITABLE:
427 name = "EDITABLE";
428 break;
429 case accessibility::AccessibleStateType::ENABLED:
430 name = "ENABLED";
431 break;
432 case accessibility::AccessibleStateType::EXPANDABLE:
433 name = "EXPANDABLE";
434 break;
435 case accessibility::AccessibleStateType::EXPANDED:
436 name = "EXPANDED";
437 break;
438 case accessibility::AccessibleStateType::FOCUSABLE:
439 name = "FOCUSABLE";
440 break;
441 case accessibility::AccessibleStateType::FOCUSED:
442 name = "FOCUSED";
443 break;
444 case accessibility::AccessibleStateType::HORIZONTAL:
445 name = "HORIZONTAL";
446 break;
447 case accessibility::AccessibleStateType::ICONIFIED:
448 name = "ICONIFIED";
449 break;
450 case accessibility::AccessibleStateType::INDETERMINATE:
451 name = "INDETERMINATE";
452 break;
453 case accessibility::AccessibleStateType::INVALID:
454 name = "INVALID";
455 break;
456 case accessibility::AccessibleStateType::MANAGES_DESCENDANTS:
457 name = "MANAGES_DESCENDANTS";
458 break;
459 case accessibility::AccessibleStateType::MODAL:
460 name = "MODAL";
461 break;
462 case accessibility::AccessibleStateType::MOVEABLE:
463 name = "MOVEABLE";
464 break;
465 case accessibility::AccessibleStateType::MULTI_LINE:
466 name = "MULTI_LINE";
467 break;
468 case accessibility::AccessibleStateType::MULTI_SELECTABLE:
469 name = "MULTI_SELECTABLE";
470 break;
471 case accessibility::AccessibleStateType::OFFSCREEN:
472 name = "OFFSCREEN";
473 break;
474 case accessibility::AccessibleStateType::OPAQUE:
475 name = "OPAQUE";
476 break;
477 case accessibility::AccessibleStateType::PRESSED:
478 name = "PRESSED";
479 break;
480 case accessibility::AccessibleStateType::RESIZABLE:
481 name = "RESIZABLE";
482 break;
483 case accessibility::AccessibleStateType::SELECTABLE:
484 name = "SELECTABLE";
485 break;
486 case accessibility::AccessibleStateType::SELECTED:
487 name = "SELECTED";
488 break;
489 case accessibility::AccessibleStateType::SENSITIVE:
490 name = "SENSITIVE";
491 break;
492 case accessibility::AccessibleStateType::SHOWING:
493 name = "SHOWING";
494 break;
495 case accessibility::AccessibleStateType::SINGLE_LINE:
496 name = "SINGLE_LINE";
497 break;
498 case accessibility::AccessibleStateType::STALE:
499 name = "STALE";
500 break;
501 case accessibility::AccessibleStateType::TRANSIENT:
502 name = "TRANSIENT";
503 break;
504 case accessibility::AccessibleStateType::VERTICAL:
505 name = "VERTICAL";
506 break;
507 case accessibility::AccessibleStateType::VISIBLE:
508 name = "VISIBLE";
509 break;
510 default:
511 name = unknownName(state);
512 break;
513 }
514 if (combinedName.getLength())
515 combinedName += " | ";
516 combinedName += name;
517 }
518
519 if (combinedName.isEmpty())
520 return "unknown";
521 return combinedName;
522}
523
524OUString AccessibilityTools::getEventIdName(const sal_Int16 event_id)
525{
526 switch (event_id)
527 {
528 case accessibility::AccessibleEventId::ACTION_CHANGED:
529 return "ACTION_CHANGED";
530 case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED:
531 return "ACTIVE_DESCENDANT_CHANGED";
532 case accessibility::AccessibleEventId::ACTIVE_DESCENDANT_CHANGED_NOFOCUS:
533 return "ACTIVE_DESCENDANT_CHANGED_NOFOCUS";
534 case accessibility::AccessibleEventId::BOUNDRECT_CHANGED:
535 return "BOUNDRECT_CHANGED";
536 case accessibility::AccessibleEventId::CARET_CHANGED:
537 return "CARET_CHANGED";
538 case accessibility::AccessibleEventId::CHILD:
539 return "CHILD";
540 case accessibility::AccessibleEventId::COLUMN_CHANGED:
541 return "COLUMN_CHANGED";
542 case accessibility::AccessibleEventId::CONTENT_FLOWS_FROM_RELATION_CHANGED:
543 return "CONTENT_FLOWS_FROM_RELATION_CHANGED";
544 case accessibility::AccessibleEventId::CONTENT_FLOWS_TO_RELATION_CHANGED:
545 return "CONTENT_FLOWS_TO_RELATION_CHANGED";
546 case accessibility::AccessibleEventId::CONTROLLED_BY_RELATION_CHANGED:
547 return "CONTROLLED_BY_RELATION_CHANGED";
548 case accessibility::AccessibleEventId::CONTROLLER_FOR_RELATION_CHANGED:
549 return "CONTROLLER_FOR_RELATION_CHANGED";
550 case accessibility::AccessibleEventId::DESCRIPTION_CHANGED:
551 return "DESCRIPTION_CHANGED";
552 case accessibility::AccessibleEventId::HYPERTEXT_CHANGED:
553 return "HYPERTEXT_CHANGED";
554 case accessibility::AccessibleEventId::INVALIDATE_ALL_CHILDREN:
555 return "INVALIDATE_ALL_CHILDREN";
556 case accessibility::AccessibleEventId::LABELED_BY_RELATION_CHANGED:
557 return "LABELED_BY_RELATION_CHANGED";
558 case accessibility::AccessibleEventId::LABEL_FOR_RELATION_CHANGED:
559 return "LABEL_FOR_RELATION_CHANGED";
560 case accessibility::AccessibleEventId::LISTBOX_ENTRY_COLLAPSED:
561 return "LISTBOX_ENTRY_COLLAPSED";
562 case accessibility::AccessibleEventId::LISTBOX_ENTRY_EXPANDED:
563 return "LISTBOX_ENTRY_EXPANDED";
564 case accessibility::AccessibleEventId::MEMBER_OF_RELATION_CHANGED:
565 return "MEMBER_OF_RELATION_CHANGED";
566 case accessibility::AccessibleEventId::NAME_CHANGED:
567 return "NAME_CHANGED";
568 case accessibility::AccessibleEventId::PAGE_CHANGED:
569 return "PAGE_CHANGED";
570 case accessibility::AccessibleEventId::ROLE_CHANGED:
571 return "ROLE_CHANGED";
572 case accessibility::AccessibleEventId::SECTION_CHANGED:
573 return "SECTION_CHANGED";
574 case accessibility::AccessibleEventId::SELECTION_CHANGED:
575 return "SELECTION_CHANGED";
576 case accessibility::AccessibleEventId::SELECTION_CHANGED_ADD:
577 return "SELECTION_CHANGED_ADD";
578 case accessibility::AccessibleEventId::SELECTION_CHANGED_REMOVE:
579 return "SELECTION_CHANGED_REMOVE";
580 case accessibility::AccessibleEventId::SELECTION_CHANGED_WITHIN:
581 return "SELECTION_CHANGED_WITHIN";
582 case accessibility::AccessibleEventId::STATE_CHANGED:
583 return "STATE_CHANGED";
584 case accessibility::AccessibleEventId::SUB_WINDOW_OF_RELATION_CHANGED:
585 return "SUB_WINDOW_OF_RELATION_CHANGED";
586 case accessibility::AccessibleEventId::TABLE_CAPTION_CHANGED:
587 return "TABLE_CAPTION_CHANGED";
588 case accessibility::AccessibleEventId::TABLE_COLUMN_DESCRIPTION_CHANGED:
589 return "TABLE_COLUMN_DESCRIPTION_CHANGED";
590 case accessibility::AccessibleEventId::TABLE_COLUMN_HEADER_CHANGED:
591 return "TABLE_COLUMN_HEADER_CHANGED";
592 case accessibility::AccessibleEventId::TABLE_MODEL_CHANGED:
593 return "TABLE_MODEL_CHANGED";
594 case accessibility::AccessibleEventId::TABLE_ROW_DESCRIPTION_CHANGED:
595 return "TABLE_ROW_DESCRIPTION_CHANGED";
596 case accessibility::AccessibleEventId::TABLE_ROW_HEADER_CHANGED:
597 return "TABLE_ROW_HEADER_CHANGED";
598 case accessibility::AccessibleEventId::TABLE_SUMMARY_CHANGED:
599 return "TABLE_SUMMARY_CHANGED";
600 case accessibility::AccessibleEventId::TEXT_ATTRIBUTE_CHANGED:
601 return "TEXT_ATTRIBUTE_CHANGED";
602 case accessibility::AccessibleEventId::TEXT_CHANGED:
603 return "TEXT_CHANGED";
604 case accessibility::AccessibleEventId::TEXT_SELECTION_CHANGED:
605 return "TEXT_SELECTION_CHANGED";
606 case accessibility::AccessibleEventId::VALUE_CHANGED:
607 return "VALUE_CHANGED";
608 case accessibility::AccessibleEventId::VISIBLE_DATA_CHANGED:
609 return "VISIBLE_DATA_CHANGED";
610 }
611 return unknownName(event_id);
612}
613
614OUString AccessibilityTools::getRelationTypeName(const sal_Int16 rel_type)
615{
616 switch (rel_type)
617 {
618 case accessibility::AccessibleRelationType::CONTENT_FLOWS_FROM:
619 return "CONTENT_FLOWS_FROM";
620 case accessibility::AccessibleRelationType::CONTENT_FLOWS_TO:
621 return "CONTENT_FLOWS_TO";
622 case accessibility::AccessibleRelationType::CONTROLLED_BY:
623 return "CONTROLLED_BY";
624 case accessibility::AccessibleRelationType::CONTROLLER_FOR:
625 return "CONTROLLER_FOR";
626 case accessibility::AccessibleRelationType::DESCRIBED_BY:
627 return "DESCRIBED_BY";
628 case accessibility::AccessibleRelationType::INVALID:
629 return "INVALID";
630 case accessibility::AccessibleRelationType::LABELED_BY:
631 return "LABELED_BY";
632 case accessibility::AccessibleRelationType::LABEL_FOR:
633 return "LABEL_FOR";
634 case accessibility::AccessibleRelationType::MEMBER_OF:
635 return "MEMBER_OF";
636 case accessibility::AccessibleRelationType::NODE_CHILD_OF:
637 return "NODE_CHILD_OF";
638 case accessibility::AccessibleRelationType::SUB_WINDOW_OF:
639 return "SUB_WINDOW_OF";
640 }
641 return unknownName(rel_type);
642}
643
644OUString AccessibilityTools::debugName(accessibility::XAccessibleContext* ctx)
645{
646 return "role=" + AccessibilityTools::getRoleName(ctx->getAccessibleRole()) + " name=\""
647 + ctx->getAccessibleName() + "\" description=\"" + ctx->getAccessibleDescription()
648 + "\"";
649}
650
651OUString AccessibilityTools::debugName(accessibility::XAccessible* acc)
652{
653 return debugName(acc->getAccessibleContext().get());
654}
655
656OUString AccessibilityTools::debugName(accessibility::XAccessibleAction* xAct)
657{
658 OUStringBuffer r = "actions=[";
659
660 const sal_Int32 nActions = xAct->getAccessibleActionCount();
661 for (sal_Int32 i = 0; i < nActions; i++)
662 {
663 if (i > 0)
664 r.append(", ");
665
666 r.append("description=\"" + xAct->getAccessibleActionDescription(i) + "\"");
667
668 const auto& xKeyBinding = xAct->getAccessibleActionKeyBinding(i);
669 if (xKeyBinding)
670 {
671 r.append(" keybindings=[");
672 const sal_Int32 nKeyBindings = xKeyBinding->getAccessibleKeyBindingCount();
673 for (sal_Int32 j = 0; j < nKeyBindings; j++)
674 {
675 if (j > 0)
676 r.append(", ");
677
678 int k = 0;
679 for (const auto& keyStroke : xKeyBinding->getAccessibleKeyBinding(j))
680 {
681 if (k++ > 0)
682 r.append(", ");
683
684 r.append('"');
685 if (keyStroke.Modifiers & awt::KeyModifier::MOD1)
686 r.append("<Mod1>");
687 if (keyStroke.Modifiers & awt::KeyModifier::MOD2)
688 r.append("<Mod2>");
689 if (keyStroke.Modifiers & awt::KeyModifier::MOD3)
690 r.append("<Mod3>");
691 if (keyStroke.Modifiers & awt::KeyModifier::SHIFT)
692 r.append("<Shift>");
693 r.append(OUStringChar(keyStroke.KeyChar) + "\"");
694 }
695 }
696 r.append("]");
697 }
698 }
699 r.append("]");
700 return r.makeStringAndClear();
701}
702
703OUString AccessibilityTools::debugName(accessibility::XAccessibleText* xTxt)
704{
705 uno::Reference<accessibility::XAccessibleContext> xCtx(xTxt, uno::UNO_QUERY);
706 return debugName(xCtx.get());
707}
708
709OUString AccessibilityTools::debugName(const accessibility::AccessibleEventObject* evobj)
710{
711 return "(AccessibleEventObject) { id=" + getEventIdName(evobj->EventId)
712 + " old=" + evobj->OldValue.getValueTypeName()
713 + " new=" + evobj->NewValue.getValueTypeName() + " }";
714}
715
716bool AccessibilityTools::Await(const std::function<bool()>& cUntilCallback, sal_uInt64 nTimeoutMs)
717{
718 bool success = false;
719 Timer aTimer("wait for event");
720 aTimer.SetTimeout(nTimeoutMs);
721 aTimer.Start();
722 do
723 {
725 success = cUntilCallback();
726 } while (!success && aTimer.IsActive());
727 SAL_WARN_IF(!success, "test", "timeout reached");
728 return success;
729}
730
731void AccessibilityTools::Wait(sal_uInt64 nTimeoutMs)
732{
733 Timer aTimer("wait for event");
734 aTimer.SetTimeout(nTimeoutMs);
735 aTimer.Start();
736 std::cout << "waiting for " << nTimeoutMs << "ms... ";
737 do
738 {
740 } while (aTimer.IsActive());
741 std::cout << "ok." << std::endl;
742}
743
744/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
static OUString unknownName(const sal_Int64 value)
static const sal_Int32 MAX_CHILDREN
Maximum number of children to work on.
static OUString getRoleName(const sal_Int16 role)
static OUString debugName(css::accessibility::XAccessibleContext *xctx)
static void Wait(sal_uInt64 nTimeoutMs)
Process events for a given time.
static bool nameEquals(const css::uno::Reference< css::accessibility::XAccessibleContext > &xCtx, const std::u16string_view name)
Compares the accessible name against a string.
static css::uno::Reference< css::accessibility::XAccessibleContext > getAccessibleObjectForRole(const css::uno::Reference< css::accessibility::XAccessibleContext > &xCtx, sal_Int16 role)
static OUString debugAccessibleStateSet(sal_Int64 p)
static bool Await(const std::function< bool()> &cUntilCallback, sal_uInt64 nTimeoutMs=3000)
Process events until a condition or a timeout.
static OUString getRelationTypeName(const sal_Int16 rel_type)
static OUString getEventIdName(const sal_Int16 event_id)
static css::uno::Reference< css::accessibility::XAccessibleContext > getAccessibleObjectForName(const css::uno::Reference< css::accessibility::XAccessibleContext > &xCtx, const sal_Int16 role, std::u16string_view name)
Gets a descendant of xCtx (or xCtx itself) that matches the given role and name.
static css::uno::Reference< css::accessibility::XAccessibleContext > getAccessibleObjectForPredicate(const css::uno::Reference< css::accessibility::XAccessibleContext > &xCtx, const std::function< bool(const css::uno::Reference< css::accessibility::XAccessibleContext > &)> &cPredicate)
static bool equals(const css::uno::Reference< css::accessibility::XAccessible > &xacc1, const css::uno::Reference< css::accessibility::XAccessible > &xacc2)
static void ProcessEventsToIdle()
bool IsActive() const
void SetTimeout(sal_uInt64 nTimeoutMs)
virtual void Start(bool bStartTimer=true) override
Any value
const char * name
#define SAL_WARN_IF(condition, area, stream)
int i
ctx
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept