android: Update app-specific/MIME type icons
[LibreOffice.git] / vcl / qt5 / QtAccessibleWidget.cxx
blob84b398c006cc09874db246dea86081127a97dd85
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 <QtAccessibleWidget.hxx>
22 #include <QtGui/QAccessibleInterface>
24 #include <QtAccessibleEventListener.hxx>
25 #include <QtAccessibleRegistry.hxx>
26 #include <QtFrame.hxx>
27 #include <QtTools.hxx>
28 #include <QtWidget.hxx>
29 #include <QtXAccessible.hxx>
31 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
32 #include <com/sun/star/accessibility/AccessibleRole.hpp>
33 #include <com/sun/star/accessibility/AccessibleScrollType.hpp>
34 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
35 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
36 #include <com/sun/star/accessibility/XAccessible.hpp>
37 #include <com/sun/star/accessibility/XAccessibleAction.hpp>
38 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
39 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
40 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
41 #include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
42 #include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp>
43 #include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
44 #include <com/sun/star/accessibility/XAccessibleSelection.hpp>
45 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
46 #include <com/sun/star/accessibility/XAccessibleTableSelection.hpp>
47 #include <com/sun/star/accessibility/XAccessibleText.hpp>
48 #include <com/sun/star/accessibility/XAccessibleValue.hpp>
49 #include <com/sun/star/awt/FontSlant.hpp>
50 #include <com/sun/star/awt/FontUnderline.hpp>
51 #include <com/sun/star/awt/FontWeight.hpp>
52 #include <com/sun/star/beans/PropertyValue.hpp>
53 #include <com/sun/star/lang/DisposedException.hpp>
54 #include <com/sun/star/uno/Sequence.hxx>
56 #include <comphelper/AccessibleImplementationHelper.hxx>
57 #include <o3tl/any.hxx>
58 #include <sal/log.hxx>
60 using namespace css;
61 using namespace css::accessibility;
62 using namespace css::beans;
63 using namespace css::uno;
65 QtAccessibleWidget::QtAccessibleWidget(const Reference<XAccessible> xAccessible, QObject* pObject)
66 : m_xAccessible(xAccessible)
67 , m_pObject(pObject)
69 Reference<XAccessibleContext> xContext = xAccessible->getAccessibleContext();
70 Reference<XAccessibleEventBroadcaster> xBroadcaster(xContext, UNO_QUERY);
71 if (xBroadcaster.is())
73 Reference<XAccessibleEventListener> xListener(new QtAccessibleEventListener(this));
74 xBroadcaster->addAccessibleEventListener(xListener);
78 void QtAccessibleWidget::invalidate()
80 QtAccessibleRegistry::remove(m_xAccessible);
81 m_xAccessible.clear();
84 Reference<XAccessibleContext> QtAccessibleWidget::getAccessibleContextImpl() const
86 Reference<XAccessibleContext> xAc;
88 if (m_xAccessible.is())
90 try
92 xAc = m_xAccessible->getAccessibleContext();
94 catch (css::lang::DisposedException /*ex*/)
96 SAL_WARN("vcl.qt", "Accessible context disposed already");
98 // sometimes getAccessibleContext throws also RuntimeException if context is no longer alive
99 catch (css::uno::RuntimeException /*ex*/)
101 // so let's catch it here, cuz otherwise soffice falls flat on its face
102 // with FatalError and nothing else
103 SAL_WARN("vcl.qt", "Accessible context no longer alive");
107 return xAc;
110 css::uno::Reference<css::accessibility::XAccessibleTable>
111 QtAccessibleWidget::getAccessibleTableForParent() const
113 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
114 if (!xAcc.is())
115 return nullptr;
117 Reference<XAccessible> xParent = xAcc->getAccessibleParent();
118 if (!xParent.is())
119 return nullptr;
121 Reference<XAccessibleContext> xParentContext = xParent->getAccessibleContext();
122 if (!xParentContext.is())
123 return nullptr;
125 return Reference<XAccessibleTable>(xParentContext, UNO_QUERY);
128 QWindow* QtAccessibleWidget::window() const
130 assert(m_pObject);
131 if (m_pObject->isWidgetType())
133 QWidget* pWidget = static_cast<QWidget*>(m_pObject);
134 QWidget* pWindow = pWidget->window();
135 if (pWindow)
136 return pWindow->windowHandle();
139 QAccessibleInterface* pParent = parent();
140 if (pParent)
141 return pParent->window();
143 return nullptr;
146 int QtAccessibleWidget::childCount() const
148 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
149 if (!xAc.is())
150 return 0;
152 sal_Int64 nChildCount = xAc->getAccessibleChildCount();
153 if (nChildCount > std::numeric_limits<int>::max())
155 SAL_WARN("vcl.qt", "QtAccessibleWidget::childCount: Child count exceeds maximum int value, "
156 "returning max int.");
157 nChildCount = std::numeric_limits<int>::max();
160 return nChildCount;
163 int QtAccessibleWidget::indexOfChild(const QAccessibleInterface* pChild) const
165 const QtAccessibleWidget* pAccessibleWidget = dynamic_cast<const QtAccessibleWidget*>(pChild);
166 if (!pAccessibleWidget)
168 SAL_WARN(
169 "vcl.qt",
170 "QtAccessibleWidget::indexOfChild called with child that is no QtAccessibleWidget");
171 return -1;
174 Reference<XAccessibleContext> xContext = pAccessibleWidget->getAccessibleContextImpl();
175 if (!xContext.is())
176 return -1;
178 sal_Int64 nChildIndex = xContext->getAccessibleIndexInParent();
179 if (nChildIndex > std::numeric_limits<int>::max())
181 // use -2 when the child index is too large to fit into 32 bit to neither use the
182 // valid index of another child nor -1, which would e.g. make the Orca screen reader
183 // interpret the object as being a zombie
184 SAL_WARN("vcl.qt",
185 "QtAccessibleWidget::indexOfChild: Child index exceeds maximum int value, "
186 "returning -2.");
187 nChildIndex = -2;
189 return nChildIndex;
192 namespace
194 sal_Int16 lcl_matchQtTextBoundaryType(QAccessible::TextBoundaryType boundaryType)
196 switch (boundaryType)
198 case QAccessible::CharBoundary:
199 return com::sun::star::accessibility::AccessibleTextType::CHARACTER;
200 case QAccessible::WordBoundary:
201 return com::sun::star::accessibility::AccessibleTextType::WORD;
202 case QAccessible::SentenceBoundary:
203 return com::sun::star::accessibility::AccessibleTextType::SENTENCE;
204 case QAccessible::ParagraphBoundary:
205 return com::sun::star::accessibility::AccessibleTextType::PARAGRAPH;
206 case QAccessible::LineBoundary:
207 return com::sun::star::accessibility::AccessibleTextType::LINE;
208 case QAccessible::NoBoundary:
209 // assert here, better handle it directly at call site
210 assert(false
211 && "No match for QAccessible::NoBoundary, handle it separately at call site.");
212 break;
213 default:
214 break;
217 SAL_WARN("vcl.qt", "Unmatched text boundary type: " << boundaryType);
218 return -1;
221 QAccessible::Relation lcl_matchUnoRelation(short relationType)
223 // Qt semantics is the other way around
224 switch (relationType)
226 #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
227 case AccessibleRelationType::CONTENT_FLOWS_FROM:
228 return QAccessible::FlowsTo;
229 case AccessibleRelationType::CONTENT_FLOWS_TO:
230 return QAccessible::FlowsFrom;
231 #endif
232 case AccessibleRelationType::CONTROLLED_BY:
233 return QAccessible::Controller;
234 case AccessibleRelationType::CONTROLLER_FOR:
235 return QAccessible::Controlled;
236 #if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
237 case AccessibleRelationType::DESCRIBED_BY:
238 return QAccessible::DescriptionFor;
239 #endif
240 case AccessibleRelationType::LABELED_BY:
241 return QAccessible::Label;
242 case AccessibleRelationType::LABEL_FOR:
243 return QAccessible::Labelled;
244 case AccessibleRelationType::INVALID:
245 case AccessibleRelationType::MEMBER_OF:
246 case AccessibleRelationType::SUB_WINDOW_OF:
247 case AccessibleRelationType::NODE_CHILD_OF:
248 default:
249 SAL_WARN("vcl.qt", "Unmatched relation: " << relationType);
250 return {};
254 void lcl_appendRelation(QVector<QPair<QAccessibleInterface*, QAccessible::Relation>>* relations,
255 AccessibleRelation aRelation, QAccessible::Relation match)
257 QAccessible::Relation aQRelation = lcl_matchUnoRelation(aRelation.RelationType);
258 // skip in case there's no Qt relation matching the filter
259 if (!(aQRelation & match))
260 return;
262 sal_uInt32 nTargetCount = aRelation.TargetSet.getLength();
264 for (sal_uInt32 i = 0; i < nTargetCount; i++)
266 Reference<XAccessible> xAccessible(aRelation.TargetSet[i], uno::UNO_QUERY);
267 relations->append(
268 { QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xAccessible)),
269 aQRelation });
274 QVector<QPair<QAccessibleInterface*, QAccessible::Relation>>
275 QtAccessibleWidget::relations(QAccessible::Relation match) const
277 QVector<QPair<QAccessibleInterface*, QAccessible::Relation>> relations;
279 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
280 if (!xAc.is())
281 return relations;
283 Reference<XAccessibleRelationSet> xRelationSet = xAc->getAccessibleRelationSet();
284 if (xRelationSet.is())
286 int count = xRelationSet->getRelationCount();
287 for (int i = 0; i < count; i++)
289 AccessibleRelation aRelation = xRelationSet->getRelation(i);
290 lcl_appendRelation(&relations, aRelation, match);
294 return relations;
297 QAccessibleInterface* QtAccessibleWidget::focusChild() const
299 /* if (m_pWindow->HasChildPathFocus())
300 return QAccessible::queryAccessibleInterface(
301 new QtXAccessible(m_xAccessible->getAccessibleContext()->getAccessibleChild(index))); */
302 return QAccessible::queryAccessibleInterface(object());
305 QRect QtAccessibleWidget::rect() const
307 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
308 if (!xAc.is())
309 return QRect();
311 Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
312 awt::Point aPoint = xAccessibleComponent->getLocationOnScreen();
313 awt::Size aSize = xAccessibleComponent->getSize();
315 return QRect(aPoint.X, aPoint.Y, aSize.Width, aSize.Height);
318 QAccessibleInterface* QtAccessibleWidget::parent() const
320 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
321 if (!xAc.is())
322 return nullptr;
324 if (xAc->getAccessibleParent().is())
325 return QAccessible::queryAccessibleInterface(
326 QtAccessibleRegistry::getQObject(xAc->getAccessibleParent()));
328 // go via the QObject hierarchy; some a11y objects like the application
329 // (at the root of the a11y hierarchy) are handled solely by Qt and have
330 // no LO-internal a11y objects associated with them
331 QObject* pObj = object();
332 if (pObj && pObj->parent())
333 return QAccessible::queryAccessibleInterface(pObj->parent());
335 return nullptr;
338 QAccessibleInterface* QtAccessibleWidget::child(int index) const
340 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
341 if (!xAc.is())
342 return nullptr;
343 return QAccessible::queryAccessibleInterface(
344 QtAccessibleRegistry::getQObject(xAc->getAccessibleChild(index)));
347 QString QtAccessibleWidget::text(QAccessible::Text text) const
349 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
350 if (!xAc.is())
351 return QString();
353 switch (text)
355 case QAccessible::Name:
356 return toQString(xAc->getAccessibleName());
357 case QAccessible::Description:
358 case QAccessible::DebugDescription:
359 return toQString(xAc->getAccessibleDescription());
360 case QAccessible::Value:
361 case QAccessible::Help:
362 case QAccessible::Accelerator:
363 case QAccessible::UserText:
364 default:
365 return QString("Unknown");
368 QAccessible::Role QtAccessibleWidget::role() const
370 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
371 if (!xAc.is())
372 return QAccessible::NoRole;
374 switch (xAc->getAccessibleRole())
376 case AccessibleRole::UNKNOWN:
377 return QAccessible::NoRole;
378 case AccessibleRole::ALERT:
379 return QAccessible::AlertMessage;
380 case AccessibleRole::COLUMN_HEADER:
381 return QAccessible::ColumnHeader;
382 case AccessibleRole::CANVAS:
383 return QAccessible::Canvas;
384 case AccessibleRole::CHECK_BOX:
385 return QAccessible::CheckBox;
386 case AccessibleRole::CHECK_MENU_ITEM:
387 return QAccessible::MenuItem;
388 case AccessibleRole::COLOR_CHOOSER:
389 return QAccessible::ColorChooser;
390 case AccessibleRole::COMBO_BOX:
391 return QAccessible::ComboBox;
392 case AccessibleRole::DATE_EDITOR:
393 return QAccessible::EditableText;
394 case AccessibleRole::DESKTOP_ICON:
395 return QAccessible::Graphic;
396 case AccessibleRole::DESKTOP_PANE:
397 case AccessibleRole::DIRECTORY_PANE:
398 return QAccessible::Pane;
399 case AccessibleRole::DIALOG:
400 return QAccessible::Dialog;
401 case AccessibleRole::DOCUMENT:
402 return QAccessible::Document;
403 case AccessibleRole::EMBEDDED_OBJECT:
404 return QAccessible::UserRole;
405 case AccessibleRole::END_NOTE:
406 return QAccessible::Note;
407 case AccessibleRole::FILE_CHOOSER:
408 return QAccessible::Dialog;
409 case AccessibleRole::FILLER:
410 return QAccessible::Whitespace;
411 case AccessibleRole::FONT_CHOOSER:
412 return QAccessible::UserRole;
413 case AccessibleRole::FOOTER:
414 return QAccessible::Footer;
415 case AccessibleRole::FOOTNOTE:
416 return QAccessible::Note;
417 case AccessibleRole::FRAME: // top-level window with title bar
418 return QAccessible::Window;
419 case AccessibleRole::GLASS_PANE:
420 return QAccessible::UserRole;
421 case AccessibleRole::GRAPHIC:
422 return QAccessible::Graphic;
423 case AccessibleRole::GROUP_BOX:
424 return QAccessible::Grouping;
425 case AccessibleRole::HEADER:
426 return QAccessible::UserRole;
427 case AccessibleRole::HEADING:
428 return QAccessible::Heading;
429 case AccessibleRole::HYPER_LINK:
430 return QAccessible::Link;
431 case AccessibleRole::ICON:
432 return QAccessible::Graphic;
433 case AccessibleRole::INTERNAL_FRAME:
434 return QAccessible::UserRole;
435 case AccessibleRole::LABEL:
436 return QAccessible::StaticText;
437 case AccessibleRole::LAYERED_PANE:
438 return QAccessible::Pane;
439 case AccessibleRole::LIST:
440 return QAccessible::List;
441 case AccessibleRole::LIST_ITEM:
442 return QAccessible::ListItem;
443 case AccessibleRole::MENU:
444 case AccessibleRole::MENU_BAR:
445 return QAccessible::MenuBar;
446 case AccessibleRole::MENU_ITEM:
447 return QAccessible::MenuItem;
448 case AccessibleRole::NOTIFICATION:
449 #if QT_VERSION >= QT_VERSION_CHECK(5, 12, 5)
450 return QAccessible::Notification;
451 #else
452 return QAccessible::StaticText;
453 #endif
454 case AccessibleRole::OPTION_PANE:
455 return QAccessible::Pane;
456 case AccessibleRole::PAGE_TAB:
457 return QAccessible::PageTab;
458 case AccessibleRole::PAGE_TAB_LIST:
459 return QAccessible::PageTabList;
460 case AccessibleRole::PANEL:
461 return QAccessible::Pane;
462 case AccessibleRole::PARAGRAPH:
463 return QAccessible::Paragraph;
464 case AccessibleRole::PASSWORD_TEXT:
465 return QAccessible::EditableText;
466 case AccessibleRole::POPUP_MENU:
467 return QAccessible::PopupMenu;
468 case AccessibleRole::PUSH_BUTTON:
469 return QAccessible::Button;
470 case AccessibleRole::PROGRESS_BAR:
471 return QAccessible::ProgressBar;
472 case AccessibleRole::RADIO_BUTTON:
473 return QAccessible::RadioButton;
474 case AccessibleRole::RADIO_MENU_ITEM:
475 return QAccessible::MenuItem;
476 case AccessibleRole::ROW_HEADER:
477 return QAccessible::RowHeader;
478 case AccessibleRole::ROOT_PANE:
479 return QAccessible::Pane;
480 case AccessibleRole::SCROLL_BAR:
481 return QAccessible::ScrollBar;
482 case AccessibleRole::SCROLL_PANE:
483 return QAccessible::Pane;
484 case AccessibleRole::SHAPE:
485 return QAccessible::Graphic;
486 case AccessibleRole::SEPARATOR:
487 return QAccessible::Separator;
488 case AccessibleRole::SLIDER:
489 return QAccessible::Slider;
490 case AccessibleRole::SPIN_BOX:
491 return QAccessible::SpinBox;
492 case AccessibleRole::SPLIT_PANE:
493 return QAccessible::Pane;
494 case AccessibleRole::STATUS_BAR:
495 return QAccessible::StatusBar;
496 case AccessibleRole::TABLE:
497 return QAccessible::Table;
498 case AccessibleRole::TABLE_CELL:
499 return QAccessible::Cell;
500 case AccessibleRole::TEXT:
501 return QAccessible::EditableText;
502 case AccessibleRole::TEXT_FRAME:
503 return QAccessible::UserRole;
504 case AccessibleRole::TOGGLE_BUTTON:
505 return QAccessible::Button;
506 case AccessibleRole::TOOL_BAR:
507 return QAccessible::ToolBar;
508 case AccessibleRole::TOOL_TIP:
509 return QAccessible::ToolTip;
510 case AccessibleRole::TREE:
511 return QAccessible::Tree;
512 case AccessibleRole::VIEW_PORT:
513 return QAccessible::UserRole;
514 case AccessibleRole::BUTTON_DROPDOWN:
515 return QAccessible::ButtonDropDown;
516 case AccessibleRole::BUTTON_MENU:
517 return QAccessible::ButtonMenu;
518 case AccessibleRole::CAPTION:
519 return QAccessible::StaticText;
520 case AccessibleRole::CHART:
521 return QAccessible::Chart;
522 case AccessibleRole::EDIT_BAR:
523 return QAccessible::Equation;
524 case AccessibleRole::FORM:
525 return QAccessible::Form;
526 case AccessibleRole::IMAGE_MAP:
527 return QAccessible::Graphic;
528 case AccessibleRole::NOTE:
529 return QAccessible::Note;
530 case AccessibleRole::RULER:
531 return QAccessible::UserRole;
532 case AccessibleRole::SECTION:
533 return QAccessible::Section;
534 case AccessibleRole::TREE_ITEM:
535 return QAccessible::TreeItem;
536 case AccessibleRole::TREE_TABLE:
537 return QAccessible::Tree;
538 case AccessibleRole::COMMENT:
539 return QAccessible::Note;
540 case AccessibleRole::COMMENT_END:
541 return QAccessible::UserRole;
542 case AccessibleRole::DOCUMENT_PRESENTATION:
543 return QAccessible::Document;
544 case AccessibleRole::DOCUMENT_SPREADSHEET:
545 return QAccessible::Document;
546 case AccessibleRole::DOCUMENT_TEXT:
547 return QAccessible::Document;
548 case AccessibleRole::STATIC:
549 return QAccessible::StaticText;
550 case AccessibleRole::WINDOW: // top-level window without title bar
551 return QAccessible::Window;
554 SAL_WARN("vcl.qt", "Unmapped role: " << getAccessibleContextImpl()->getAccessibleRole());
555 return QAccessible::NoRole;
558 namespace
560 void lcl_addState(QAccessible::State* state, sal_Int64 nState)
562 switch (nState)
564 case AccessibleStateType::INVALID:
565 state->invalid = true;
566 break;
567 case AccessibleStateType::ACTIVE:
568 state->active = true;
569 break;
570 case AccessibleStateType::ARMED:
571 // No match
572 break;
573 case AccessibleStateType::BUSY:
574 state->busy = true;
575 break;
576 case AccessibleStateType::CHECKED:
577 state->checked = true;
578 break;
579 case AccessibleStateType::EDITABLE:
580 state->editable = true;
581 break;
582 case AccessibleStateType::ENABLED:
583 state->disabled = false;
584 break;
585 case AccessibleStateType::EXPANDABLE:
586 state->expandable = true;
587 break;
588 case AccessibleStateType::EXPANDED:
589 state->expanded = true;
590 break;
591 case AccessibleStateType::FOCUSABLE:
592 state->focusable = true;
593 break;
594 case AccessibleStateType::FOCUSED:
595 state->focused = true;
596 break;
597 case AccessibleStateType::HORIZONTAL:
598 // No match
599 break;
600 case AccessibleStateType::ICONIFIED:
601 // No match
602 break;
603 case AccessibleStateType::INDETERMINATE:
604 // No match
605 break;
606 case AccessibleStateType::MANAGES_DESCENDANTS:
607 // No match
608 break;
609 case AccessibleStateType::MODAL:
610 state->modal = true;
611 break;
612 case AccessibleStateType::MOVEABLE:
613 state->movable = true;
614 break;
615 case AccessibleStateType::MULTI_LINE:
616 state->multiLine = true;
617 break;
618 case AccessibleStateType::OPAQUE:
619 // No match
620 break;
621 case AccessibleStateType::PRESSED:
622 state->pressed = true;
623 break;
624 case AccessibleStateType::RESIZABLE:
625 state->sizeable = true;
626 break;
627 case AccessibleStateType::SELECTABLE:
628 state->selectable = true;
629 break;
630 case AccessibleStateType::SELECTED:
631 state->selected = true;
632 break;
633 case AccessibleStateType::SENSITIVE:
634 // No match
635 break;
636 case AccessibleStateType::SHOWING:
637 // No match
638 break;
639 case AccessibleStateType::SINGLE_LINE:
640 // No match
641 break;
642 case AccessibleStateType::STALE:
643 // No match
644 break;
645 case AccessibleStateType::TRANSIENT:
646 // No match
647 break;
648 case AccessibleStateType::VERTICAL:
649 // No match
650 break;
651 case AccessibleStateType::VISIBLE:
652 state->invisible = false;
653 break;
654 case AccessibleStateType::DEFAULT:
655 // No match
656 break;
657 case AccessibleStateType::DEFUNC:
658 state->invalid = true;
659 break;
660 case AccessibleStateType::MULTI_SELECTABLE:
661 state->multiSelectable = true;
662 break;
663 default:
664 SAL_WARN("vcl.qt", "Unmapped state: " << nState);
665 break;
670 QAccessible::State QtAccessibleWidget::state() const
672 QAccessible::State state;
674 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
675 if (!xAc.is())
676 return state;
678 sal_Int64 nStateSet(xAc->getAccessibleStateSet());
680 for (int i = 0; i < 63; ++i)
682 sal_Int64 nState = sal_Int64(1) << i;
683 if (nStateSet & nState)
684 lcl_addState(&state, nState);
687 return state;
690 QColor QtAccessibleWidget::foregroundColor() const
692 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
693 if (!xAc.is())
694 return QColor();
696 Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
697 return toQColor(Color(ColorTransparency, xAccessibleComponent->getForeground()));
700 QColor QtAccessibleWidget::backgroundColor() const
702 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
703 if (!xAc.is())
704 return QColor();
706 Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
707 return toQColor(Color(ColorTransparency, xAccessibleComponent->getBackground()));
710 void* QtAccessibleWidget::interface_cast(QAccessible::InterfaceType t)
712 if (t == QAccessible::ActionInterface && accessibleProvidesInterface<XAccessibleAction>())
713 return static_cast<QAccessibleActionInterface*>(this);
714 if (t == QAccessible::TextInterface && accessibleProvidesInterface<XAccessibleText>())
715 return static_cast<QAccessibleTextInterface*>(this);
716 if (t == QAccessible::EditableTextInterface
717 && accessibleProvidesInterface<XAccessibleEditableText>())
718 return static_cast<QAccessibleEditableTextInterface*>(this);
719 if (t == QAccessible::ValueInterface && accessibleProvidesInterface<XAccessibleValue>())
720 return static_cast<QAccessibleValueInterface*>(this);
721 if (t == QAccessible::TableCellInterface)
723 // parent must be a table
724 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
725 if (xTable.is())
726 return static_cast<QAccessibleTableCellInterface*>(this);
728 if (t == QAccessible::TableInterface && accessibleProvidesInterface<XAccessibleTable>())
729 return static_cast<QAccessibleTableInterface*>(this);
730 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
731 if (t == QAccessible::SelectionInterface && accessibleProvidesInterface<XAccessibleSelection>())
732 return static_cast<QAccessibleSelectionInterface*>(this);
733 #endif
734 return nullptr;
737 bool QtAccessibleWidget::isValid() const
739 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
740 return xAc.is();
743 QObject* QtAccessibleWidget::object() const { return m_pObject; }
745 void QtAccessibleWidget::setText(QAccessible::Text /* t */, const QString& /* text */) {}
747 QAccessibleInterface* QtAccessibleWidget::childAt(int x, int y) const
749 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
750 if (!xAc.is())
751 return nullptr;
753 Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
754 // convert from screen to local coordinates
755 QPoint aLocalCoords = QPoint(x, y) - rect().topLeft();
756 return QAccessible::queryAccessibleInterface(
757 QtAccessibleRegistry::getQObject(xAccessibleComponent->getAccessibleAtPoint(
758 awt::Point(aLocalCoords.x(), aLocalCoords.y()))));
761 QAccessibleInterface* QtAccessibleWidget::customFactory(const QString& classname, QObject* object)
763 if (classname == QLatin1String("QtWidget") && object && object->isWidgetType())
765 QtWidget* pWidget = static_cast<QtWidget*>(object);
766 vcl::Window* pWindow = pWidget->frame().GetWindow();
768 if (pWindow)
770 css::uno::Reference<XAccessible> xAcc = pWindow->GetAccessible();
771 // insert into registry so the association between the XAccessible and the QtWidget
772 // is remembered rather than creating a different QtXAccessible when a QObject is needed later
773 QtAccessibleRegistry::insert(xAcc, object);
774 return new QtAccessibleWidget(xAcc, object);
777 if (classname == QLatin1String("QtXAccessible") && object)
779 QtXAccessible* pXAccessible = static_cast<QtXAccessible*>(object);
780 if (pXAccessible->m_xAccessible.is())
782 QtAccessibleWidget* pRet = new QtAccessibleWidget(pXAccessible->m_xAccessible, object);
783 // clear the reference in the QtXAccessible, no longer needed now that the QtAccessibleWidget holds one
784 pXAccessible->m_xAccessible.clear();
785 return pRet;
789 return nullptr;
792 // QAccessibleActionInterface
793 QStringList QtAccessibleWidget::actionNames() const
795 QStringList actionNames;
796 Reference<XAccessibleAction> xAccessibleAction(getAccessibleContextImpl(), UNO_QUERY);
797 if (!xAccessibleAction.is())
798 return actionNames;
800 int count = xAccessibleAction->getAccessibleActionCount();
801 for (int i = 0; i < count; i++)
803 OUString desc = xAccessibleAction->getAccessibleActionDescription(i);
804 actionNames.append(toQString(desc));
806 return actionNames;
809 void QtAccessibleWidget::doAction(const QString& actionName)
811 Reference<XAccessibleAction> xAccessibleAction(getAccessibleContextImpl(), UNO_QUERY);
812 if (!xAccessibleAction.is())
813 return;
815 int index = actionNames().indexOf(actionName);
816 if (index == -1)
817 return;
818 xAccessibleAction->doAccessibleAction(index);
821 QStringList QtAccessibleWidget::keyBindingsForAction(const QString& actionName) const
823 QStringList keyBindings;
824 Reference<XAccessibleAction> xAccessibleAction(getAccessibleContextImpl(), UNO_QUERY);
825 if (!xAccessibleAction.is())
826 return keyBindings;
828 int index = actionNames().indexOf(actionName);
829 if (index == -1)
830 return keyBindings;
832 Reference<XAccessibleKeyBinding> xKeyBinding
833 = xAccessibleAction->getAccessibleActionKeyBinding(index);
835 if (!xKeyBinding.is())
836 return keyBindings;
838 int count = xKeyBinding->getAccessibleKeyBindingCount();
839 for (int i = 0; i < count; i++)
841 Sequence<awt::KeyStroke> keyStroke = xKeyBinding->getAccessibleKeyBinding(i);
842 keyBindings.append(toQString(comphelper::GetkeyBindingStrByXkeyBinding(keyStroke)));
844 return keyBindings;
847 // QAccessibleTextInterface
848 void QtAccessibleWidget::addSelection(int /* startOffset */, int /* endOffset */)
850 SAL_INFO("vcl.qt", "Unsupported QAccessibleTextInterface::addSelection");
853 namespace
855 OUString lcl_convertFontWeight(double fontWeight)
857 if (fontWeight == awt::FontWeight::THIN || fontWeight == awt::FontWeight::ULTRALIGHT)
858 return "100";
859 if (fontWeight == awt::FontWeight::LIGHT)
860 return "200";
861 if (fontWeight == awt::FontWeight::SEMILIGHT)
862 return "300";
863 if (fontWeight == awt::FontWeight::NORMAL)
864 return "normal";
865 if (fontWeight == awt::FontWeight::SEMIBOLD)
866 return "500";
867 if (fontWeight == awt::FontWeight::BOLD)
868 return "bold";
869 if (fontWeight == awt::FontWeight::ULTRABOLD)
870 return "800";
871 if (fontWeight == awt::FontWeight::BLACK)
872 return "900";
874 // awt::FontWeight::DONTKNOW || fontWeight == awt::FontWeight::NORMAL
875 return "normal";
878 OUString lcl_ConvertFontSlant(awt::FontSlant eFontSlant)
880 switch (eFontSlant)
882 case awt::FontSlant::FontSlant_NONE:
883 return "normal";
884 case awt::FontSlant::FontSlant_OBLIQUE:
885 case awt::FontSlant::FontSlant_REVERSE_OBLIQUE:
886 return "oblique";
887 case awt::FontSlant::FontSlant_ITALIC:
888 case awt::FontSlant::FontSlant_REVERSE_ITALIC:
889 return "italic";
890 case awt::FontSlant::FontSlant_DONTKNOW:
891 case awt::FontSlant::FontSlant_MAKE_FIXED_SIZE:
892 default:
893 return "";
897 // s. https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes
898 // for values
899 void lcl_ConvertFontUnderline(sal_Int16 nFontUnderline, OUString& rUnderlineStyle,
900 OUString& rUnderlineType, OUString& rUnderlineWidth)
902 rUnderlineStyle = u"";
903 rUnderlineType = u"single";
904 rUnderlineWidth = u"auto";
906 switch (nFontUnderline)
908 case awt::FontUnderline::BOLD:
909 rUnderlineWidth = u"bold";
910 return;
911 case awt::FontUnderline::BOLDDASH:
912 rUnderlineWidth = u"bold";
913 rUnderlineStyle = u"dash";
914 return;
915 case awt::FontUnderline::BOLDDASHDOT:
916 rUnderlineWidth = u"bold";
917 rUnderlineStyle = u"dot-dash";
918 return;
919 case awt::FontUnderline::BOLDDASHDOTDOT:
920 rUnderlineWidth = u"bold";
921 rUnderlineStyle = u"dot-dot-dash";
922 return;
923 case awt::FontUnderline::BOLDDOTTED:
924 rUnderlineWidth = u"bold";
925 rUnderlineStyle = u"dotted";
926 return;
927 case awt::FontUnderline::BOLDLONGDASH:
928 rUnderlineWidth = u"bold";
929 rUnderlineStyle = u"long-dash";
930 return;
931 case awt::FontUnderline::BOLDWAVE:
932 rUnderlineWidth = u"bold";
933 rUnderlineStyle = u"wave";
934 return;
935 case awt::FontUnderline::DASH:
936 rUnderlineStyle = u"dash";
937 return;
938 case awt::FontUnderline::DASHDOT:
939 rUnderlineStyle = u"dot-dash";
940 return;
941 case awt::FontUnderline::DASHDOTDOT:
942 rUnderlineStyle = u"dot-dot-dash";
943 return;
944 case awt::FontUnderline::DONTKNOW:
945 rUnderlineWidth = u"";
946 rUnderlineStyle = u"";
947 rUnderlineType = u"";
948 return;
949 case awt::FontUnderline::DOTTED:
950 rUnderlineStyle = u"dotted";
951 return;
952 case awt::FontUnderline::DOUBLE:
953 rUnderlineType = u"double";
954 return;
955 case awt::FontUnderline::DOUBLEWAVE:
956 rUnderlineStyle = u"wave";
957 rUnderlineType = u"double";
958 return;
959 case awt::FontUnderline::LONGDASH:
960 rUnderlineStyle = u"long-dash";
961 return;
962 case awt::FontUnderline::NONE:
963 rUnderlineWidth = u"none";
964 rUnderlineStyle = u"none";
965 rUnderlineType = u"none";
966 return;
967 case awt::FontUnderline::SINGLE:
968 rUnderlineType = u"single";
969 return;
970 case awt::FontUnderline::SMALLWAVE:
971 case awt::FontUnderline::WAVE:
972 rUnderlineStyle = u"wave";
973 return;
974 default:
975 assert(false && "Unhandled font underline type");
979 /** Converts Color to "rgb(r,g,b)" as specified in https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes. */
980 OUString lcl_ConvertColor(Color aColor)
982 return u"rgb(" + OUString::number(aColor.GetRed()) + u"," + OUString::number(aColor.GetGreen())
983 + u"," + OUString::number(aColor.GetBlue()) + u")";
987 // Text attributes are returned in format specified in IAccessible2 spec, since that
988 // is what Qt handles:
989 // https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes
990 QString QtAccessibleWidget::attributes(int offset, int* startOffset, int* endOffset) const
992 if (startOffset == nullptr || endOffset == nullptr)
993 return QString();
995 *startOffset = -1;
996 *endOffset = -1;
998 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
999 if (!xText.is())
1000 return QString();
1002 // handle special values for offset the same way base class's QAccessibleTextWidget::attributes does
1003 // (as defined in IAccessible 2: -1 -> length, -2 -> cursor position)
1004 if (offset == -2)
1005 offset = cursorPosition();
1007 const int nTextLength = characterCount();
1008 if (offset == -1 || offset == nTextLength)
1009 offset = nTextLength - 1;
1011 if (offset < 0 || offset > nTextLength)
1013 SAL_WARN("vcl.qt", "QtAccessibleWidget::attributes called with invalid offset: " << offset);
1014 return QString();
1017 const Sequence<PropertyValue> attribs
1018 = xText->getCharacterAttributes(offset, Sequence<OUString>());
1019 OUString aRet;
1020 for (PropertyValue const& prop : attribs)
1022 OUString sAttribute;
1023 OUString sValue;
1024 if (prop.Name == "CharBackColor")
1026 sAttribute = "background-color";
1027 sValue = lcl_ConvertColor(
1028 Color(ColorTransparency, *o3tl::doAccess<sal_Int32>(prop.Value)));
1030 else if (prop.Name == "CharColor")
1032 sAttribute = "color";
1033 sValue = lcl_ConvertColor(
1034 Color(ColorTransparency, *o3tl::doAccess<sal_Int32>(prop.Value)));
1036 else if (prop.Name == "CharFontName")
1038 sAttribute = "font-family";
1039 sValue = *o3tl::doAccess<OUString>(prop.Value);
1041 else if (prop.Name == "CharHeight")
1043 sAttribute = "font-size";
1044 sValue = OUString::number(*o3tl::doAccess<double>(prop.Value)) + "pt";
1046 else if (prop.Name == "CharPosture")
1048 sAttribute = "font-style";
1049 const awt::FontSlant eFontSlant = *o3tl::doAccess<awt::FontSlant>(prop.Value);
1050 sValue = lcl_ConvertFontSlant(eFontSlant);
1052 else if (prop.Name == "CharUnderline")
1054 OUString sUnderlineStyle;
1055 OUString sUnderlineType;
1056 OUString sUnderlineWidth;
1057 const sal_Int16 nUnderline = *o3tl::doAccess<sal_Int16>(prop.Value);
1058 lcl_ConvertFontUnderline(nUnderline, sUnderlineStyle, sUnderlineType, sUnderlineWidth);
1060 // leave 'sAttribute' and 'sName' empty, set all attributes here
1061 if (!sUnderlineStyle.isEmpty())
1062 aRet += u"text-underline-style:" + sUnderlineStyle + ";";
1063 if (!sUnderlineType.isEmpty())
1064 aRet += u"text-underline-type:" + sUnderlineType + ";";
1065 if (!sUnderlineWidth.isEmpty())
1066 aRet += u"text-underline-width:" + sUnderlineWidth + ";";
1068 else if (prop.Name == "CharWeight")
1070 sAttribute = "font-weight";
1071 sValue = lcl_convertFontWeight(*o3tl::doAccess<double>(prop.Value));
1074 if (!sAttribute.isEmpty() && !sValue.isEmpty())
1075 aRet += sAttribute + ":" + sValue + ";";
1078 accessibility::TextSegment aAttributeRun
1079 = xText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN);
1080 *startOffset = aAttributeRun.SegmentStart;
1081 *endOffset = aAttributeRun.SegmentEnd;
1082 return toQString(aRet);
1085 int QtAccessibleWidget::characterCount() const
1087 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1088 if (xText.is())
1089 return xText->getCharacterCount();
1090 return 0;
1093 QRect QtAccessibleWidget::characterRect(int nOffset) const
1095 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1096 if (!xText.is())
1097 return QRect();
1099 if (nOffset < 0 || nOffset > xText->getCharacterCount())
1101 SAL_WARN("vcl.qt",
1102 "QtAccessibleWidget::characterRect called with invalid offset: " << nOffset);
1103 return QRect();
1106 const awt::Rectangle aBounds = xText->getCharacterBounds(nOffset);
1107 const QRect aRect(aBounds.X, aBounds.Y, aBounds.Width, aBounds.Height);
1108 // convert to screen coordinates
1109 const QRect aScreenPos = rect();
1110 return aRect.translated(aScreenPos.x(), aScreenPos.y());
1113 int QtAccessibleWidget::cursorPosition() const
1115 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1116 if (xText.is())
1117 return xText->getCaretPosition();
1118 return 0;
1121 int QtAccessibleWidget::offsetAtPoint(const QPoint& rPoint) const
1123 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1124 if (!xText.is())
1125 return -1;
1127 // convert from screen to local coordinates
1128 QPoint aLocalCoords = rPoint - rect().topLeft();
1129 awt::Point aPoint(aLocalCoords.x(), aLocalCoords.y());
1130 return xText->getIndexAtPoint(aPoint);
1133 void QtAccessibleWidget::removeSelection(int /* selectionIndex */)
1135 SAL_INFO("vcl.qt", "Unsupported QAccessibleTextInterface::removeSelection");
1138 void QtAccessibleWidget::scrollToSubstring(int startIndex, int endIndex)
1140 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1141 if (!xText.is())
1142 return;
1144 sal_Int32 nTextLength = xText->getCharacterCount();
1145 if (startIndex < 0 || startIndex > nTextLength || endIndex < 0 || endIndex > nTextLength)
1147 SAL_WARN("vcl.qt", "QtAccessibleWidget::scrollToSubstring called with invalid offset.");
1148 return;
1151 xText->scrollSubstringTo(startIndex, endIndex, AccessibleScrollType_SCROLL_ANYWHERE);
1154 void QtAccessibleWidget::selection(int selectionIndex, int* startOffset, int* endOffset) const
1156 if (!startOffset && !endOffset)
1157 return;
1159 Reference<XAccessibleText> xText;
1160 if (selectionIndex == 0)
1161 xText = Reference<XAccessibleText>(getAccessibleContextImpl(), UNO_QUERY);
1163 if (startOffset)
1164 *startOffset = xText.is() ? xText->getSelectionStart() : 0;
1165 if (endOffset)
1166 *endOffset = xText.is() ? xText->getSelectionEnd() : 0;
1169 int QtAccessibleWidget::selectionCount() const
1171 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1172 if (xText.is() && !xText->getSelectedText().isEmpty())
1173 return 1; // Only 1 selection supported atm
1174 return 0;
1177 void QtAccessibleWidget::setCursorPosition(int position)
1179 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1180 if (!xText.is())
1181 return;
1183 if (position < 0 || position > xText->getCharacterCount())
1185 SAL_WARN("vcl.qt",
1186 "QtAccessibleWidget::setCursorPosition called with invalid offset: " << position);
1187 return;
1190 xText->setCaretPosition(position);
1193 void QtAccessibleWidget::setSelection(int /* selectionIndex */, int startOffset, int endOffset)
1195 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1196 if (!xText.is())
1197 return;
1199 sal_Int32 nTextLength = xText->getCharacterCount();
1200 if (startOffset < 0 || startOffset > nTextLength || endOffset < 0 || endOffset > nTextLength)
1202 SAL_WARN("vcl.qt", "QtAccessibleWidget::setSelection called with invalid offset.");
1203 return;
1206 xText->setSelection(startOffset, endOffset);
1209 QString QtAccessibleWidget::text(int startOffset, int endOffset) const
1211 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1212 if (!xText.is())
1213 return QString();
1215 sal_Int32 nTextLength = xText->getCharacterCount();
1216 if (startOffset < 0 || startOffset > nTextLength || endOffset < 0 || endOffset > nTextLength)
1218 SAL_WARN("vcl.qt", "QtAccessibleWidget::text called with invalid offset.");
1219 return QString();
1222 return toQString(xText->getTextRange(startOffset, endOffset));
1225 QString QtAccessibleWidget::textAfterOffset(int nOffset,
1226 QAccessible::TextBoundaryType eBoundaryType,
1227 int* pStartOffset, int* pEndOffset) const
1229 if (pStartOffset == nullptr || pEndOffset == nullptr)
1230 return QString();
1232 *pStartOffset = -1;
1233 *pEndOffset = -1;
1235 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1236 if (!xText.is())
1237 return QString();
1239 const int nCharCount = characterCount();
1240 // -1 is special value for text length
1241 if (nOffset == -1)
1242 nOffset = nCharCount;
1243 else if (nOffset < -1 || nOffset > nCharCount)
1245 SAL_WARN("vcl.qt",
1246 "QtAccessibleWidget::textAfterOffset called with invalid offset: " << nOffset);
1247 return QString();
1250 if (eBoundaryType == QAccessible::NoBoundary)
1252 if (nOffset == nCharCount)
1253 return QString();
1254 *pStartOffset = nOffset + 1;
1255 *pEndOffset = nCharCount;
1256 return text(nOffset + 1, nCharCount);
1259 sal_Int16 nUnoBoundaryType = lcl_matchQtTextBoundaryType(eBoundaryType);
1260 assert(nUnoBoundaryType > 0);
1261 const TextSegment aSegment = xText->getTextBehindIndex(nOffset, nUnoBoundaryType);
1262 *pStartOffset = aSegment.SegmentStart;
1263 *pEndOffset = aSegment.SegmentEnd;
1264 return toQString(aSegment.SegmentText);
1267 QString QtAccessibleWidget::textAtOffset(int offset, QAccessible::TextBoundaryType boundaryType,
1268 int* startOffset, int* endOffset) const
1270 if (startOffset == nullptr || endOffset == nullptr)
1271 return QString();
1273 const int nCharCount = characterCount();
1274 if (boundaryType == QAccessible::NoBoundary)
1276 *startOffset = 0;
1277 *endOffset = nCharCount;
1278 return text(0, nCharCount);
1281 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1282 if (!xText.is())
1283 return QString();
1285 sal_Int16 nUnoBoundaryType = lcl_matchQtTextBoundaryType(boundaryType);
1286 assert(nUnoBoundaryType > 0);
1288 // special value of -1 for offset means text length
1289 if (offset == -1)
1290 offset = nCharCount;
1292 if (offset < 0 || offset > nCharCount)
1294 SAL_WARN("vcl.qt",
1295 "QtAccessibleWidget::textAtOffset called with invalid offset: " << offset);
1296 return QString();
1299 const TextSegment segment = xText->getTextAtIndex(offset, nUnoBoundaryType);
1300 *startOffset = segment.SegmentStart;
1301 *endOffset = segment.SegmentEnd;
1302 return toQString(segment.SegmentText);
1305 QString QtAccessibleWidget::textBeforeOffset(int nOffset,
1306 QAccessible::TextBoundaryType eBoundaryType,
1307 int* pStartOffset, int* pEndOffset) const
1309 if (pStartOffset == nullptr || pEndOffset == nullptr)
1310 return QString();
1312 *pStartOffset = -1;
1313 *pEndOffset = -1;
1315 Reference<XAccessibleText> xText(getAccessibleContextImpl(), UNO_QUERY);
1316 if (!xText.is())
1317 return QString();
1319 const int nCharCount = characterCount();
1320 // -1 is special value for text length
1321 if (nOffset == -1)
1322 nOffset = nCharCount;
1323 else if (nOffset < -1 || nOffset > nCharCount)
1325 SAL_WARN("vcl.qt",
1326 "QtAccessibleWidget::textBeforeOffset called with invalid offset: " << nOffset);
1327 return QString();
1330 if (eBoundaryType == QAccessible::NoBoundary)
1332 *pStartOffset = 0;
1333 *pEndOffset = nOffset;
1334 return text(0, nOffset);
1337 sal_Int16 nUnoBoundaryType = lcl_matchQtTextBoundaryType(eBoundaryType);
1338 assert(nUnoBoundaryType > 0);
1339 const TextSegment aSegment = xText->getTextBeforeIndex(nOffset, nUnoBoundaryType);
1340 *pStartOffset = aSegment.SegmentStart;
1341 *pEndOffset = aSegment.SegmentEnd;
1342 return toQString(aSegment.SegmentText);
1345 // QAccessibleEditableTextInterface
1347 void QtAccessibleWidget::deleteText(int startOffset, int endOffset)
1349 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1350 if (!xAc.is())
1351 return;
1353 Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY);
1354 if (!xEditableText.is())
1355 return;
1357 sal_Int32 nTextLength = xEditableText->getCharacterCount();
1358 if (startOffset < 0 || startOffset > nTextLength || endOffset < 0 || endOffset > nTextLength)
1360 SAL_WARN("vcl.qt", "QtAccessibleWidget::deleteText called with invalid offset.");
1361 return;
1364 xEditableText->deleteText(startOffset, endOffset);
1367 void QtAccessibleWidget::insertText(int offset, const QString& text)
1369 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1370 if (!xAc.is())
1371 return;
1373 Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY);
1374 if (!xEditableText.is())
1375 return;
1377 if (offset < 0 || offset > xEditableText->getCharacterCount())
1379 SAL_WARN("vcl.qt", "QtAccessibleWidget::insertText called with invalid offset: " << offset);
1380 return;
1383 xEditableText->insertText(toOUString(text), offset);
1386 void QtAccessibleWidget::replaceText(int startOffset, int endOffset, const QString& text)
1388 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1389 if (!xAc.is())
1390 return;
1392 Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY);
1393 if (!xEditableText.is())
1394 return;
1396 sal_Int32 nTextLength = xEditableText->getCharacterCount();
1397 if (startOffset < 0 || startOffset > nTextLength || endOffset < 0 || endOffset > nTextLength)
1399 SAL_WARN("vcl.qt", "QtAccessibleWidget::replaceText called with invalid offset.");
1400 return;
1403 xEditableText->replaceText(startOffset, endOffset, toOUString(text));
1406 // QAccessibleValueInterface
1407 QVariant QtAccessibleWidget::currentValue() const
1409 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1410 if (!xAc.is())
1411 return QVariant();
1413 Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1414 if (!xValue.is())
1415 return QVariant();
1416 double aDouble = 0;
1417 xValue->getCurrentValue() >>= aDouble;
1418 return QVariant(aDouble);
1421 QVariant QtAccessibleWidget::maximumValue() const
1423 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1424 if (!xAc.is())
1425 return QVariant();
1427 Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1428 if (!xValue.is())
1429 return QVariant();
1430 double aDouble = 0;
1431 xValue->getMaximumValue() >>= aDouble;
1432 return QVariant(aDouble);
1435 QVariant QtAccessibleWidget::minimumStepSize() const
1437 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1438 if (!xAc.is())
1439 return QVariant();
1441 Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1442 if (!xValue.is())
1443 return QVariant();
1444 double dMinStep = 0;
1445 xValue->getMinimumIncrement() >>= dMinStep;
1446 return QVariant(dMinStep);
1449 QVariant QtAccessibleWidget::minimumValue() const
1451 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1452 if (!xAc.is())
1453 return QVariant();
1455 Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1456 if (!xValue.is())
1457 return QVariant();
1458 double aDouble = 0;
1459 xValue->getMinimumValue() >>= aDouble;
1460 return QVariant(aDouble);
1463 void QtAccessibleWidget::setCurrentValue(const QVariant& value)
1465 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1466 if (!xAc.is())
1467 return;
1469 Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1470 if (!xValue.is())
1471 return;
1472 xValue->setCurrentValue(Any(value.toDouble()));
1475 // QAccessibleTableInterface
1476 QAccessibleInterface* QtAccessibleWidget::caption() const
1478 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1479 if (!xAc.is())
1480 return nullptr;
1482 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1483 if (!xTable.is())
1484 return nullptr;
1485 return QAccessible::queryAccessibleInterface(
1486 QtAccessibleRegistry::getQObject(xTable->getAccessibleCaption()));
1489 QAccessibleInterface* QtAccessibleWidget::cellAt(int row, int column) const
1491 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1492 if (!xAc.is())
1493 return nullptr;
1495 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1496 if (!xTable.is())
1497 return nullptr;
1499 if (row < 0 || row >= xTable->getAccessibleRowCount() || column < 0
1500 || column >= xTable->getAccessibleColumnCount())
1502 SAL_WARN("vcl.qt", "QtAccessibleWidget::cellAt called with invalid row/column index ("
1503 << row << ", " << column << ")");
1504 return nullptr;
1507 return QAccessible::queryAccessibleInterface(
1508 QtAccessibleRegistry::getQObject(xTable->getAccessibleCellAt(row, column)));
1511 int QtAccessibleWidget::columnCount() const
1513 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1514 if (!xAc.is())
1515 return 0;
1517 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1518 if (!xTable.is())
1519 return 0;
1520 return xTable->getAccessibleColumnCount();
1523 QString QtAccessibleWidget::columnDescription(int column) const
1525 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1526 if (!xAc.is())
1527 return QString();
1529 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1530 if (!xTable.is())
1531 return QString();
1532 return toQString(xTable->getAccessibleColumnDescription(column));
1535 bool QtAccessibleWidget::isColumnSelected(int nColumn) const
1537 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1538 if (!xAc.is())
1539 return false;
1541 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1542 if (!xTable.is())
1543 return false;
1545 if (nColumn < 0 || nColumn >= xTable->getAccessibleColumnCount())
1547 SAL_WARN("vcl.qt", "QtAccessibleWidget::isColumnSelected called with invalid column index "
1548 << nColumn);
1549 return false;
1552 return xTable->isAccessibleColumnSelected(nColumn);
1555 bool QtAccessibleWidget::isRowSelected(int nRow) const
1557 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1558 if (!xAc.is())
1559 return false;
1561 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1562 if (!xTable.is())
1563 return false;
1565 if (nRow < 0 || nRow >= xTable->getAccessibleRowCount())
1567 SAL_WARN("vcl.qt",
1568 "QtAccessibleWidget::isRowSelected called with invalid row index " << nRow);
1569 return false;
1572 return xTable->isAccessibleRowSelected(nRow);
1575 void QtAccessibleWidget::modelChange(QAccessibleTableModelChangeEvent*) {}
1577 int QtAccessibleWidget::rowCount() const
1579 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1580 if (!xAc.is())
1581 return 0;
1583 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1584 if (!xTable.is())
1585 return 0;
1586 return xTable->getAccessibleRowCount();
1589 QString QtAccessibleWidget::rowDescription(int row) const
1591 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1592 if (!xAc.is())
1593 return QString();
1595 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1596 if (!xTable.is())
1597 return QString();
1598 return toQString(xTable->getAccessibleRowDescription(row));
1601 bool QtAccessibleWidget::selectColumn(int column)
1603 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1604 if (!xAc.is())
1605 return false;
1607 if (column < 0 || column >= columnCount())
1609 SAL_WARN("vcl.qt",
1610 "QtAccessibleWidget::selectColumn called with invalid column index " << column);
1611 return false;
1614 Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
1615 if (!xTableSelection.is())
1616 return false;
1617 return xTableSelection->selectColumn(column);
1620 bool QtAccessibleWidget::selectRow(int row)
1622 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1623 if (!xAc.is())
1624 return false;
1626 if (row < 0 || row >= rowCount())
1628 SAL_WARN("vcl.qt", "QtAccessibleWidget::selectRow called with invalid row index " << row);
1629 return false;
1632 Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
1633 if (!xTableSelection.is())
1634 return false;
1635 return xTableSelection->selectRow(row);
1638 int QtAccessibleWidget::selectedCellCount() const { return selectedItemCount(); }
1640 QList<QAccessibleInterface*> QtAccessibleWidget::selectedCells() const { return selectedItems(); }
1642 int QtAccessibleWidget::selectedColumnCount() const
1644 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1645 if (!xAc.is())
1646 return 0;
1648 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1649 if (!xTable.is())
1650 return 0;
1651 return xTable->getSelectedAccessibleColumns().getLength();
1654 QList<int> QtAccessibleWidget::selectedColumns() const
1656 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1657 if (!xAc.is())
1658 return QList<int>();
1660 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1661 if (!xTable.is())
1662 return QList<int>();
1663 return toQList(xTable->getSelectedAccessibleColumns());
1666 int QtAccessibleWidget::selectedRowCount() const
1668 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1669 if (!xAc.is())
1670 return 0;
1672 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1673 if (!xTable.is())
1674 return 0;
1675 return xTable->getSelectedAccessibleRows().getLength();
1678 QList<int> QtAccessibleWidget::selectedRows() const
1680 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1681 if (!xAc.is())
1682 return QList<int>();
1684 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1685 if (!xTable.is())
1686 return QList<int>();
1687 return toQList(xTable->getSelectedAccessibleRows());
1690 QAccessibleInterface* QtAccessibleWidget::summary() const
1692 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1693 if (!xAc.is())
1694 return nullptr;
1696 Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1697 if (!xTable.is())
1698 return nullptr;
1699 return QAccessible::queryAccessibleInterface(
1700 QtAccessibleRegistry::getQObject(xTable->getAccessibleSummary()));
1703 bool QtAccessibleWidget::unselectColumn(int column)
1705 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1706 if (!xAc.is())
1707 return false;
1709 Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
1710 if (!xTableSelection.is())
1711 return false;
1712 return xTableSelection->unselectColumn(column);
1715 bool QtAccessibleWidget::unselectRow(int row)
1717 Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1718 if (!xAc.is())
1719 return false;
1721 Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
1722 if (!xTableSelection.is())
1723 return false;
1724 return xTableSelection->unselectRow(row);
1727 // QAccessibleTableCellInterface
1728 QList<QAccessibleInterface*> QtAccessibleWidget::columnHeaderCells() const
1730 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
1731 if (!xTable.is())
1732 return QList<QAccessibleInterface*>();
1734 Reference<XAccessibleTable> xHeaders = xTable->getAccessibleColumnHeaders();
1735 if (!xHeaders.is())
1736 return QList<QAccessibleInterface*>();
1738 const sal_Int32 nCol = columnIndex();
1739 QList<QAccessibleInterface*> aHeaderCells;
1740 for (sal_Int32 nRow = 0; nRow < xHeaders->getAccessibleRowCount(); nRow++)
1742 Reference<XAccessible> xCell = xHeaders->getAccessibleCellAt(nRow, nCol);
1743 QAccessibleInterface* pInterface
1744 = QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xCell));
1745 aHeaderCells.push_back(pInterface);
1747 return aHeaderCells;
1750 int QtAccessibleWidget::columnIndex() const
1752 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1753 if (!xAcc.is())
1754 return -1;
1756 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
1757 if (!xTable.is())
1758 return -1;
1760 const sal_Int64 nIndexInParent = xAcc->getAccessibleIndexInParent();
1761 return xTable->getAccessibleColumn(nIndexInParent);
1764 bool QtAccessibleWidget::isSelected() const
1766 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1767 if (!xAcc.is())
1768 return false;
1770 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
1771 if (!xTable.is())
1772 return false;
1774 const sal_Int32 nColumn = columnIndex();
1775 const sal_Int32 nRow = rowIndex();
1776 return xTable->isAccessibleSelected(nRow, nColumn);
1779 int QtAccessibleWidget::columnExtent() const
1781 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1782 if (!xAcc.is())
1783 return -1;
1785 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
1786 if (!xTable.is())
1787 return -1;
1789 const sal_Int32 nColumn = columnIndex();
1790 const sal_Int32 nRow = rowIndex();
1791 return xTable->getAccessibleColumnExtentAt(nRow, nColumn);
1794 QList<QAccessibleInterface*> QtAccessibleWidget::rowHeaderCells() const
1796 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
1797 if (!xTable.is())
1798 return QList<QAccessibleInterface*>();
1800 Reference<XAccessibleTable> xHeaders = xTable->getAccessibleRowHeaders();
1801 if (!xHeaders.is())
1802 return QList<QAccessibleInterface*>();
1804 const sal_Int32 nRow = rowIndex();
1805 QList<QAccessibleInterface*> aHeaderCells;
1806 for (sal_Int32 nCol = 0; nCol < xHeaders->getAccessibleColumnCount(); nCol++)
1808 Reference<XAccessible> xCell = xHeaders->getAccessibleCellAt(nRow, nCol);
1809 QAccessibleInterface* pInterface
1810 = QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xCell));
1811 aHeaderCells.push_back(pInterface);
1813 return aHeaderCells;
1816 int QtAccessibleWidget::rowExtent() const
1818 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1819 if (!xAcc.is())
1820 return -1;
1822 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
1823 if (!xTable.is())
1824 return -1;
1826 const sal_Int32 nColumn = columnIndex();
1827 const sal_Int32 nRow = rowIndex();
1828 return xTable->getAccessibleRowExtentAt(nRow, nColumn);
1831 int QtAccessibleWidget::rowIndex() const
1833 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1834 if (!xAcc.is())
1835 return -1;
1837 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
1838 if (!xTable.is())
1839 return -1;
1841 const sal_Int64 nIndexInParent = xAcc->getAccessibleIndexInParent();
1842 return xTable->getAccessibleRow(nIndexInParent);
1845 QAccessibleInterface* QtAccessibleWidget::table() const
1847 Reference<XAccessibleTable> xTable = getAccessibleTableForParent();
1848 if (!xTable.is())
1849 return nullptr;
1851 Reference<XAccessible> xTableAcc(xTable, UNO_QUERY);
1852 if (!xTableAcc.is())
1853 return nullptr;
1855 return QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xTableAcc));
1858 // QAccessibleSelectionInterface
1859 int QtAccessibleWidget::selectedItemCount() const
1861 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1862 if (!xAcc.is())
1863 return 0;
1865 Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY);
1866 if (!xSelection.is())
1867 return 0;
1869 sal_Int64 nSelected = xSelection->getSelectedAccessibleChildCount();
1870 if (nSelected > std::numeric_limits<int>::max())
1872 SAL_WARN("vcl.qt",
1873 "QtAccessibleWidget::selectedItemCount: Cell count exceeds maximum int value, "
1874 "using max int.");
1875 nSelected = std::numeric_limits<int>::max();
1877 return nSelected;
1880 QList<QAccessibleInterface*> QtAccessibleWidget::selectedItems() const
1882 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1883 if (!xAcc.is())
1884 return QList<QAccessibleInterface*>();
1886 Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY);
1887 if (!xSelection.is())
1888 return QList<QAccessibleInterface*>();
1890 QList<QAccessibleInterface*> aSelectedItems;
1891 sal_Int64 nSelected = xSelection->getSelectedAccessibleChildCount();
1892 if (nSelected > std::numeric_limits<int>::max())
1894 SAL_WARN("vcl.qt",
1895 "QtAccessibleWidget::selectedItems: Cell count exceeds maximum int value, "
1896 "using max int.");
1897 nSelected = std::numeric_limits<int>::max();
1899 for (sal_Int64 i = 0; i < nSelected; i++)
1901 Reference<XAccessible> xChild = xSelection->getSelectedAccessibleChild(i);
1902 QAccessibleInterface* pInterface
1903 = QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xChild));
1904 aSelectedItems.push_back(pInterface);
1906 return aSelectedItems;
1909 #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0)
1910 QAccessibleInterface* QtAccessibleWidget::selectedItem(int nSelectionIndex) const
1912 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1913 if (!xAcc.is())
1914 return nullptr;
1916 Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY);
1917 if (!xSelection.is())
1918 return nullptr;
1920 if (nSelectionIndex < 0 || nSelectionIndex >= xSelection->getSelectedAccessibleChildCount())
1922 SAL_WARN("vcl.qt",
1923 "QtAccessibleWidget::selectedItem called with invalid index: " << nSelectionIndex);
1924 return nullptr;
1927 Reference<XAccessible> xChild = xSelection->getSelectedAccessibleChild(nSelectionIndex);
1928 if (!xChild)
1929 return nullptr;
1931 return QAccessible::queryAccessibleInterface(QtAccessibleRegistry::getQObject(xChild));
1934 bool QtAccessibleWidget::isSelected(QAccessibleInterface* pItem) const
1936 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1937 if (!xAcc.is())
1938 return false;
1940 Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY);
1941 if (!xSelection.is())
1942 return false;
1944 int nChildIndex = indexOfChild(pItem);
1945 if (nChildIndex < 0)
1946 return false;
1948 return xSelection->isAccessibleChildSelected(nChildIndex);
1951 bool QtAccessibleWidget::select(QAccessibleInterface* pItem)
1953 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1954 if (!xAcc.is())
1955 return false;
1957 Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY);
1958 if (!xSelection.is())
1959 return false;
1961 int nChildIndex = indexOfChild(pItem);
1962 if (nChildIndex < 0)
1963 return false;
1965 xSelection->selectAccessibleChild(nChildIndex);
1966 return true;
1969 bool QtAccessibleWidget::unselect(QAccessibleInterface* pItem)
1971 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1972 if (!xAcc.is())
1973 return false;
1975 Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY);
1976 if (!xSelection.is())
1977 return false;
1979 int nChildIndex = indexOfChild(pItem);
1980 if (nChildIndex < 0)
1981 return false;
1983 xSelection->deselectAccessibleChild(nChildIndex);
1984 return true;
1987 bool QtAccessibleWidget::selectAll()
1989 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
1990 if (!xAcc.is())
1991 return false;
1993 Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY);
1994 if (!xSelection.is())
1995 return false;
1997 xSelection->selectAllAccessibleChildren();
1998 return true;
2001 bool QtAccessibleWidget::clear()
2003 Reference<XAccessibleContext> xAcc = getAccessibleContextImpl();
2004 if (!xAcc.is())
2005 return false;
2007 Reference<XAccessibleSelection> xSelection(xAcc, UNO_QUERY);
2008 if (!xSelection.is())
2009 return false;
2011 xSelection->clearAccessibleSelection();
2012 return true;
2014 #endif
2016 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */