mirror of https://github.com/BOINC/boinc.git
1511 lines
64 KiB
C++
Executable File
1511 lines
64 KiB
C++
Executable File
// This file is part of BOINC.
|
|
// http://boinc.berkeley.edu
|
|
// Copyright (C) 2009 University of California
|
|
//
|
|
// BOINC is free software; you can redistribute it and/or modify it
|
|
// under the terms of the GNU Lesser General Public License
|
|
// as published by the Free Software Foundation,
|
|
// either version 3 of the License, or (at your option) any later version.
|
|
//
|
|
// BOINC is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
// See the GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with BOINC. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
// macAccessiblity.cpp
|
|
|
|
#include <Carbon/Carbon.h>
|
|
|
|
#include "stdwx.h"
|
|
#include "BOINCGUIApp.h"
|
|
#include "BOINCBaseFrame.h"
|
|
#include "BOINCBaseView.h"
|
|
#include "DlgEventLog.h"
|
|
#include "MainDocument.h"
|
|
#include "AdvancedFrame.h"
|
|
#include "BOINCListCtrl.h"
|
|
#include "DlgEventLogListCtrl.h"
|
|
#include "ProjectListCtrl.h"
|
|
#include "ViewStatistics.h"
|
|
#include "wxPieCtrl.h"
|
|
#include "sg_BoincSimpleGUI.h"
|
|
#include "Events.h"
|
|
#include "macAccessiblity.h"
|
|
|
|
#define MAX_LIST_COL 100
|
|
|
|
void AccessibilityIgnoreAllChildren(HIViewRef parent, int recursionLevel) {
|
|
HIViewRef child;
|
|
OSStatus err;
|
|
|
|
if (recursionLevel > 100) {
|
|
fprintf(stderr, "Error: AccessibilityIgnoreAllChildren recursion level > 100\n");
|
|
return;
|
|
}
|
|
|
|
child = HIViewGetFirstSubview(parent);
|
|
while (child) {
|
|
err = HIObjectSetAccessibilityIgnored((HIObjectRef)child, true);
|
|
AccessibilityIgnoreAllChildren(child, recursionLevel + 1);
|
|
child = HIViewGetNextView(child);
|
|
}
|
|
}
|
|
|
|
|
|
OSStatus BOINCListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData, Boolean isHeader);
|
|
|
|
pascal OSStatus BOINCListHeaderAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData) {
|
|
return BOINCListAccessibilityEventHandler(inHandlerCallRef, inEvent, pData, true);
|
|
}
|
|
|
|
pascal OSStatus BOINCListBodyAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData) {
|
|
return BOINCListAccessibilityEventHandler(inHandlerCallRef, inEvent, pData, false);
|
|
}
|
|
|
|
pascal OSStatus AttachListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData);
|
|
|
|
|
|
static EventTypeSpec myAccessibilityEvents[] = {
|
|
{ kEventClassAccessibility, kEventAccessibleGetChildAtPoint },
|
|
{ kEventClassAccessibility, kEventAccessibleGetFocusedChild },
|
|
{ kEventClassAccessibility, kEventAccessibleGetAllAttributeNames },
|
|
{ kEventClassAccessibility, kEventAccessibleGetAllParameterizedAttributeNames },
|
|
{ kEventClassAccessibility, kEventAccessibleIsNamedAttributeSettable },
|
|
{ kEventClassAccessibility, kEventAccessibleSetNamedAttribute },
|
|
{ kEventClassAccessibility, kEventAccessibleGetNamedAttribute },
|
|
{ kEventClassAccessibility, kEventAccessibleGetAllActionNames },
|
|
{ kEventClassAccessibility, kEventAccessibleGetNamedActionDescription },
|
|
{ kEventClassAccessibility, kEventAccessiblePerformNamedAction }
|
|
};
|
|
|
|
|
|
static EventTypeSpec Simple_AccessibilityEvents[] = {
|
|
{ kEventClassAccessibility, kEventAccessibleGetNamedAttribute }
|
|
};
|
|
|
|
pascal OSStatus SimpleAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData);
|
|
|
|
pascal OSStatus PieCtrlAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData);
|
|
|
|
|
|
|
|
void CBOINCListCtrl::SetupMacAccessibilitySupport() {
|
|
#if !USE_NATIVE_LISTCONTROL
|
|
HIViewRef listControlView;
|
|
HIViewRef headerView;
|
|
HIViewRef bodyView;
|
|
OSErr err;
|
|
|
|
listControlView = (HIViewRef)GetHandle();
|
|
headerView = HIViewGetFirstSubview(listControlView);
|
|
bodyView = HIViewGetNextView(headerView);
|
|
err = HIViewSetEnabled(headerView, true);
|
|
|
|
accessibilityHandlerData.pList = (wxGenericListCtrl*)this;
|
|
accessibilityHandlerData.pView = m_pParentView;
|
|
accessibilityHandlerData.headerView = headerView;
|
|
accessibilityHandlerData.bodyView = bodyView;
|
|
accessibilityHandlerData.pEventLog = NULL;
|
|
|
|
err = InstallHIObjectEventHandler((HIObjectRef)headerView, NewEventHandlerUPP(BOINCListHeaderAccessibilityEventHandler),
|
|
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
|
|
&accessibilityHandlerData, &m_pHeaderAccessibilityEventHandlerRef);
|
|
|
|
err = InstallHIObjectEventHandler((HIObjectRef)bodyView, NewEventHandlerUPP(BOINCListBodyAccessibilityEventHandler),
|
|
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
|
|
&accessibilityHandlerData, &m_pBodyAccessibilityEventHandlerRef);
|
|
#endif
|
|
}
|
|
|
|
|
|
void CBOINCListCtrl::RemoveMacAccessibilitySupport() {
|
|
#if !USE_NATIVE_LISTCONTROL
|
|
::RemoveEventHandler(m_pHeaderAccessibilityEventHandlerRef);
|
|
::RemoveEventHandler(m_pBodyAccessibilityEventHandlerRef);
|
|
#endif
|
|
}
|
|
|
|
|
|
void CDlgEventLogListCtrl::SetupMacAccessibilitySupport() {
|
|
#if !USE_NATIVE_LISTCONTROL
|
|
HIViewRef listControlView;
|
|
HIViewRef headerView;
|
|
HIViewRef bodyView;
|
|
OSErr err;
|
|
|
|
listControlView = (HIViewRef)GetHandle();
|
|
headerView = HIViewGetFirstSubview(listControlView);
|
|
bodyView = HIViewGetNextView(headerView);
|
|
err = HIViewSetEnabled(headerView, true);
|
|
|
|
accessibilityHandlerData.pList = (wxGenericListCtrl*)this;
|
|
accessibilityHandlerData.pView = NULL;
|
|
accessibilityHandlerData.headerView = headerView;
|
|
accessibilityHandlerData.bodyView = bodyView;
|
|
accessibilityHandlerData.pEventLog = m_pParentView;
|
|
|
|
err = InstallHIObjectEventHandler((HIObjectRef)headerView, NewEventHandlerUPP(BOINCListHeaderAccessibilityEventHandler),
|
|
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
|
|
&accessibilityHandlerData, &m_pHeaderAccessibilityEventHandlerRef);
|
|
|
|
err = InstallHIObjectEventHandler((HIObjectRef)bodyView, NewEventHandlerUPP(BOINCListBodyAccessibilityEventHandler),
|
|
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
|
|
&accessibilityHandlerData, &m_pBodyAccessibilityEventHandlerRef);
|
|
#endif
|
|
}
|
|
|
|
|
|
void CDlgEventLogListCtrl::RemoveMacAccessibilitySupport() {
|
|
#if !USE_NATIVE_LISTCONTROL
|
|
::RemoveEventHandler(m_pHeaderAccessibilityEventHandlerRef);
|
|
::RemoveEventHandler(m_pBodyAccessibilityEventHandlerRef);
|
|
#endif
|
|
}
|
|
|
|
|
|
void CProjectListCtrlAccessible::SetupMacAccessibilitySupport() {
|
|
OSErr err;
|
|
|
|
CProjectListCtrl* pCtrl = wxDynamicCast(mp_win, CProjectListCtrl);
|
|
wxASSERT(pCtrl);
|
|
|
|
if (pCtrl)
|
|
{
|
|
m_listView = (HIViewRef)pCtrl->GetHandle();
|
|
err = HIViewSetEnabled(m_listView, true);
|
|
|
|
err = InstallHIObjectEventHandler((HIObjectRef)m_listView, NewEventHandlerUPP(AttachListAccessibilityEventHandler),
|
|
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
|
|
this, &m_plistAccessibilityEventHandlerRef);
|
|
} else {
|
|
m_plistAccessibilityEventHandlerRef = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void CProjectListCtrlAccessible::RemoveMacAccessibilitySupport() {
|
|
if (m_plistAccessibilityEventHandlerRef) {
|
|
::RemoveEventHandler(m_plistAccessibilityEventHandlerRef);
|
|
m_plistAccessibilityEventHandlerRef = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void CSimplePanel::SetupMacAccessibilitySupport() {
|
|
OSStatus err;
|
|
wxString str = _("for accessibility support, please select advanced from the view menu or type command shift a");
|
|
HIViewRef simple = (HIViewRef)GetHandle();
|
|
CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
|
|
// Have the screen reader tell user to switch to advanced view.
|
|
HIObjectSetAuxiliaryAccessibilityAttribute((HIObjectRef)simple, 0, kAXDescriptionAttribute, description);
|
|
CFRelease( description );
|
|
|
|
err = InstallHIObjectEventHandler((HIObjectRef)simple, NewEventHandlerUPP(SimpleAccessibilityEventHandler),
|
|
sizeof(Simple_AccessibilityEvents) / sizeof(EventTypeSpec), Simple_AccessibilityEvents,
|
|
this, &m_pSGAccessibilityEventHandlerRef);
|
|
}
|
|
|
|
|
|
void CSimplePanel::RemoveMacAccessibilitySupport() {
|
|
if (m_pSGAccessibilityEventHandlerRef) {
|
|
::RemoveEventHandler(m_pSGAccessibilityEventHandlerRef);
|
|
m_pSGAccessibilityEventHandlerRef = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void CTaskItemGroup::SetupMacAccessibilitySupport() {
|
|
OSStatus err;
|
|
HIViewRef boxView = (HIViewRef)m_pStaticBox->GetHandle();
|
|
|
|
if (m_pTaskGroupAccessibilityEventHandlerRef == NULL) {
|
|
err = InstallHIObjectEventHandler((HIObjectRef)boxView, NewEventHandlerUPP(SimpleAccessibilityEventHandler),
|
|
sizeof(Simple_AccessibilityEvents) / sizeof(EventTypeSpec), Simple_AccessibilityEvents,
|
|
this, &m_pTaskGroupAccessibilityEventHandlerRef);
|
|
}
|
|
}
|
|
|
|
|
|
void CTaskItemGroup::RemoveMacAccessibilitySupport() {
|
|
if (m_pTaskGroupAccessibilityEventHandlerRef) {
|
|
::RemoveEventHandler(m_pTaskGroupAccessibilityEventHandlerRef);
|
|
m_pTaskGroupAccessibilityEventHandlerRef = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
void CViewStatistics::SetupMacAccessibilitySupport() {
|
|
OSStatus err;
|
|
HIViewRef paintPanelView = (HIViewRef)m_PaintStatistics->GetHandle();
|
|
wxString str = _("This panel contains graphs showing user totals for projects");
|
|
CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
|
|
// Have the screen reader tell user to switch to advanced view.
|
|
HIObjectSetAuxiliaryAccessibilityAttribute((HIObjectRef)paintPanelView, 0, kAXDescriptionAttribute, description);
|
|
CFRelease( description );
|
|
|
|
err = InstallHIObjectEventHandler((HIObjectRef)paintPanelView, NewEventHandlerUPP(SimpleAccessibilityEventHandler),
|
|
sizeof(Simple_AccessibilityEvents) / sizeof(EventTypeSpec), Simple_AccessibilityEvents,
|
|
this, &m_pStatisticsAccessibilityEventHandlerRef);
|
|
}
|
|
|
|
|
|
void CViewStatistics::RemoveMacAccessibilitySupport() {
|
|
::RemoveEventHandler(m_pStatisticsAccessibilityEventHandlerRef);
|
|
}
|
|
|
|
|
|
void wxPieCtrl::SetupMacAccessibilitySupport() {
|
|
OSStatus err;
|
|
HIViewRef pieControlView = (HIViewRef)GetHandle();
|
|
|
|
HIObjectSetAuxiliaryAccessibilityAttribute((HIObjectRef)pieControlView, 0, kAXDescriptionAttribute, CFSTR(""));
|
|
|
|
err = InstallHIObjectEventHandler((HIObjectRef)pieControlView, NewEventHandlerUPP(PieCtrlAccessibilityEventHandler),
|
|
sizeof(Simple_AccessibilityEvents) / sizeof(EventTypeSpec), Simple_AccessibilityEvents,
|
|
this, &m_pPieCtrlAccessibilityEventHandlerRef);
|
|
}
|
|
|
|
|
|
void wxPieCtrl::RemoveMacAccessibilitySupport() {
|
|
if (m_pPieCtrlAccessibilityEventHandlerRef) {
|
|
::RemoveEventHandler(m_pPieCtrlAccessibilityEventHandlerRef);
|
|
m_pPieCtrlAccessibilityEventHandlerRef = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
OSStatus BOINCListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData, Boolean isHeader) {
|
|
const UInt32 eventClass = GetEventClass(inEvent);
|
|
const UInt32 eventKind = GetEventKind(inEvent);
|
|
OSStatus err;
|
|
wxGenericListCtrl* pList = ((struct ListAccessData*)pData)->pList;
|
|
CBOINCBaseView* pView = ((struct ListAccessData*)pData)->pView;
|
|
HIViewRef headerView = ((struct ListAccessData*)pData)->headerView;
|
|
HIViewRef bodyView = ((struct ListAccessData*)pData)->bodyView;
|
|
CDlgEventLog* pEventLog = ((struct ListAccessData*)pData)->pEventLog;
|
|
|
|
if (eventClass != kEventClassAccessibility) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
AXUIElementRef element;
|
|
UInt64 inIdentifier = 0;
|
|
UInt64 outIdentifier = 0;
|
|
SInt32 row = 0;
|
|
SInt32 col = 0;
|
|
HIObjectRef obj = NULL;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleObject,
|
|
typeCFTypeRef, NULL, sizeof(typeCFTypeRef), NULL, &element);
|
|
if (err) return err;
|
|
|
|
AXUIElementGetIdentifier( element, &inIdentifier );
|
|
obj = AXUIElementGetHIObject(element);
|
|
|
|
if (inIdentifier) {
|
|
if (! isHeader) {
|
|
row = (inIdentifier / MAX_LIST_COL) - 1 + pList->GetTopItem();
|
|
}
|
|
col = (inIdentifier % MAX_LIST_COL) - 1;
|
|
}
|
|
|
|
switch (eventKind) {
|
|
case kEventAccessibleGetChildAtPoint:
|
|
{
|
|
CFTypeRef child = NULL;
|
|
HIPoint where;
|
|
long theRow = wxNOT_FOUND;
|
|
long ignored;
|
|
int hitflags;
|
|
|
|
// Only the whole view can be tested since the parts don't have sub-parts.
|
|
if (inIdentifier != 0) {
|
|
return noErr;
|
|
}
|
|
|
|
err = GetEventParameter (inEvent, kEventParamMouseLocation,
|
|
typeHIPoint, NULL, sizeof(HIPoint), NULL, &where);
|
|
if (err) return err;
|
|
|
|
wxPoint p((int)where.x, (int)where.y);
|
|
pList->ScreenToClient(&p.x, &p.y);
|
|
|
|
// HitTest returns the column only on wxMSW
|
|
int x = 0, n = pList->GetColumnCount();
|
|
for (col=0; col<n; col++) {
|
|
x += pList->GetColumnWidth(col);
|
|
if (p.x < x) break;
|
|
}
|
|
|
|
if (col >= n) {
|
|
return noErr;
|
|
}
|
|
|
|
if (isHeader) {
|
|
if ((p.y > 0) || (p.y < -pList->m_headerHeight)) {
|
|
return noErr;
|
|
}
|
|
outIdentifier = col+1;
|
|
} else {
|
|
theRow = pList->HitTest(p, hitflags, &ignored);
|
|
if (theRow == wxNOT_FOUND) {
|
|
return noErr;
|
|
}
|
|
outIdentifier = ((theRow + 1 - pList->GetTopItem()) * MAX_LIST_COL) + col + 1;
|
|
}
|
|
|
|
child = AXUIElementCreateWithHIObjectAndIdentifier(obj, outIdentifier );
|
|
if (child == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
err = SetEventParameter (inEvent, kEventParamAccessibleChild, typeCFTypeRef,
|
|
sizeof(typeCFTypeRef), &child);
|
|
if (err) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetFocusedChild:
|
|
return noErr;
|
|
break;
|
|
|
|
case kEventAccessibleGetAllAttributeNames:
|
|
{
|
|
CFMutableArrayRef namesArray;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames,
|
|
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray);
|
|
if (err)
|
|
return err;
|
|
|
|
CallNextEventHandler( inHandlerCallRef, inEvent );
|
|
|
|
if ( inIdentifier == 0 )
|
|
{
|
|
// Identifier 0 means "the whole view".
|
|
// Let accessibility know that this view has children and can
|
|
// return a list of them.
|
|
CFArrayAppendValue( namesArray, kAXChildrenAttribute );
|
|
} else {
|
|
// Let accessibility know that this view's children can return description,
|
|
// size, position, parent window, top level element and isFocused attributes.
|
|
CFArrayAppendValue( namesArray, kAXWindowAttribute );
|
|
CFArrayAppendValue( namesArray, kAXTopLevelUIElementAttribute );
|
|
CFArrayAppendValue( namesArray, kAXSizeAttribute );
|
|
CFArrayAppendValue( namesArray, kAXPositionAttribute );
|
|
if (isHeader) {
|
|
CFArrayAppendValue( namesArray, kAXTitleAttribute );
|
|
}
|
|
CFArrayAppendValue( namesArray, kAXEnabledAttribute );
|
|
}
|
|
|
|
CFArrayAppendValue( namesArray, kAXFocusedAttribute );
|
|
CFArrayAppendValue( namesArray, kAXRoleAttribute );
|
|
CFArrayAppendValue( namesArray, kAXRoleDescriptionAttribute );
|
|
CFArrayAppendValue( namesArray, kAXDescriptionAttribute );
|
|
CFArrayAppendValue( namesArray, kAXParentAttribute );
|
|
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetAllParameterizedAttributeNames:
|
|
{
|
|
CFMutableArrayRef namesArray;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames,
|
|
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray);
|
|
if (err) return err;
|
|
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetNamedAttribute:
|
|
{
|
|
CFStringRef attribute;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute);
|
|
if (err) return err;
|
|
|
|
if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return whether or not this part is focused.
|
|
//TODO: Add kAXFocusedAttribute support?
|
|
Boolean focused = false;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused );
|
|
return noErr;
|
|
}
|
|
|
|
if ( inIdentifier == 0 ) {
|
|
// String compare the incoming attribute name and return the appropriate accessibility
|
|
// information as an event parameter.
|
|
|
|
if ( CFStringCompare( attribute, kAXChildrenAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Create and return an array of AXUIElements describing the children of this view.
|
|
CFMutableArrayRef children;
|
|
AXUIElementRef child;
|
|
int c, n = pList->GetColumnCount();
|
|
|
|
if (isHeader) {
|
|
children = CFArrayCreateMutable( kCFAllocatorDefault, n, &kCFTypeArrayCallBacks );
|
|
|
|
for ( c = 0; c < n; c++ ) {
|
|
// Header item for each column
|
|
outIdentifier = c+1;
|
|
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
|
|
CFArrayAppendValue( children, child );
|
|
CFRelease( child );
|
|
}
|
|
} else { // ! isHeader
|
|
int r, m = pList->GetCountPerPage();
|
|
|
|
children = CFArrayCreateMutable( kCFAllocatorDefault, (m + 1) * n, &kCFTypeArrayCallBacks );
|
|
|
|
for ( r = 0; r < m; r++ ) {
|
|
// For each row
|
|
for ( c = 0; c < n; c++ ) {
|
|
// For each column
|
|
outIdentifier = ((r + 1) * MAX_LIST_COL) + c + 1;
|
|
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
|
|
CFArrayAppendValue( children, child );
|
|
CFRelease( child );
|
|
}
|
|
}
|
|
}
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
|
|
CFRelease( children );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string indicating the role of this view. Using the table role doesn't work.
|
|
CFStringRef role = isHeader? kAXColumnRole : kAXListRole;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string indicating the role of this part.
|
|
|
|
CFStringRef roleDesc = CFSTR("");
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc );
|
|
CFRelease( roleDesc );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string indicating the role of this part.
|
|
wxString str;
|
|
|
|
if (isHeader) {
|
|
str = _("blank");
|
|
} else {
|
|
str = _("list of ");
|
|
if (pEventLog) {
|
|
str += _("events");
|
|
} else {
|
|
if (pView) {
|
|
str += pView->GetViewDisplayName();
|
|
}
|
|
}
|
|
if (pList->GetItemCount() <= 0) {
|
|
str += _(" is empty");
|
|
}
|
|
}
|
|
|
|
CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description );
|
|
CFRelease( description );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
AXUIElementRef parent;
|
|
HIViewRef parentView;
|
|
|
|
parentView = HIViewGetSuperview(isHeader ? headerView : bodyView);
|
|
parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0);
|
|
if (parent == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent );
|
|
CFRelease( parent );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXSizeAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
HIRect r;
|
|
|
|
err = HIViewGetBounds(isHeader ? headerView : bodyView, &r);
|
|
HISize size = r.size;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeHISize, sizeof( HISize ), &size );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXPositionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
HIRect r;
|
|
HIPoint pt;
|
|
|
|
err = HIViewGetBounds(isHeader ? headerView : bodyView, &r);
|
|
int x = r.origin.x, y = r.origin.y;
|
|
|
|
// Now convert to global coordinates
|
|
pList->ClientToScreen(&x, &y);
|
|
pt.x = x;
|
|
if (isHeader) {
|
|
pt.y = y - pList->m_headerHeight;
|
|
} else {
|
|
pt.y = y;
|
|
}
|
|
|
|
SetEventParameter(inEvent, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(HIPoint), &pt);
|
|
return noErr;
|
|
|
|
} else {
|
|
return CallNextEventHandler( inHandlerCallRef, inEvent );
|
|
|
|
}
|
|
|
|
} else { // End if ( inIdentifier == 0 )
|
|
|
|
if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
wxString str, buf;
|
|
int rowCount;
|
|
Boolean isCurrentSortCol = false;
|
|
|
|
if (isHeader) {
|
|
wxListItem headerItem;
|
|
|
|
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
|
|
if (!pFrame) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
pList->GetColumn(col, headerItem);
|
|
buf.Printf(_("%d of %d; "), col+1, pList->GetColumnCount());
|
|
if (pView) {
|
|
if (col == pView->m_iSortColumn) {
|
|
isCurrentSortCol = true;
|
|
}
|
|
}
|
|
if (isCurrentSortCol) {
|
|
str = _("current sort column ");
|
|
str += buf;
|
|
str += (pView->m_bReverseSort ? _(" descending order ") : _(" ascending order "));
|
|
} else {
|
|
str = _("column ");
|
|
str += buf;
|
|
}
|
|
str += headerItem.GetText();
|
|
} else { // ! isHeader
|
|
rowCount = pList->GetItemCount();
|
|
if (rowCount <= 0) {
|
|
str = _("list is empty");
|
|
} else {
|
|
if (pList->GetItemState(row, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED) {
|
|
str = _("selected ");
|
|
} else {
|
|
str = wxEmptyString;
|
|
}
|
|
|
|
buf.Printf(_("row %d "), row+1);
|
|
str += buf;
|
|
if (col == 0) {
|
|
buf.Printf(_("of %d; "), rowCount);
|
|
str += buf;
|
|
}
|
|
buf.Printf(_("column %d; "), col+1);
|
|
str += buf;
|
|
if (pEventLog) {
|
|
buf = pEventLog->OnListGetItemText(row, col);
|
|
} else {
|
|
buf = pView->FireOnListGetItemText(row, col);
|
|
}
|
|
if (buf.IsEmpty()) {
|
|
buf = _("blank");
|
|
}
|
|
str += buf;
|
|
}
|
|
}
|
|
CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description );
|
|
CFRelease( description );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
AXUIElementRef parent;
|
|
HIViewRef parentView;
|
|
|
|
parentView = isHeader ? headerView : bodyView;
|
|
parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0);
|
|
if (parent == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent );
|
|
CFRelease( parent );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXSubroleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
CFStringRef subRole;
|
|
int currentTabView;
|
|
|
|
if (! isHeader) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
|
|
if (!pFrame) {
|
|
return eventNotHandledErr;
|
|
}
|
|
currentTabView = pFrame->GetCurrentViewPage();
|
|
|
|
if (currentTabView & (VW_PROJ | VW_TASK |VW_XFER)) {
|
|
subRole = kAXSortButtonSubrole;
|
|
} else {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( subRole ), &subRole );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string indicating the role of this part. The parts of the view behave like
|
|
// buttons, so use that system role.
|
|
|
|
CFStringRef role;
|
|
int currentTabView;
|
|
|
|
if (isHeader) {
|
|
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
|
|
if (!pFrame) {
|
|
return eventNotHandledErr;
|
|
}
|
|
currentTabView = pFrame->GetCurrentViewPage();
|
|
|
|
if (currentTabView & (VW_PROJ | VW_TASK |VW_XFER)) {
|
|
role = kAXButtonRole;
|
|
} else {
|
|
role = kAXColumnRole;
|
|
}
|
|
} else { // ! isHeader
|
|
role = kAXStaticTextRole;
|
|
}
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string describing the role of this part. Use the system description.
|
|
CFStringRef roleDesc;
|
|
int currentTabView;
|
|
|
|
if (isHeader) {
|
|
CBOINCBaseFrame* pFrame = wxGetApp().GetFrame();
|
|
if (!pFrame) {
|
|
return eventNotHandledErr;
|
|
}
|
|
currentTabView = pFrame->GetCurrentViewPage();
|
|
|
|
if (currentTabView & (VW_PROJ | VW_TASK |VW_XFER)) {
|
|
roleDesc = HICopyAccessibilityRoleDescription( kAXButtonRole, kAXSortButtonSubrole );
|
|
} else {
|
|
roleDesc = CFStringCreateCopy(NULL, CFSTR(""));
|
|
}
|
|
} else { // ! isHeader
|
|
roleDesc = HICopyAccessibilityRoleDescription( kAXStaticTextRole, NULL );
|
|
}
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc );
|
|
CFRelease( roleDesc );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXSizeAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
HISize size;
|
|
wxRect r;
|
|
|
|
// Return the size of this part as an HISize.
|
|
size.width = pList->GetColumnWidth(col);
|
|
if (isHeader) {
|
|
size.height = pList->m_headerHeight;
|
|
} else { // ! isHeader
|
|
pList->GetItemRect(row, r);
|
|
size.height = r.height;
|
|
}
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeHISize, sizeof( HISize ), &size );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXPositionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
HIPoint pt;
|
|
wxRect r;
|
|
int i, x = 0, y = 0;
|
|
|
|
// Return the position of this part as an HIPoint.
|
|
// First get the position relative to the ListCtrl
|
|
for (i=0; i< col; i++) {
|
|
x += pList->GetColumnWidth(i);
|
|
}
|
|
|
|
if (!isHeader) {
|
|
pList->GetItemRect(row, r);
|
|
y = r.y - 1;
|
|
}
|
|
// Now convert to global coordinates
|
|
pList->ClientToScreen(&x, &y);
|
|
pt.x = x;
|
|
pt.y = y - pList->m_headerHeight;
|
|
|
|
SetEventParameter(inEvent, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(HIPoint), &pt);
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXWindowAttribute, 0 ) == kCFCompareEqualTo
|
|
|| CFStringCompare( attribute, kAXTopLevelUIElementAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return the window or top level ui element for this part. They are both the same so re-use the code.
|
|
AXUIElementRef windOrTopUI;
|
|
|
|
WindowRef win = GetControlOwner(bodyView);
|
|
if (win == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
windOrTopUI = AXUIElementCreateWithHIObjectAndIdentifier( (HIObjectRef)win, 0 );
|
|
if (windOrTopUI == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( windOrTopUI ), &windOrTopUI );
|
|
CFRelease( windOrTopUI );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXEnabledAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
Boolean enabled = true;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( enabled ), &enabled );
|
|
return noErr;
|
|
|
|
#if 0
|
|
} else if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return whether or not this part is focused.
|
|
//TODO: Add kAXFocusedAttribute support?
|
|
Boolean focused = false;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused );
|
|
return noErr;
|
|
#endif
|
|
|
|
} else if ( CFStringCompare( attribute, kAXTitleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return the item's text
|
|
wxString str;
|
|
wxListItem headerItem;
|
|
|
|
if (!isHeader) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
pList->GetColumn(col, headerItem);
|
|
str = headerItem.GetText();
|
|
|
|
CFStringRef title = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( title ), &title );
|
|
CFRelease( title );
|
|
return noErr;
|
|
|
|
} else {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
} // End if ( inIdentifier != 0 )
|
|
break;
|
|
} // End case kEventAccessibleGetNamedAttribute:
|
|
|
|
|
|
case kEventAccessibleIsNamedAttributeSettable:
|
|
{
|
|
CFStringRef attribute;
|
|
Boolean isSettable = false;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute);
|
|
if (err) return err;
|
|
|
|
// The focused attribute is the only settable attribute for this view,
|
|
// and it can only be set on part (or subelements), not the whole view.
|
|
if (inIdentifier != 0)
|
|
{
|
|
if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo )
|
|
{
|
|
isSettable = true;
|
|
}
|
|
}
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeSettable, typeBoolean, sizeof( Boolean ), &isSettable );
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleSetNamedAttribute:
|
|
{
|
|
return eventNotHandledErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetAllActionNames:
|
|
{
|
|
CFMutableArrayRef array;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleActionNames,
|
|
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &array);
|
|
if (err) return err;
|
|
|
|
if (inIdentifier != 0) {
|
|
CFArrayAppendValue( array, kAXPressAction );
|
|
}
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetNamedActionDescription:
|
|
{
|
|
CFStringRef action;
|
|
CFMutableStringRef desc;
|
|
CFStringRef selfDesc = NULL;
|
|
|
|
if (inIdentifier == 0) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleActionName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action);
|
|
if (err) return err;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleActionDescription,
|
|
typeCFMutableStringRef, NULL, sizeof(typeCFMutableStringRef), NULL, &desc);
|
|
if (err) return err;
|
|
|
|
selfDesc = HICopyAccessibilityActionDescription( action );
|
|
|
|
CFStringReplaceAll( desc, selfDesc );
|
|
CFRelease( selfDesc );
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessiblePerformNamedAction:
|
|
{
|
|
CFStringRef action;
|
|
wxWindowID id = pList->GetId();
|
|
|
|
if (inIdentifier == 0) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleActionName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action);
|
|
if (err) return err;
|
|
|
|
if ( CFStringCompare( action, kAXPressAction, 0 ) != kCFCompareEqualTo ) {
|
|
return eventNotHandledErr;
|
|
}
|
|
if (isHeader) {
|
|
wxListEvent event(wxEVT_COMMAND_LIST_COL_CLICK, id);
|
|
event.m_col = col;
|
|
pList->AddPendingEvent(event);
|
|
} else {
|
|
if (pView) {
|
|
pView->ClearSelections();
|
|
}
|
|
pList->SetItemState(row, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
|
}
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return eventNotHandledErr;
|
|
} // End switch(eventKind)
|
|
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
|
|
pascal OSStatus AttachListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData) {
|
|
const UInt32 eventClass = GetEventClass(inEvent);
|
|
const UInt32 eventKind = GetEventKind(inEvent);
|
|
OSStatus err;
|
|
|
|
CProjectListCtrlAccessible* pAccessible = (CProjectListCtrlAccessible*)pData;
|
|
if (pAccessible == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
CProjectListCtrl* pCtrl = wxDynamicCast(pAccessible->GetWindow(), CProjectListCtrl);
|
|
if (pCtrl == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
if (eventClass != kEventClassAccessibility) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
AXUIElementRef element;
|
|
UInt64 inIdentifier = 0;
|
|
UInt64 outIdentifier = 0;
|
|
SInt32 row = 0;
|
|
HIObjectRef obj = NULL;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleObject,
|
|
typeCFTypeRef, NULL, sizeof(typeCFTypeRef), NULL, &element);
|
|
if (err) return err;
|
|
|
|
AXUIElementGetIdentifier( element, &inIdentifier );
|
|
obj = AXUIElementGetHIObject(element);
|
|
|
|
row = inIdentifier;
|
|
|
|
switch (eventKind) {
|
|
case kEventAccessibleGetChildAtPoint:
|
|
{
|
|
CFTypeRef child = NULL;
|
|
HIPoint where;
|
|
int hitRow;
|
|
|
|
// Only the whole view can be tested since the parts don't have sub-parts.
|
|
if (inIdentifier != 0) {
|
|
return noErr;
|
|
}
|
|
|
|
err = GetEventParameter (inEvent, kEventParamMouseLocation,
|
|
typeHIPoint, NULL, sizeof(HIPoint), NULL, &where);
|
|
if (err) return err;
|
|
|
|
wxPoint p((int)where.x, (int)where.y);
|
|
pCtrl->ScreenToClient(&p.x, &p.y);
|
|
|
|
err = pAccessible->HitTest(p, &hitRow, NULL);
|
|
|
|
if (err) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
if (hitRow >= 0) {
|
|
|
|
//TODO: Check with Rom why this starts at 0 not 1
|
|
outIdentifier = hitRow + 1;
|
|
child = AXUIElementCreateWithHIObjectAndIdentifier(obj, outIdentifier );
|
|
if (child == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
err = SetEventParameter (inEvent, kEventParamAccessibleChild, typeCFTypeRef,
|
|
sizeof(typeCFTypeRef), &child);
|
|
if (err) {
|
|
return eventNotHandledErr;
|
|
}
|
|
}
|
|
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetFocusedChild:
|
|
return noErr;
|
|
break;
|
|
|
|
case kEventAccessibleGetAllAttributeNames:
|
|
{
|
|
CFMutableArrayRef namesArray;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames,
|
|
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray);
|
|
if (err)
|
|
return err;
|
|
|
|
CallNextEventHandler( inHandlerCallRef, inEvent );
|
|
|
|
if ( inIdentifier == 0 )
|
|
{
|
|
// Identifier 0 means "the whole view".
|
|
// Let accessibility know that this view has children and can
|
|
// return a list of them.
|
|
CFArrayAppendValue( namesArray, kAXChildrenAttribute );
|
|
} else {
|
|
// Let accessibility know that this view's children can return description,
|
|
// size, position, parent window, top level element and isFocused attributes.
|
|
CFArrayAppendValue( namesArray, kAXWindowAttribute );
|
|
CFArrayAppendValue( namesArray, kAXTopLevelUIElementAttribute );
|
|
CFArrayAppendValue( namesArray, kAXDescriptionAttribute );
|
|
CFArrayAppendValue( namesArray, kAXSizeAttribute );
|
|
CFArrayAppendValue( namesArray, kAXPositionAttribute );
|
|
CFArrayAppendValue( namesArray, kAXTitleAttribute );
|
|
CFArrayAppendValue( namesArray, kAXEnabledAttribute );
|
|
}
|
|
|
|
CFArrayAppendValue( namesArray, kAXFocusedAttribute );
|
|
CFArrayAppendValue( namesArray, kAXRoleAttribute );
|
|
CFArrayAppendValue( namesArray, kAXRoleDescriptionAttribute );
|
|
CFArrayAppendValue( namesArray, kAXParentAttribute );
|
|
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetAllParameterizedAttributeNames:
|
|
{
|
|
CFMutableArrayRef namesArray;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames,
|
|
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray);
|
|
if (err) return err;
|
|
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetNamedAttribute:
|
|
{
|
|
CFStringRef attribute;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute);
|
|
if (err) return err;
|
|
|
|
if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return whether or not this part is focused.
|
|
//TODO: Add kAXFocusedAttribute support?
|
|
Boolean focused = false;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused );
|
|
return noErr;
|
|
} else if ( CFStringCompare( attribute, kAXSizeAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
wxRect r;
|
|
HISize theSize;
|
|
|
|
err = pAccessible->GetLocation(r, row);
|
|
if (err) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
theSize.width = r.width;
|
|
theSize.height = r.height;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeHISize, sizeof( HISize ), &theSize );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXPositionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
wxRect r;
|
|
HIPoint pt;
|
|
int x, y;
|
|
|
|
err = pAccessible->GetLocation(r, row);
|
|
if (err) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
x = r.x;
|
|
y = r.y;
|
|
|
|
// Now convert to global coordinates
|
|
pCtrl->ClientToScreen(&x, &y);
|
|
pt.x = x;
|
|
pt.y = y;
|
|
|
|
SetEventParameter(inEvent, kEventParamAccessibleAttributeValue, typeHIPoint, sizeof(HIPoint), &pt);
|
|
return noErr;
|
|
}
|
|
|
|
if ( inIdentifier == 0 ) {
|
|
// String compare the incoming attribute name and return the appropriate accessibility
|
|
// information as an event parameter.
|
|
|
|
if ( CFStringCompare( attribute, kAXChildrenAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Create and return an array of AXUIElements describing the children of this view.
|
|
CFMutableArrayRef children;
|
|
AXUIElementRef child;
|
|
int i, n;
|
|
|
|
err = pAccessible->GetChildCount(&n);
|
|
children = CFArrayCreateMutable( kCFAllocatorDefault, n, &kCFTypeArrayCallBacks );
|
|
|
|
for ( i = 0; i < n; i++ ) {
|
|
outIdentifier = i+1;
|
|
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
|
|
CFArrayAppendValue( children, child );
|
|
CFRelease( child );
|
|
}
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
|
|
CFRelease( children );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string indicating the role of this view. Using the table role doesn't work.
|
|
CFStringRef role = kAXListRole;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string indicating the role of this part.
|
|
//TODO: specify whether projects or account managers
|
|
wxString str;
|
|
|
|
str = _("list of projects or account managers");
|
|
// str += pView->GetViewDisplayName();
|
|
CFStringRef roleDesc = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc );
|
|
CFRelease( roleDesc );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
AXUIElementRef parent;
|
|
HIViewRef parentView;
|
|
|
|
parentView = HIViewGetSuperview(pAccessible->m_listView);
|
|
parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0);
|
|
if (parent == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent );
|
|
CFRelease( parent );
|
|
return noErr;
|
|
|
|
} else {
|
|
return CallNextEventHandler( inHandlerCallRef, inEvent );
|
|
|
|
}
|
|
|
|
} else { // End if ( inIdentifier == 0 )
|
|
|
|
if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
wxString str, buf;
|
|
int n;
|
|
|
|
err = pAccessible->GetChildCount(&n);
|
|
if (err) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
if (pCtrl->IsSelected(row - 1)) {
|
|
str = _("selected ");
|
|
} else {
|
|
str = wxEmptyString;
|
|
}
|
|
buf.Printf(_("row %d of %d; "), row, n);
|
|
str += buf;
|
|
|
|
err = pAccessible->GetDescription(row, &buf);
|
|
if (err) {
|
|
return eventNotHandledErr;
|
|
}
|
|
str += buf;
|
|
|
|
CFStringRef description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description );
|
|
CFRelease( description );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
AXUIElementRef parent;
|
|
HIViewRef parentView;
|
|
|
|
parentView = pAccessible->m_listView;
|
|
parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, 0);
|
|
if (parent == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( parent ), &parent );
|
|
CFRelease( parent );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXSubroleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
return eventNotHandledErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string indicating the role of this part. The parts of the view behave like
|
|
// buttons, so use that system role.
|
|
|
|
CFStringRef role = kAXStaticTextRole; // kAXRowRole;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXRoleDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string describing the role of this part. Use the system description.
|
|
CFStringRef roleDesc = HICopyAccessibilityRoleDescription( kAXRowRole, NULL );
|
|
// CFStringRef roleDesc = HICopyAccessibilityRoleDescription( kAXStaticTextRole, NULL );
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc );
|
|
CFRelease( roleDesc );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXWindowAttribute, 0 ) == kCFCompareEqualTo
|
|
|| CFStringCompare( attribute, kAXTopLevelUIElementAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return the window or top level ui element for this part. They are both the same so re-use the code.
|
|
AXUIElementRef windOrTopUI;
|
|
|
|
WindowRef win = GetControlOwner((HIViewRef)obj);
|
|
if (win == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
windOrTopUI = AXUIElementCreateWithHIObjectAndIdentifier( (HIObjectRef)win, 0 );
|
|
if (windOrTopUI == NULL) {
|
|
return eventNotHandledErr;
|
|
}
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( windOrTopUI ), &windOrTopUI );
|
|
CFRelease( windOrTopUI );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXEnabledAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
Boolean enabled = true;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( enabled ), &enabled );
|
|
return noErr;
|
|
|
|
#if 0
|
|
} else if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return whether or not this part is focused.
|
|
//TODO: Add kAXFocusedAttribute support?
|
|
Boolean focused = false;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused );
|
|
return noErr;
|
|
#endif
|
|
|
|
} else if ( CFStringCompare( attribute, kAXTitleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return the item's text
|
|
wxString str;
|
|
wxListItem headerItem;
|
|
|
|
pAccessible->GetName(row, &str);
|
|
|
|
CFStringRef title = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( title ), &title );
|
|
CFRelease( title );
|
|
return noErr;
|
|
|
|
} else {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
} // End if ( inIdentifier != 0 )
|
|
break;
|
|
} // End case kEventAccessibleGetNamedAttribute:
|
|
|
|
|
|
case kEventAccessibleIsNamedAttributeSettable:
|
|
{
|
|
CFStringRef attribute;
|
|
Boolean isSettable = false;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute);
|
|
if (err) return err;
|
|
|
|
// The focused attribute is the only settable attribute for this view,
|
|
// and it can only be set on part (or subelements), not the whole view.
|
|
if (inIdentifier != 0)
|
|
{
|
|
if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo )
|
|
{
|
|
isSettable = true;
|
|
}
|
|
}
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeSettable, typeBoolean, sizeof( Boolean ), &isSettable );
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleSetNamedAttribute:
|
|
{
|
|
return eventNotHandledErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetAllActionNames:
|
|
{
|
|
CFMutableArrayRef array;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleActionNames,
|
|
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &array);
|
|
if (err) return err;
|
|
|
|
if (inIdentifier != 0) {
|
|
CFArrayAppendValue( array, kAXPressAction );
|
|
}
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessibleGetNamedActionDescription:
|
|
{
|
|
CFStringRef action;
|
|
CFMutableStringRef desc;
|
|
CFStringRef selfDesc = NULL;
|
|
|
|
if (inIdentifier == 0) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleActionName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action);
|
|
if (err) return err;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleActionDescription,
|
|
typeCFMutableStringRef, NULL, sizeof(typeCFMutableStringRef), NULL, &desc);
|
|
if (err) return err;
|
|
|
|
selfDesc = HICopyAccessibilityActionDescription( action );
|
|
|
|
CFStringReplaceAll( desc, selfDesc );
|
|
CFRelease( selfDesc );
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
case kEventAccessiblePerformNamedAction:
|
|
{
|
|
CFStringRef action;
|
|
|
|
if (inIdentifier == 0) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleActionName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &action);
|
|
if (err) return err;
|
|
|
|
if ( CFStringCompare( action, kAXPressAction, 0 ) != kCFCompareEqualTo ) {
|
|
return eventNotHandledErr;
|
|
}
|
|
err = pAccessible->DoDefaultAction(inIdentifier);
|
|
if (err) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
return noErr;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return eventNotHandledErr;
|
|
} // End switch(eventKind)
|
|
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
pascal OSStatus SimpleAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData) {
|
|
const UInt32 eventClass = GetEventClass(inEvent);
|
|
const UInt32 eventKind = GetEventKind(inEvent);
|
|
OSStatus err;
|
|
|
|
if (eventClass != kEventClassAccessibility) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
switch (eventKind) {
|
|
|
|
case kEventAccessibleGetNamedAttribute:
|
|
CFStringRef attribute;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute);
|
|
if (err) return err;
|
|
|
|
if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
CFStringRef role = kAXStaticTextRole;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role );
|
|
return noErr;
|
|
}
|
|
|
|
return CallNextEventHandler( inHandlerCallRef, inEvent );
|
|
return eventNotHandledErr;
|
|
break;
|
|
|
|
|
|
default:
|
|
return CallNextEventHandler( inHandlerCallRef, inEvent );
|
|
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
|
|
pascal OSStatus PieCtrlAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
|
|
EventRef inEvent, void* pData) {
|
|
const UInt32 eventClass = GetEventClass(inEvent);
|
|
const UInt32 eventKind = GetEventKind(inEvent);
|
|
OSStatus err;
|
|
wxPieCtrl* pPieCtrl = (wxPieCtrl*)pData;
|
|
|
|
if (eventClass != kEventClassAccessibility) {
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
switch (eventKind) {
|
|
|
|
case kEventAccessibleGetNamedAttribute:
|
|
CFStringRef attribute;
|
|
|
|
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeName,
|
|
typeCFStringRef, NULL, sizeof(typeCFStringRef), NULL, &attribute);
|
|
if (err) return err;
|
|
|
|
if ( CFStringCompare( attribute, kAXRoleAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
CFStringRef role = kAXStaticTextRole;
|
|
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( role ), &role );
|
|
return noErr;
|
|
|
|
} else if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
|
|
// Return a string indicating the role of this part.
|
|
wxString str;
|
|
CFStringRef description;
|
|
unsigned int i;
|
|
|
|
str = pPieCtrl->GetLabel();
|
|
for(i=0; i<pPieCtrl->m_Series.Count(); i++) {
|
|
str += wxT("; ");
|
|
str += pPieCtrl->m_Series[i].GetLabel();
|
|
}
|
|
|
|
description = CFStringCreateWithCString(NULL, str.char_str(), kCFStringEncodingUTF8);
|
|
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( description ), &description );
|
|
CFRelease( description );
|
|
return noErr;
|
|
}
|
|
|
|
return CallNextEventHandler( inHandlerCallRef, inEvent );
|
|
return eventNotHandledErr;
|
|
break;
|
|
|
|
|
|
default:
|
|
return CallNextEventHandler( inHandlerCallRef, inEvent );
|
|
|
|
return eventNotHandledErr;
|
|
}
|
|
|
|
return eventNotHandledErr;
|
|
}
|
|
|