1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 
20 #include <Qt5AccessibleWidget.hxx>
21 #include <Qt5AccessibleWidget.moc>
22 
23 #include <QtGui/QAccessibleInterface>
24 
25 #include <Qt5AccessibleEventListener.hxx>
26 #include <Qt5Frame.hxx>
27 #include <Qt5Tools.hxx>
28 #include <Qt5Widget.hxx>
29 #include <Qt5XAccessible.hxx>
30 
31 #include <com/sun/star/accessibility/AccessibleRelationType.hpp>
32 #include <com/sun/star/accessibility/AccessibleRole.hpp>
33 #include <com/sun/star/accessibility/AccessibleStateType.hpp>
34 #include <com/sun/star/accessibility/XAccessible.hpp>
35 #include <com/sun/star/accessibility/XAccessibleAction.hpp>
36 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
37 #include <com/sun/star/accessibility/XAccessibleEditableText.hpp>
38 #include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
39 #include <com/sun/star/accessibility/XAccessibleEventListener.hpp>
40 #include <com/sun/star/accessibility/XAccessibleKeyBinding.hpp>
41 #include <com/sun/star/accessibility/XAccessibleRelationSet.hpp>
42 #include <com/sun/star/accessibility/XAccessibleStateSet.hpp>
43 #include <com/sun/star/accessibility/XAccessibleTable.hpp>
44 #include <com/sun/star/accessibility/XAccessibleTableSelection.hpp>
45 #include <com/sun/star/accessibility/XAccessibleText.hpp>
46 #include <com/sun/star/accessibility/XAccessibleValue.hpp>
47 #include <com/sun/star/awt/FontWeight.hpp>
48 #include <com/sun/star/beans/PropertyValue.hpp>
49 #include <com/sun/star/lang/DisposedException.hpp>
50 #include <com/sun/star/uno/Sequence.hxx>
51 
52 #include <comphelper/AccessibleImplementationHelper.hxx>
53 #include <sal/log.hxx>
54 #include <vcl/popupmenuwindow.hxx>
55 
56 using namespace css;
57 using namespace css::accessibility;
58 using namespace css::beans;
59 using namespace css::uno;
60 
Qt5AccessibleWidget(const Reference<XAccessible> xAccessible,QObject * pObject)61 Qt5AccessibleWidget::Qt5AccessibleWidget(const Reference<XAccessible> xAccessible, QObject* pObject)
62     : m_xAccessible(xAccessible)
63     , m_pObject(pObject)
64 {
65     Reference<XAccessibleContext> xContext = xAccessible->getAccessibleContext();
66     Reference<XAccessibleEventBroadcaster> xBroadcaster(xContext, UNO_QUERY);
67     if (xBroadcaster.is())
68     {
69         Reference<XAccessibleEventListener> xListener(
70             new Qt5AccessibleEventListener(xAccessible, this));
71         xBroadcaster->addAccessibleEventListener(xListener);
72     }
73 }
74 
getAccessibleContextImpl() const75 Reference<XAccessibleContext> Qt5AccessibleWidget::getAccessibleContextImpl() const
76 {
77     Reference<XAccessibleContext> xAc;
78 
79     if (m_xAccessible.is())
80     {
81         try
82         {
83             xAc = m_xAccessible->getAccessibleContext();
84         }
85         catch (css::lang::DisposedException /*ex*/)
86         {
87             SAL_WARN("vcl.qt5", "Accessible context disposed already");
88         }
89         // sometimes getAccessibleContext throws also RuntimeException if context is no longer alive
90         catch (css::uno::RuntimeException /*ex*/)
91         {
92             // so let's catch it here, cuz otherwise soffice falls flat on its face
93             // with FatalError and nothing else
94             SAL_WARN("vcl.qt5", "Accessible context no longer alive");
95         }
96     }
97 
98     return xAc;
99 }
100 
window() const101 QWindow* Qt5AccessibleWidget::window() const { return nullptr; }
102 
childCount() const103 int Qt5AccessibleWidget::childCount() const
104 {
105     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
106     if (!xAc.is())
107         return 0;
108 
109     return xAc->getAccessibleChildCount();
110 }
111 
indexOfChild(const QAccessibleInterface *) const112 int Qt5AccessibleWidget::indexOfChild(const QAccessibleInterface* /* child */) const { return 0; }
113 
114 namespace
115 {
lcl_matchUnoRelation(short relationType)116 QAccessible::Relation lcl_matchUnoRelation(short relationType)
117 {
118     switch (relationType)
119     {
120         case AccessibleRelationType::CONTROLLER_FOR:
121             return QAccessible::Controller;
122         case AccessibleRelationType::CONTROLLED_BY:
123             return QAccessible::Controlled;
124         case AccessibleRelationType::LABEL_FOR:
125             return QAccessible::Label;
126         case AccessibleRelationType::LABELED_BY:
127             return QAccessible::Labelled;
128         case AccessibleRelationType::INVALID:
129         case AccessibleRelationType::CONTENT_FLOWS_FROM:
130         case AccessibleRelationType::CONTENT_FLOWS_TO:
131         case AccessibleRelationType::MEMBER_OF:
132         case AccessibleRelationType::SUB_WINDOW_OF:
133         case AccessibleRelationType::NODE_CHILD_OF:
134         case AccessibleRelationType::DESCRIBED_BY:
135         default:
136             SAL_WARN("vcl.qt5", "Unmatched relation: " << relationType);
137             return nullptr;
138     }
139 }
140 
lcl_matchQtRelation(QAccessible::Relation relationType)141 short lcl_matchQtRelation(QAccessible::Relation relationType)
142 {
143     switch (relationType)
144     {
145         case QAccessible::Controller:
146             return AccessibleRelationType::CONTROLLER_FOR;
147         case QAccessible::Controlled:
148             return AccessibleRelationType::CONTROLLED_BY;
149         case QAccessible::Label:
150             return AccessibleRelationType::LABEL_FOR;
151         case QAccessible::Labelled:
152             return AccessibleRelationType::LABELED_BY;
153         default:
154             SAL_WARN("vcl.qt5", "Unmatched relation: " << relationType);
155     }
156     return 0;
157 }
158 
lcl_appendRelation(QVector<QPair<QAccessibleInterface *,QAccessible::Relation>> * relations,AccessibleRelation aRelation)159 void lcl_appendRelation(QVector<QPair<QAccessibleInterface*, QAccessible::Relation>>* relations,
160                         AccessibleRelation aRelation)
161 {
162     QAccessible::Relation aQRelation = lcl_matchUnoRelation(aRelation.RelationType);
163     sal_uInt32 nTargetCount = aRelation.TargetSet.getLength();
164 
165     for (sal_uInt32 i = 0; i < nTargetCount; i++)
166     {
167         Reference<XAccessible> xAccessible(aRelation.TargetSet[i], uno::UNO_QUERY);
168         relations->append(
169             { QAccessible::queryAccessibleInterface(new Qt5XAccessible(xAccessible)), aQRelation });
170     }
171 }
172 }
173 
174 QVector<QPair<QAccessibleInterface*, QAccessible::Relation>>
relations(QAccessible::Relation match) const175 Qt5AccessibleWidget::relations(QAccessible::Relation match) const
176 {
177     QVector<QPair<QAccessibleInterface*, QAccessible::Relation>> relations;
178 
179     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
180     if (!xAc.is())
181         return relations;
182 
183     Reference<XAccessibleRelationSet> xRelationSet = xAc->getAccessibleRelationSet();
184     if (xRelationSet.is())
185     {
186         if (match == QAccessible::AllRelations)
187         {
188             int count = xRelationSet->getRelationCount();
189             for (int i = 0; i < count; i++)
190             {
191                 AccessibleRelation aRelation = xRelationSet->getRelation(i);
192                 lcl_appendRelation(&relations, aRelation);
193             }
194         }
195         else
196         {
197             AccessibleRelation aRelation = xRelationSet->getRelation(lcl_matchQtRelation(match));
198             lcl_appendRelation(&relations, aRelation);
199         }
200     }
201 
202     return relations;
203 }
204 
focusChild() const205 QAccessibleInterface* Qt5AccessibleWidget::focusChild() const
206 {
207     /* if (m_pWindow->HasChildPathFocus())
208         return QAccessible::queryAccessibleInterface(
209             new Qt5XAccessible(m_xAccessible->getAccessibleContext()->getAccessibleChild(index))); */
210     return QAccessible::queryAccessibleInterface(object());
211 }
212 
rect() const213 QRect Qt5AccessibleWidget::rect() const
214 {
215     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
216     if (!xAc.is())
217         return QRect();
218 
219     Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
220     awt::Point aPoint = xAccessibleComponent->getLocation();
221     awt::Size aSize = xAccessibleComponent->getSize();
222 
223     return QRect(aPoint.X, aPoint.Y, aSize.Width, aSize.Height);
224 }
225 
parent() const226 QAccessibleInterface* Qt5AccessibleWidget::parent() const
227 {
228     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
229     if (!xAc.is())
230         return nullptr;
231 
232     return QAccessible::queryAccessibleInterface(new Qt5XAccessible(xAc->getAccessibleParent()));
233 }
child(int index) const234 QAccessibleInterface* Qt5AccessibleWidget::child(int index) const
235 {
236     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
237     if (!xAc.is())
238         return nullptr;
239 
240     return QAccessible::queryAccessibleInterface(
241         new Qt5XAccessible(xAc->getAccessibleChild(index)));
242 }
243 
text(QAccessible::Text text) const244 QString Qt5AccessibleWidget::text(QAccessible::Text text) const
245 {
246     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
247     if (!xAc.is())
248         return QString();
249 
250     switch (text)
251     {
252         case QAccessible::Name:
253             return toQString(xAc->getAccessibleName());
254         case QAccessible::Description:
255         case QAccessible::DebugDescription:
256             return toQString(xAc->getAccessibleDescription());
257         case QAccessible::Value:
258         case QAccessible::Help:
259         case QAccessible::Accelerator:
260         case QAccessible::UserText:
261         default:
262             return QString("Unknown");
263     }
264 }
role() const265 QAccessible::Role Qt5AccessibleWidget::role() const
266 {
267     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
268     if (!xAc.is())
269         return QAccessible::NoRole;
270 
271     switch (xAc->getAccessibleRole())
272     {
273         case AccessibleRole::UNKNOWN:
274             return QAccessible::NoRole;
275 
276         case AccessibleRole::ALERT:
277             return QAccessible::AlertMessage;
278 
279         case AccessibleRole::COLUMN_HEADER:
280             return QAccessible::ColumnHeader;
281 
282         case AccessibleRole::CANVAS:
283             return QAccessible::Canvas;
284 
285         case AccessibleRole::CHECK_BOX:
286             return QAccessible::CheckBox;
287 
288         case AccessibleRole::CHECK_MENU_ITEM:
289             return QAccessible::MenuItem;
290 
291         case AccessibleRole::COLOR_CHOOSER:
292             return QAccessible::ColorChooser;
293 
294         case AccessibleRole::COMBO_BOX:
295             return QAccessible::ComboBox;
296 
297         case AccessibleRole::DATE_EDITOR:
298             return QAccessible::EditableText;
299 
300         case AccessibleRole::DESKTOP_ICON:
301             return QAccessible::Graphic;
302 
303         case AccessibleRole::DESKTOP_PANE:
304         case AccessibleRole::DIRECTORY_PANE:
305             return QAccessible::Pane;
306 
307         case AccessibleRole::DIALOG:
308             return QAccessible::Dialog;
309 
310         case AccessibleRole::DOCUMENT:
311             return QAccessible::Document;
312 
313         case AccessibleRole::EMBEDDED_OBJECT:
314             return QAccessible::UserRole;
315 
316         case AccessibleRole::END_NOTE:
317             return QAccessible::Note;
318 
319         case AccessibleRole::FILLER:
320             return QAccessible::Whitespace;
321 
322         case AccessibleRole::FONT_CHOOSER:
323             return QAccessible::UserRole;
324 
325         case AccessibleRole::FOOTER:
326             return QAccessible::Footer;
327 
328         case AccessibleRole::FOOTNOTE:
329             return QAccessible::Note;
330 
331         case AccessibleRole::FRAME: // top-level window with title bar
332             return QAccessible::Window;
333 
334         case AccessibleRole::GLASS_PANE:
335             return QAccessible::UserRole;
336 
337         case AccessibleRole::GRAPHIC:
338             return QAccessible::Graphic;
339 
340         case AccessibleRole::GROUP_BOX:
341             return QAccessible::Grouping;
342 
343         case AccessibleRole::HEADER:
344             return QAccessible::UserRole;
345 
346         case AccessibleRole::HEADING:
347             return QAccessible::Heading;
348 
349         case AccessibleRole::HYPER_LINK:
350             return QAccessible::Link;
351 
352         case AccessibleRole::ICON:
353             return QAccessible::Graphic;
354 
355         case AccessibleRole::INTERNAL_FRAME:
356             return QAccessible::UserRole;
357 
358         case AccessibleRole::LABEL:
359             return QAccessible::StaticText;
360 
361         case AccessibleRole::LAYERED_PANE:
362             return QAccessible::Pane;
363 
364         case AccessibleRole::LIST:
365             return QAccessible::List;
366 
367         case AccessibleRole::LIST_ITEM:
368             return QAccessible::ListItem;
369 
370         case AccessibleRole::MENU:
371         case AccessibleRole::MENU_BAR:
372             return QAccessible::MenuBar;
373 
374         case AccessibleRole::MENU_ITEM:
375             return QAccessible::MenuItem;
376 
377         case AccessibleRole::OPTION_PANE:
378             return QAccessible::Pane;
379 
380         case AccessibleRole::PAGE_TAB:
381             return QAccessible::PageTab;
382 
383         case AccessibleRole::PAGE_TAB_LIST:
384             return QAccessible::PageTabList;
385 
386         case AccessibleRole::PANEL:
387             return QAccessible::Pane;
388 
389         case AccessibleRole::PARAGRAPH:
390             return QAccessible::Paragraph;
391 
392         case AccessibleRole::PASSWORD_TEXT:
393             return QAccessible::EditableText;
394 
395         case AccessibleRole::POPUP_MENU:
396             return QAccessible::PopupMenu;
397 
398         case AccessibleRole::PUSH_BUTTON:
399             return QAccessible::Button;
400 
401         case AccessibleRole::PROGRESS_BAR:
402             return QAccessible::ProgressBar;
403 
404         case AccessibleRole::RADIO_BUTTON:
405             return QAccessible::RadioButton;
406 
407         case AccessibleRole::RADIO_MENU_ITEM:
408             return QAccessible::MenuItem;
409 
410         case AccessibleRole::ROW_HEADER:
411             return QAccessible::RowHeader;
412 
413         case AccessibleRole::ROOT_PANE:
414             return QAccessible::Pane;
415 
416         case AccessibleRole::SCROLL_BAR:
417             return QAccessible::ScrollBar;
418 
419         case AccessibleRole::SCROLL_PANE:
420             return QAccessible::Pane;
421 
422         case AccessibleRole::SHAPE:
423             return QAccessible::Graphic;
424 
425         case AccessibleRole::SEPARATOR:
426             return QAccessible::Separator;
427 
428         case AccessibleRole::SLIDER:
429             return QAccessible::Slider;
430 
431         case AccessibleRole::SPIN_BOX:
432             return QAccessible::SpinBox;
433 
434         case AccessibleRole::SPLIT_PANE:
435             return QAccessible::Pane;
436 
437         case AccessibleRole::STATUS_BAR:
438             return QAccessible::StatusBar;
439 
440         case AccessibleRole::TABLE:
441             return QAccessible::Table;
442 
443         case AccessibleRole::TABLE_CELL:
444             return QAccessible::Cell;
445 
446         case AccessibleRole::TEXT:
447             return QAccessible::EditableText;
448 
449         case AccessibleRole::TEXT_FRAME:
450             return QAccessible::UserRole;
451 
452         case AccessibleRole::TOGGLE_BUTTON:
453             return QAccessible::Button;
454 
455         case AccessibleRole::TOOL_BAR:
456             return QAccessible::ToolBar;
457 
458         case AccessibleRole::TOOL_TIP:
459             return QAccessible::ToolTip;
460 
461         case AccessibleRole::TREE:
462             return QAccessible::Tree;
463 
464         case AccessibleRole::VIEW_PORT:
465             return QAccessible::UserRole;
466 
467         case AccessibleRole::BUTTON_DROPDOWN:
468             return QAccessible::Button;
469 
470         case AccessibleRole::BUTTON_MENU:
471             return QAccessible::Button;
472 
473         case AccessibleRole::CAPTION:
474             return QAccessible::StaticText;
475 
476         case AccessibleRole::CHART:
477             return QAccessible::Chart;
478 
479         case AccessibleRole::EDIT_BAR:
480             return QAccessible::Equation;
481 
482         case AccessibleRole::FORM:
483             return QAccessible::Form;
484 
485         case AccessibleRole::IMAGE_MAP:
486             return QAccessible::Graphic;
487 
488         case AccessibleRole::NOTE:
489             return QAccessible::Note;
490 
491         case AccessibleRole::RULER:
492             return QAccessible::UserRole;
493 
494         case AccessibleRole::SECTION:
495             return QAccessible::Section;
496 
497         case AccessibleRole::TREE_ITEM:
498             return QAccessible::TreeItem;
499 
500         case AccessibleRole::TREE_TABLE:
501             return QAccessible::Tree;
502 
503         case AccessibleRole::COMMENT:
504             return QAccessible::Note;
505 
506         case AccessibleRole::COMMENT_END:
507             return QAccessible::UserRole;
508 
509         case AccessibleRole::DOCUMENT_PRESENTATION:
510             return QAccessible::Document;
511 
512         case AccessibleRole::DOCUMENT_SPREADSHEET:
513             return QAccessible::Document;
514 
515         case AccessibleRole::DOCUMENT_TEXT:
516             return QAccessible::Document;
517 
518         case AccessibleRole::STATIC:
519             return QAccessible::StaticText;
520 
521         /* Ignore window objects for sub-menus, combo- and list boxes,
522          *  which are exposed as children of their parents.
523          */
524         case AccessibleRole::WINDOW: // top-level window without title bar
525         {
526             return QAccessible::Window;
527         }
528     }
529 
530     SAL_WARN("vcl.qt5",
531              "Unmapped role: " << m_xAccessible->getAccessibleContext()->getAccessibleRole());
532     return QAccessible::NoRole;
533 }
534 
535 namespace
536 {
lcl_addState(QAccessible::State * state,sal_Int16 nState)537 void lcl_addState(QAccessible::State* state, sal_Int16 nState)
538 {
539     switch (nState)
540     {
541         case AccessibleStateType::INVALID:
542             state->invalid = true;
543             break;
544         case AccessibleStateType::ACTIVE:
545             state->active = true;
546             break;
547         case AccessibleStateType::ARMED:
548             // No match
549             break;
550         case AccessibleStateType::BUSY:
551             state->busy = true;
552             break;
553         case AccessibleStateType::CHECKED:
554             state->checked = true;
555             break;
556         case AccessibleStateType::EDITABLE:
557             state->editable = true;
558             break;
559         case AccessibleStateType::ENABLED:
560             state->disabled = false;
561             break;
562         case AccessibleStateType::EXPANDABLE:
563             state->expandable = true;
564             break;
565         case AccessibleStateType::FOCUSABLE:
566             state->focusable = true;
567             break;
568         case AccessibleStateType::FOCUSED:
569             state->focused = true;
570             break;
571         case AccessibleStateType::HORIZONTAL:
572             // No match
573             break;
574         case AccessibleStateType::ICONIFIED:
575             // No match
576             break;
577         case AccessibleStateType::INDETERMINATE:
578             // No match
579             break;
580         case AccessibleStateType::MANAGES_DESCENDANTS:
581             // No match
582             break;
583         case AccessibleStateType::MODAL:
584             state->modal = true;
585             break;
586         case AccessibleStateType::OPAQUE:
587             // No match
588             break;
589         case AccessibleStateType::PRESSED:
590             state->pressed = true;
591             break;
592         case AccessibleStateType::RESIZABLE:
593             state->sizeable = true;
594             break;
595         case AccessibleStateType::SELECTABLE:
596             state->selectable = true;
597             break;
598         case AccessibleStateType::SELECTED:
599             state->selected = true;
600             break;
601         case AccessibleStateType::SENSITIVE:
602             // No match
603             break;
604         case AccessibleStateType::SHOWING:
605             // No match
606             break;
607         case AccessibleStateType::SINGLE_LINE:
608             // No match
609             break;
610         case AccessibleStateType::STALE:
611             // No match
612             break;
613         case AccessibleStateType::TRANSIENT:
614             // No match
615             break;
616         case AccessibleStateType::VERTICAL:
617             // No match
618             break;
619         case AccessibleStateType::VISIBLE:
620             state->invisible = false;
621             break;
622         case AccessibleStateType::DEFAULT:
623             // No match
624             break;
625         case AccessibleStateType::DEFUNC:
626             state->invalid = true;
627             break;
628         case AccessibleStateType::MULTI_SELECTABLE:
629             state->multiSelectable = true;
630             break;
631         default:
632             SAL_WARN("vcl.qt5", "Unmapped state: " << nState);
633             break;
634     }
635 }
636 }
637 
state() const638 QAccessible::State Qt5AccessibleWidget::state() const
639 {
640     QAccessible::State state;
641 
642     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
643     if (!xAc.is())
644         return state;
645 
646     Reference<XAccessibleStateSet> xStateSet(xAc->getAccessibleStateSet());
647 
648     if (!xStateSet.is())
649         return state;
650 
651     Sequence<sal_Int16> aStates = xStateSet->getStates();
652 
653     for (sal_Int32 n = 0; n < aStates.getLength(); n++)
654     {
655         lcl_addState(&state, n);
656     }
657 
658     return state;
659 }
660 
foregroundColor() const661 QColor Qt5AccessibleWidget::foregroundColor() const
662 {
663     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
664     if (!xAc.is())
665         return QColor();
666 
667     Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
668     return toQColor(xAccessibleComponent->getForeground());
669 }
670 
backgroundColor() const671 QColor Qt5AccessibleWidget::backgroundColor() const
672 {
673     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
674     if (!xAc.is())
675         return QColor();
676 
677     Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
678     return toQColor(xAccessibleComponent->getBackground());
679 }
680 
interface_cast(QAccessible::InterfaceType t)681 void* Qt5AccessibleWidget::interface_cast(QAccessible::InterfaceType t)
682 {
683     if (t == QAccessible::ActionInterface)
684         return static_cast<QAccessibleActionInterface*>(this);
685     if (t == QAccessible::TextInterface)
686         return static_cast<QAccessibleTextInterface*>(this);
687     if (t == QAccessible::EditableTextInterface)
688         return static_cast<QAccessibleEditableTextInterface*>(this);
689     if (t == QAccessible::ValueInterface)
690         return static_cast<QAccessibleValueInterface*>(this);
691     if (t == QAccessible::TableInterface)
692         return static_cast<QAccessibleTableInterface*>(this);
693     return nullptr;
694 }
695 
isValid() const696 bool Qt5AccessibleWidget::isValid() const
697 {
698     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
699     return xAc.is();
700 }
701 
object() const702 QObject* Qt5AccessibleWidget::object() const { return m_pObject; }
703 
setText(QAccessible::Text,const QString &)704 void Qt5AccessibleWidget::setText(QAccessible::Text /* t */, const QString& /* text */) {}
705 
childAt(int x,int y) const706 QAccessibleInterface* Qt5AccessibleWidget::childAt(int x, int y) const
707 {
708     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
709     if (!xAc.is())
710         return nullptr;
711 
712     Reference<XAccessibleComponent> xAccessibleComponent(xAc, UNO_QUERY);
713     return QAccessible::queryAccessibleInterface(
714         new Qt5XAccessible(xAccessibleComponent->getAccessibleAtPoint(awt::Point(x, y))));
715 }
716 
customFactory(const QString & classname,QObject * object)717 QAccessibleInterface* Qt5AccessibleWidget::customFactory(const QString& classname, QObject* object)
718 {
719     if (classname == QLatin1String("Qt5Widget") && object && object->isWidgetType())
720     {
721         Qt5Widget* pWidget = static_cast<Qt5Widget*>(object);
722         vcl::Window* pWindow = pWidget->frame().GetWindow();
723 
724         if (pWindow)
725             return new Qt5AccessibleWidget(pWindow->GetAccessible(), object);
726     }
727     if (classname == QLatin1String("Qt5XAccessible") && object)
728     {
729         Qt5XAccessible* pXAccessible = dynamic_cast<Qt5XAccessible*>(object);
730         if (pXAccessible && pXAccessible->m_xAccessible.is())
731             return new Qt5AccessibleWidget(pXAccessible->m_xAccessible, object);
732     }
733 
734     return nullptr;
735 }
736 
737 // QAccessibleActionInterface
actionNames() const738 QStringList Qt5AccessibleWidget::actionNames() const
739 {
740     QStringList actionNames;
741     Reference<XAccessibleAction> xAccessibleAction(m_xAccessible, UNO_QUERY);
742     if (!xAccessibleAction.is())
743         return actionNames;
744 
745     int count = xAccessibleAction->getAccessibleActionCount();
746     for (int i = 0; i < count; i++)
747     {
748         OUString desc = xAccessibleAction->getAccessibleActionDescription(i);
749         actionNames.append(toQString(desc));
750     }
751     return actionNames;
752 }
753 
doAction(const QString & actionName)754 void Qt5AccessibleWidget::doAction(const QString& actionName)
755 {
756     Reference<XAccessibleAction> xAccessibleAction(m_xAccessible, UNO_QUERY);
757     if (!xAccessibleAction.is())
758         return;
759 
760     int index = actionNames().indexOf(actionName);
761     if (index == -1)
762         return;
763     xAccessibleAction->doAccessibleAction(index);
764 }
765 
keyBindingsForAction(const QString & actionName) const766 QStringList Qt5AccessibleWidget::keyBindingsForAction(const QString& actionName) const
767 {
768     QStringList keyBindings;
769     Reference<XAccessibleAction> xAccessibleAction(m_xAccessible, UNO_QUERY);
770     if (!xAccessibleAction.is())
771         return keyBindings;
772 
773     int index = actionNames().indexOf(actionName);
774     if (index == -1)
775         return keyBindings;
776 
777     Reference<XAccessibleKeyBinding> xKeyBinding
778         = xAccessibleAction->getAccessibleActionKeyBinding(index);
779 
780     if (!xKeyBinding.is())
781         return keyBindings;
782 
783     int count = xKeyBinding->getAccessibleKeyBindingCount();
784     for (int i = 0; i < count; i++)
785     {
786         Sequence<awt::KeyStroke> keyStroke = xKeyBinding->getAccessibleKeyBinding(i);
787         keyBindings.append(toQString(comphelper::GetkeyBindingStrByXkeyBinding(keyStroke)));
788     }
789     return keyBindings;
790 }
791 
valueInterface()792 QAccessibleValueInterface* Qt5AccessibleWidget::valueInterface() { return nullptr; }
793 
textInterface()794 QAccessibleTextInterface* Qt5AccessibleWidget::textInterface() { return nullptr; }
795 
796 // QAccessibleTextInterface
addSelection(int,int)797 void Qt5AccessibleWidget::addSelection(int /* startOffset */, int /* endOffset */)
798 {
799     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::addSelection");
800 }
801 
802 namespace
803 {
lcl_convertFontWeight(double fontWeight)804 OUString lcl_convertFontWeight(double fontWeight)
805 {
806     if (fontWeight == awt::FontWeight::THIN || fontWeight == awt::FontWeight::ULTRALIGHT)
807         return "100";
808     if (fontWeight == awt::FontWeight::LIGHT)
809         return "200";
810     if (fontWeight == awt::FontWeight::SEMILIGHT)
811         return "300";
812     if (fontWeight == awt::FontWeight::NORMAL)
813         return "normal";
814     if (fontWeight == awt::FontWeight::SEMIBOLD)
815         return "500";
816     if (fontWeight == awt::FontWeight::BOLD)
817         return "bold";
818     if (fontWeight == awt::FontWeight::ULTRABOLD)
819         return "800";
820     if (fontWeight == awt::FontWeight::BLACK)
821         return "900";
822 
823     // awt::FontWeight::DONTKNOW || fontWeight == awt::FontWeight::NORMAL
824     return "normal";
825 }
826 }
827 
attributes(int offset,int * startOffset,int * endOffset) const828 QString Qt5AccessibleWidget::attributes(int offset, int* startOffset, int* endOffset) const
829 {
830     Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
831     if (!xText.is())
832         return QString();
833 
834     // handle special values for offset the same way base class's QAccessibleTextWidget::attributes does
835     // (as defined in IAccessible 2: -1 -> length, -2 -> cursor position)
836     if (offset == -2)
837         offset = cursorPosition(); // currently always returns 0
838 
839     const int nTextLength = characterCount();
840     if (offset == -1 || offset == nTextLength)
841         offset = nTextLength - 1;
842 
843     if (offset < 0 || offset > nTextLength)
844     {
845         *startOffset = -1;
846         *endOffset = -1;
847         return QString();
848     }
849 
850     Sequence<PropertyValue> attribs = xText->getCharacterAttributes(offset, Sequence<OUString>());
851     const PropertyValue* pValues = attribs.getConstArray();
852     OUString aRet;
853     for (sal_Int32 i = 0; i < attribs.getLength(); i++)
854     {
855         if (pValues[i].Name == "CharFontName")
856         {
857             OUString aStr;
858             pValues[i].Value >>= aStr;
859             aRet += "font-family:" + aStr + ";";
860             continue;
861         }
862         if (pValues[i].Name == "CharHeight")
863         {
864             double fHeight;
865             pValues[i].Value >>= fHeight;
866             aRet += "font-size:" + OUString::number(fHeight) + "pt;";
867             continue;
868         }
869         if (pValues[i].Name == "CharWeight")
870         {
871             double fWeight;
872             pValues[i].Value >>= fWeight;
873             aRet += "font-weight:" + lcl_convertFontWeight(fWeight) + ";";
874             continue;
875         }
876     }
877     *startOffset = offset;
878     *endOffset = offset + 1;
879     return toQString(aRet);
880 }
characterCount() const881 int Qt5AccessibleWidget::characterCount() const
882 {
883     Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
884     if (xText.is())
885         return xText->getCharacterCount();
886     return 0;
887 }
characterRect(int) const888 QRect Qt5AccessibleWidget::characterRect(int /* offset */) const
889 {
890     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::characterRect");
891     return QRect();
892 }
cursorPosition() const893 int Qt5AccessibleWidget::cursorPosition() const
894 {
895     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::cursorPosition");
896     return 0;
897 }
offsetAtPoint(const QPoint &) const898 int Qt5AccessibleWidget::offsetAtPoint(const QPoint& /* point */) const
899 {
900     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::offsetAtPoint");
901     return 0;
902 }
removeSelection(int)903 void Qt5AccessibleWidget::removeSelection(int /* selectionIndex */)
904 {
905     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::removeSelection");
906 }
scrollToSubstring(int,int)907 void Qt5AccessibleWidget::scrollToSubstring(int /* startIndex */, int /* endIndex */)
908 {
909     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::scrollToSubstring");
910 }
911 
selection(int selectionIndex,int * startOffset,int * endOffset) const912 void Qt5AccessibleWidget::selection(int selectionIndex, int* startOffset, int* endOffset) const
913 {
914     if (!startOffset && !endOffset)
915         return;
916 
917     Reference<XAccessibleText> xText;
918     if (selectionIndex == 0)
919         xText = Reference<XAccessibleText>(m_xAccessible, UNO_QUERY);
920 
921     if (startOffset)
922         *startOffset = xText.is() ? xText->getSelectionStart() : 0;
923     if (endOffset)
924         *endOffset = xText.is() ? xText->getSelectionEnd() : 0;
925 }
926 
selectionCount() const927 int Qt5AccessibleWidget::selectionCount() const
928 {
929     Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
930     if (xText.is() && !xText->getSelectedText().isEmpty())
931         return 1; // Only 1 selection supported atm
932     return 0;
933 }
setCursorPosition(int position)934 void Qt5AccessibleWidget::setCursorPosition(int position)
935 {
936     Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
937     if (xText.is())
938         xText->setCaretPosition(position);
939 }
setSelection(int,int startOffset,int endOffset)940 void Qt5AccessibleWidget::setSelection(int /* selectionIndex */, int startOffset, int endOffset)
941 {
942     Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
943     if (xText.is())
944         xText->setSelection(startOffset, endOffset);
945 }
text(int startOffset,int endOffset) const946 QString Qt5AccessibleWidget::text(int startOffset, int endOffset) const
947 {
948     Reference<XAccessibleText> xText(m_xAccessible, UNO_QUERY);
949     if (xText.is())
950         return toQString(xText->getTextRange(startOffset, endOffset));
951     return QString();
952 }
textAfterOffset(int,QAccessible::TextBoundaryType,int *,int *) const953 QString Qt5AccessibleWidget::textAfterOffset(int /* offset */,
954                                              QAccessible::TextBoundaryType /* boundaryType */,
955                                              int* /* startOffset */, int* /* endOffset */) const
956 {
957     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::textAfterOffset");
958     return QString();
959 }
textAtOffset(int,QAccessible::TextBoundaryType,int *,int *) const960 QString Qt5AccessibleWidget::textAtOffset(int /* offset */,
961                                           QAccessible::TextBoundaryType /* boundaryType */,
962                                           int* /* startOffset */, int* /* endOffset */) const
963 {
964     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::textAtOffset");
965     return QString();
966 }
textBeforeOffset(int,QAccessible::TextBoundaryType,int *,int *) const967 QString Qt5AccessibleWidget::textBeforeOffset(int /* offset */,
968                                               QAccessible::TextBoundaryType /* boundaryType */,
969                                               int* /* startOffset */, int* /* endOffset */) const
970 {
971     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTextInterface::textBeforeOffset");
972     return QString();
973 }
974 
975 // QAccessibleEditableTextInterface
976 
deleteText(int startOffset,int endOffset)977 void Qt5AccessibleWidget::deleteText(int startOffset, int endOffset)
978 {
979     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
980     if (!xAc.is())
981         return;
982 
983     Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY);
984     if (!xEditableText.is())
985         return;
986     xEditableText->deleteText(startOffset, endOffset);
987 }
988 
insertText(int offset,const QString & text)989 void Qt5AccessibleWidget::insertText(int offset, const QString& text)
990 {
991     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
992     if (!xAc.is())
993         return;
994 
995     Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY);
996     if (!xEditableText.is())
997         return;
998     xEditableText->insertText(toOUString(text), offset);
999 }
1000 
replaceText(int startOffset,int endOffset,const QString & text)1001 void Qt5AccessibleWidget::replaceText(int startOffset, int endOffset, const QString& text)
1002 {
1003     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1004     if (!xAc.is())
1005         return;
1006 
1007     Reference<XAccessibleEditableText> xEditableText(xAc, UNO_QUERY);
1008     if (!xEditableText.is())
1009         return;
1010     xEditableText->replaceText(startOffset, endOffset, toOUString(text));
1011 }
1012 
1013 // QAccessibleValueInterface
currentValue() const1014 QVariant Qt5AccessibleWidget::currentValue() const
1015 {
1016     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1017     if (!xAc.is())
1018         return QVariant();
1019 
1020     Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1021     if (!xValue.is())
1022         return QVariant();
1023     double aDouble = 0;
1024     xValue->getCurrentValue() >>= aDouble;
1025     return QVariant(aDouble);
1026 }
maximumValue() const1027 QVariant Qt5AccessibleWidget::maximumValue() const
1028 {
1029     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1030     if (!xAc.is())
1031         return QVariant();
1032 
1033     Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1034     if (!xValue.is())
1035         return QVariant();
1036     double aDouble = 0;
1037     xValue->getMaximumValue() >>= aDouble;
1038     return QVariant(aDouble);
1039 }
minimumStepSize() const1040 QVariant Qt5AccessibleWidget::minimumStepSize() const { return QVariant(); }
minimumValue() const1041 QVariant Qt5AccessibleWidget::minimumValue() const
1042 {
1043     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1044     if (!xAc.is())
1045         return QVariant();
1046 
1047     Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1048     if (!xValue.is())
1049         return QVariant();
1050     double aDouble = 0;
1051     xValue->getMinimumValue() >>= aDouble;
1052     return QVariant(aDouble);
1053 }
setCurrentValue(const QVariant & value)1054 void Qt5AccessibleWidget::setCurrentValue(const QVariant& value)
1055 {
1056     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1057     if (!xAc.is())
1058         return;
1059 
1060     Reference<XAccessibleValue> xValue(xAc, UNO_QUERY);
1061     if (!xValue.is())
1062         return;
1063     xValue->setCurrentValue(Any(value.toDouble()));
1064 }
1065 
1066 // QAccessibleTable
caption() const1067 QAccessibleInterface* Qt5AccessibleWidget::caption() const
1068 {
1069     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1070     if (!xAc.is())
1071         return nullptr;
1072 
1073     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1074     if (!xTable.is())
1075         return nullptr;
1076     return QAccessible::queryAccessibleInterface(
1077         new Qt5XAccessible(xTable->getAccessibleCaption()));
1078 }
1079 
cellAt(int row,int column) const1080 QAccessibleInterface* Qt5AccessibleWidget::cellAt(int row, int column) const
1081 {
1082     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1083     if (!xAc.is())
1084         return nullptr;
1085 
1086     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1087     if (!xTable.is())
1088         return nullptr;
1089     return QAccessible::queryAccessibleInterface(
1090         new Qt5XAccessible(xTable->getAccessibleCellAt(row, column)));
1091 }
1092 
columnCount() const1093 int Qt5AccessibleWidget::columnCount() const
1094 {
1095     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1096     if (!xAc.is())
1097         return 0;
1098 
1099     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1100     if (!xTable.is())
1101         return 0;
1102     return xTable->getAccessibleColumnCount();
1103 }
1104 
columnDescription(int column) const1105 QString Qt5AccessibleWidget::columnDescription(int column) const
1106 {
1107     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1108     if (!xAc.is())
1109         return QString();
1110 
1111     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1112     if (!xTable.is())
1113         return QString();
1114     return toQString(xTable->getAccessibleColumnDescription(column));
1115 }
1116 
isColumnSelected(int) const1117 bool Qt5AccessibleWidget::isColumnSelected(int /* column */) const { return true; }
1118 
isRowSelected(int) const1119 bool Qt5AccessibleWidget::isRowSelected(int /* row */) const { return true; }
1120 
modelChange(QAccessibleTableModelChangeEvent *)1121 void Qt5AccessibleWidget::modelChange(QAccessibleTableModelChangeEvent*) {}
1122 
rowCount() const1123 int Qt5AccessibleWidget::rowCount() const
1124 {
1125     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1126     if (!xAc.is())
1127         return 0;
1128 
1129     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1130     if (!xTable.is())
1131         return 0;
1132     return xTable->getAccessibleRowCount();
1133 }
1134 
rowDescription(int row) const1135 QString Qt5AccessibleWidget::rowDescription(int row) const
1136 {
1137     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1138     if (!xAc.is())
1139         return QString();
1140 
1141     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1142     if (!xTable.is())
1143         return QString();
1144     return toQString(xTable->getAccessibleRowDescription(row));
1145 }
1146 
selectColumn(int column)1147 bool Qt5AccessibleWidget::selectColumn(int column)
1148 {
1149     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1150     if (!xAc.is())
1151         return false;
1152 
1153     Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
1154     if (!xTableSelection.is())
1155         return false;
1156     return xTableSelection->selectColumn(column);
1157 }
1158 
selectRow(int row)1159 bool Qt5AccessibleWidget::selectRow(int row)
1160 {
1161     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1162     if (!xAc.is())
1163         return false;
1164 
1165     Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
1166     if (!xTableSelection.is())
1167         return false;
1168     return xTableSelection->selectRow(row);
1169 }
1170 
selectedCellCount() const1171 int Qt5AccessibleWidget::selectedCellCount() const
1172 {
1173     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTableInterface::selectedCellCount");
1174     return 0;
1175 }
1176 
selectedCells() const1177 QList<QAccessibleInterface*> Qt5AccessibleWidget::selectedCells() const
1178 {
1179     SAL_INFO("vcl.qt5", "Unsupported QAccessibleTableInterface::selectedCells");
1180     return QList<QAccessibleInterface*>();
1181 }
1182 
selectedColumnCount() const1183 int Qt5AccessibleWidget::selectedColumnCount() const
1184 {
1185     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1186     if (!xAc.is())
1187         return 0;
1188 
1189     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1190     if (!xTable.is())
1191         return 0;
1192     return xTable->getSelectedAccessibleColumns().getLength();
1193 }
1194 
selectedColumns() const1195 QList<int> Qt5AccessibleWidget::selectedColumns() const
1196 {
1197     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1198     if (!xAc.is())
1199         return QList<int>();
1200 
1201     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1202     if (!xTable.is())
1203         return QList<int>();
1204     return toQList(xTable->getSelectedAccessibleColumns());
1205 }
1206 
selectedRowCount() const1207 int Qt5AccessibleWidget::selectedRowCount() const
1208 {
1209     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1210     if (!xAc.is())
1211         return 0;
1212 
1213     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1214     if (!xTable.is())
1215         return 0;
1216     return xTable->getSelectedAccessibleRows().getLength();
1217 }
1218 
selectedRows() const1219 QList<int> Qt5AccessibleWidget::selectedRows() const
1220 {
1221     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1222     if (!xAc.is())
1223         return QList<int>();
1224 
1225     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1226     if (!xTable.is())
1227         return QList<int>();
1228     return toQList(xTable->getSelectedAccessibleRows());
1229 }
1230 
summary() const1231 QAccessibleInterface* Qt5AccessibleWidget::summary() const
1232 {
1233     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1234     if (!xAc.is())
1235         return nullptr;
1236 
1237     Reference<XAccessibleTable> xTable(xAc, UNO_QUERY);
1238     if (!xTable.is())
1239         return nullptr;
1240     return QAccessible::queryAccessibleInterface(
1241         new Qt5XAccessible(xTable->getAccessibleSummary()));
1242 }
1243 
unselectColumn(int column)1244 bool Qt5AccessibleWidget::unselectColumn(int column)
1245 {
1246     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1247     if (!xAc.is())
1248         return false;
1249 
1250     Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
1251     if (!xTableSelection.is())
1252         return false;
1253     return xTableSelection->unselectColumn(column);
1254 }
1255 
unselectRow(int row)1256 bool Qt5AccessibleWidget::unselectRow(int row)
1257 {
1258     Reference<XAccessibleContext> xAc = getAccessibleContextImpl();
1259     if (!xAc.is())
1260         return false;
1261 
1262     Reference<XAccessibleTableSelection> xTableSelection(xAc, UNO_QUERY);
1263     if (!xTableSelection.is())
1264         return false;
1265     return xTableSelection->unselectRow(row);
1266 }
1267 
1268 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1269