boinc/clientgui/mac/MacAccessiblity.cpp

1903 lines
84 KiB
C++

// 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 "NoticeListCtrl.h"
#include "ViewStatistics.h"
#include "wxPieCtrl.h"
#include "sg_BoincSimpleGUI.h"
#include "Events.h"
#include "macAccessiblity.h"
#define MAX_LIST_COL 100
UInt64 makeElementIdentifier(SInt32 row, SInt32 col, Boolean isHeader) {
UInt64 id = (((row + 1) * MAX_LIST_COL) + (col + 1)) * 2;
if (isHeader) id |= 1;
return id;
}
void parseElementIdentifier(UInt64 inIdentifier, SInt32& row, SInt32&col, Boolean& isHeader) {
isHeader = inIdentifier & 1;
row = (inIdentifier / (MAX_LIST_COL * 2)) - 1;
col = ((inIdentifier / 2) % MAX_LIST_COL) - 1;
}
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);
}
}
pascal OSStatus BOINCListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
EventRef inEvent, void* pData);
pascal OSStatus HTMLListAccessibilityEventHandler( 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);
#if !USE_NATIVE_LISTCONTROL
void CBOINCListCtrl::SetupMacAccessibilitySupport() {
HIViewRef listControlView;
HIViewRef headerView;
HIViewRef bodyView;
SInt32 response;
Boolean snowLeopard;
OSErr err;
err = Gestalt(gestaltSystemVersion, &response);
snowLeopard = (err == noErr) && (response >= 0x1060);
listControlView = (HIViewRef)GetHandle();
headerView = HIViewGetFirstSubview(listControlView);
bodyView = HIViewGetNextView(headerView);
err = HIViewSetEnabled(headerView, true);
accessibilityHandlerData.pList = (wxGenericListCtrl*)this;
accessibilityHandlerData.pView = m_pParentView;
accessibilityHandlerData.pEventLog = NULL;
accessibilityHandlerData.bodyView = bodyView;
accessibilityHandlerData.headerView = headerView;
accessibilityHandlerData.snowLeopard = snowLeopard;
err = InstallHIObjectEventHandler((HIObjectRef)bodyView, NewEventHandlerUPP(BOINCListAccessibilityEventHandler),
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
&accessibilityHandlerData, &m_pBodyAccessibilityEventHandlerRef);
err = InstallHIObjectEventHandler((HIObjectRef)headerView, NewEventHandlerUPP(BOINCListAccessibilityEventHandler),
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
&accessibilityHandlerData, &m_pHeaderAccessibilityEventHandlerRef);
}
void CBOINCListCtrl::RemoveMacAccessibilitySupport() {
::RemoveEventHandler(m_pBodyAccessibilityEventHandlerRef);
::RemoveEventHandler(m_pHeaderAccessibilityEventHandlerRef);
}
#endif
void CDlgEventLogListCtrl::SetupMacAccessibilitySupport() {
#if !USE_NATIVE_LISTCONTROL
HIViewRef listControlView;
HIViewRef headerView;
HIViewRef bodyView;
SInt32 response;
Boolean snowLeopard;
OSErr err;
err = Gestalt(gestaltSystemVersion, &response);
snowLeopard = (err == noErr) && (response >= 0x1060);
listControlView = (HIViewRef)GetHandle();
headerView = HIViewGetFirstSubview(listControlView);
bodyView = HIViewGetNextView(headerView);
err = HIViewSetEnabled(headerView, true);
accessibilityHandlerData.pList = (wxGenericListCtrl*)this;
accessibilityHandlerData.pView = NULL;
accessibilityHandlerData.pEventLog = m_pParentView;
accessibilityHandlerData.bodyView = bodyView;
accessibilityHandlerData.headerView = headerView;
accessibilityHandlerData.snowLeopard = snowLeopard;
err = InstallHIObjectEventHandler((HIObjectRef)bodyView, NewEventHandlerUPP(BOINCListAccessibilityEventHandler),
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
&accessibilityHandlerData, &m_pBodyAccessibilityEventHandlerRef);
err = InstallHIObjectEventHandler((HIObjectRef)headerView, NewEventHandlerUPP(BOINCListAccessibilityEventHandler),
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
&accessibilityHandlerData, &m_pHeaderAccessibilityEventHandlerRef);
#endif
}
void CDlgEventLogListCtrl::RemoveMacAccessibilitySupport() {
#if !USE_NATIVE_LISTCONTROL
::RemoveEventHandler(m_pBodyAccessibilityEventHandlerRef);
::RemoveEventHandler(m_pHeaderAccessibilityEventHandlerRef);
#endif
}
typedef struct {
CProjectListCtrlAccessible* pProjectListCtrlAccessible;
CNoticeListCtrlAccessible* pNoticeListCtrlAccessible;
} HTMLListAccessibilityHandlerData;
void CProjectListCtrlAccessible::SetupMacAccessibilitySupport() {
static HTMLListAccessibilityHandlerData userData;
OSErr err;
CProjectListCtrl* pCtrl = wxDynamicCast(mp_win, CProjectListCtrl);
wxASSERT(pCtrl);
if (pCtrl)
{
m_listView = (HIViewRef)pCtrl->GetHandle();
err = HIViewSetEnabled(m_listView, true);
userData.pProjectListCtrlAccessible = this;
userData.pNoticeListCtrlAccessible = NULL;
err = InstallHIObjectEventHandler((HIObjectRef)m_listView, NewEventHandlerUPP(HTMLListAccessibilityEventHandler),
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
&userData, &m_plistAccessibilityEventHandlerRef);
} else {
m_plistAccessibilityEventHandlerRef = NULL;
}
}
void CProjectListCtrlAccessible::RemoveMacAccessibilitySupport() {
if (m_plistAccessibilityEventHandlerRef) {
::RemoveEventHandler(m_plistAccessibilityEventHandlerRef);
m_plistAccessibilityEventHandlerRef = NULL;
}
}
void CNoticeListCtrlAccessible::SetupMacAccessibilitySupport() {
static HTMLListAccessibilityHandlerData userData;
OSErr err;
CNoticeListCtrl* pCtrl = wxDynamicCast(mp_win, CNoticeListCtrl);
wxASSERT(pCtrl);
if (pCtrl)
{
m_listView = (HIViewRef)pCtrl->GetHandle();
err = HIViewSetEnabled(m_listView, true);
userData.pProjectListCtrlAccessible = NULL;
userData.pNoticeListCtrlAccessible = this;
err = InstallHIObjectEventHandler((HIObjectRef)m_listView, NewEventHandlerUPP(HTMLListAccessibilityEventHandler),
sizeof(myAccessibilityEvents) / sizeof(EventTypeSpec), myAccessibilityEvents,
&userData, &m_plistAccessibilityEventHandlerRef);
} else {
m_plistAccessibilityEventHandlerRef = NULL;
}
}
void CNoticeListCtrlAccessible::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;
}
}
pascal OSStatus BOINCListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
EventRef inEvent, void* pData) {
const UInt32 eventClass = GetEventClass(inEvent);
const UInt32 eventKind = GetEventKind(inEvent);
OSStatus err;
wxGenericListCtrl* pList = ((ListAccessData*)pData)->pList;
CBOINCBaseView* pView = ((ListAccessData*)pData)->pView;
HIViewRef headerView = ((ListAccessData*)pData)->headerView;
HIViewRef bodyView = ((ListAccessData*)pData)->bodyView;
CDlgEventLog* pEventLog = ((ListAccessData*)pData)->pEventLog;
Boolean snowLeopard = ((ListAccessData*)pData)->snowLeopard;
if (eventClass != kEventClassAccessibility) {
return eventNotHandledErr;
}
AXUIElementRef element;
UInt64 inIdentifier = 0;
UInt64 outIdentifier = 0;
SInt32 row = 0;
SInt32 col = 0;
Boolean isHeader;
HIObjectRef obj = NULL;
err = GetEventParameter (inEvent, kEventParamAccessibleObject,
typeCFTypeRef, NULL, sizeof(typeCFTypeRef), NULL, &element);
if (err) return err;
AXUIElementGetIdentifier( element, &inIdentifier );
obj = AXUIElementGetHIObject(element);
parseElementIdentifier(inIdentifier, row, col, isHeader);
if (obj == (HIObjectRef)headerView) {
isHeader = true;
}
switch (eventKind) {
#pragma mark kEventAccessibleGetChildAtPoint
case kEventAccessibleGetChildAtPoint:
{
CFTypeRef child = NULL;
HIPoint where;
long theRow = wxNOT_FOUND;
long ignored;
int hitflags;
int x = 0;
// Only the whole view or rows can be tested since the cells don't have sub-parts.
if (col >= 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);
int xoff = pList->GetScrollPos(wxHORIZONTAL);
if (xoff) {
int ppux, ppuy;
wxScrolledWindow * win = ((CBOINCListCtrl*)pList)->GetMainWin();
win->GetScrollPixelsPerUnit(&ppux, &ppuy);
x -= (xoff * ppux);
}
// HitTest returns the column only on wxMSW
int 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) {
outIdentifier = makeElementIdentifier(-1, col, true);
} else {
theRow = pList->HitTest(p, hitflags, &ignored);
if (theRow == wxNOT_FOUND) {
return noErr;
}
// Child of body is a row
outIdentifier = (theRow + 1) * MAX_LIST_COL;
outIdentifier = makeElementIdentifier(theRow, -1, false);
if (row >= 0) {
// Child of row is a cell
outIdentifier = makeElementIdentifier(theRow, col, false);
}
}
child = AXUIElementCreateWithHIObjectAndIdentifier(obj, outIdentifier );
if (child == NULL) {
return eventNotHandledErr;
}
err = SetEventParameter (inEvent, kEventParamAccessibleChild, typeCFTypeRef,
sizeof(typeCFTypeRef), &child);
if (err) {
return eventNotHandledErr;
}
return noErr;
}
break;
#pragma mark kEventAccessibleGetFocusedChild
case kEventAccessibleGetFocusedChild:
return noErr;
break;
#pragma mark kEventAccessibleGetAllAttributeNames
case kEventAccessibleGetAllAttributeNames:
{
CFMutableArrayRef namesArray;
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames,
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray);
if (err)
return err;
CallNextEventHandler( inHandlerCallRef, inEvent );
if ( (row < 0) && (col < 0) ) { // the whole view
// col < 0 means an entire row.
// Let accessibility know that this view has children and can
// return a list of them.
CFArrayAppendValue( namesArray, kAXChildrenAttribute );
if (!isHeader) {
if (snowLeopard) {
CFArrayAppendValue( namesArray, kAXVisibleChildrenAttribute );
CFArrayAppendValue( namesArray, kAXSelectedChildrenAttribute );
CFArrayAppendValue( namesArray, kAXOrientationAttribute );
} else {
CFArrayAppendValue( namesArray, kAXVisibleRowsAttribute );
CFArrayAppendValue( namesArray, kAXRowsAttribute );
CFArrayAppendValue( namesArray, kAXSelectedRowsAttribute );
CFArrayAppendValue( namesArray, kAXColumnsAttribute );
CFArrayAppendValue( namesArray, kAXSelectedColumnsAttribute );
CFArrayAppendValue( namesArray, kAXHeaderAttribute );
CFArrayAppendValue( namesArray, kAXVisibleColumnsAttribute );
}
}
} else {
if (isHeader) {
if (pView) {
// Sortable list
CFArrayAppendValue( namesArray, kAXSubroleAttribute );
CFArrayAppendValue( namesArray, kAXTitleAttribute );
if (snowLeopard) {
CFArrayAppendValue( namesArray, kAXSortDirectionAttribute );
}
}else {
// Event Log is not sortable
CFArrayAppendValue( namesArray, kAXValueAttribute );
}
} else {
if (col < 0) {
// Row has children
CFArrayAppendValue( namesArray, kAXChildrenAttribute );
CFArrayAppendValue( namesArray, kAXSelectedAttribute );
CFArrayAppendValue( namesArray, kAXIndexAttribute );
CFArrayAppendValue( namesArray, kAXVisibleChildrenAttribute );
CFArrayAppendValue( namesArray, kAXSubroleAttribute );
} else {
CFArrayAppendValue( namesArray, kAXValueAttribute );
}
}
// 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 );
CFArrayAppendValue( namesArray, kAXEnabledAttribute );
}
CFArrayAppendValue( namesArray, kAXFocusedAttribute );
CFArrayAppendValue( namesArray, kAXRoleAttribute );
CFArrayAppendValue( namesArray, kAXRoleDescriptionAttribute );
CFArrayAppendValue( namesArray, kAXDescriptionAttribute );
CFArrayAppendValue( namesArray, kAXParentAttribute );
return noErr;
}
break;
#pragma mark kEventAccessibleGetAllParameterizedAttributeNames
case kEventAccessibleGetAllParameterizedAttributeNames:
{
CFMutableArrayRef namesArray;
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames,
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray);
if (err) return err;
return noErr;
}
break;
#pragma mark kEventAccessibleGetNamedAttribute (Entire list or entire header)
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 real kAXFocusedAttribute support?
Boolean focused = false;
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused );
return noErr;
}
if ( (row < 0) && (col < 0) ) { // Entire list or entire header
// 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;
if (isHeader) {
int c, n = pList->GetColumnCount();
children = CFArrayCreateMutable( kCFAllocatorDefault, n, &kCFTypeArrayCallBacks );
for ( c = 0; c < n; c++ ) {
// Header item for each column
outIdentifier = makeElementIdentifier(-1, c, true);
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
CFArrayAppendValue( children, child );
CFRelease( child );
}
} else { // ! isHeader
int r, m = pList->GetItemCount();
children = CFArrayCreateMutable( kCFAllocatorDefault, m, &kCFTypeArrayCallBacks );
// Data rows are each children of entire list
for ( r = 0; r < m; r++ ) {
// For each row
outIdentifier = makeElementIdentifier(r, -1, false);
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
CFArrayAppendValue( children, child );
CFRelease( child );
}
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
CFRelease( children );
return noErr;
} else if ( CFStringCompare( attribute, kAXHeaderAttribute, 0 ) == kCFCompareEqualTo ) {
AXUIElementRef child;
outIdentifier = makeElementIdentifier(-1, -1, true);
child = AXUIElementCreateWithHIObjectAndIdentifier( (HIObjectRef)headerView, outIdentifier );
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( child ), &child );
CFRelease( child );
return noErr;
} else if ( CFStringCompare( attribute, kAXRowsAttribute, 0 ) == kCFCompareEqualTo ) {
CFMutableArrayRef children;
AXUIElementRef child;
int r, m = pList->GetItemCount();
children = CFArrayCreateMutable( kCFAllocatorDefault, m, &kCFTypeArrayCallBacks );
// Data rows are each children of entire list
for ( r = 0; r < m; r++ ) {
// For each row
outIdentifier = makeElementIdentifier(r, -1, false);
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
CFArrayAppendValue( children, child );
CFRelease( child );
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
CFRelease( children );
return noErr;
} else if (( CFStringCompare( attribute, kAXVisibleRowsAttribute, 0 ) == kCFCompareEqualTo )
|| ( CFStringCompare( attribute, kAXVisibleChildrenAttribute, 0 ) == kCFCompareEqualTo )) {
CFMutableArrayRef children;
AXUIElementRef child;
int numItems, topItem, numVisibleItems, r;
children = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
numItems = pList->GetItemCount();
if (numItems) {
topItem = pList->GetTopItem(); // Doesn't work properly for Mac Native control in wxMac-2.8.7
numVisibleItems = pList->GetCountPerPage();
++numVisibleItems;
if (numItems <= (topItem + numVisibleItems)) numVisibleItems = numItems - topItem;
for ( r = 0; r < numVisibleItems; r++ ) { // For each visible row
outIdentifier = makeElementIdentifier(r, -1, false);
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
CFArrayAppendValue( children, child );
CFRelease( child );
}
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
CFRelease( children );
return noErr;
} else if (( CFStringCompare( attribute, kAXSelectedChildrenAttribute, 0 ) == kCFCompareEqualTo )
|| ( CFStringCompare( attribute, kAXSelectedRowsAttribute, 0 ) == kCFCompareEqualTo )) {
CFMutableArrayRef children;
AXUIElementRef child;
int r;
children = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
r = -1;
while (1) {
// Step through all selected items
r = pList->GetNextItem(r, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
if (r < 0) break;
outIdentifier = makeElementIdentifier(r, -1, false);
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
CFArrayAppendValue( children, child );
CFRelease( child );
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
CFRelease( children );
return noErr;
} else if (( CFStringCompare( attribute, kAXColumnsAttribute, 0 ) == kCFCompareEqualTo )
|| ( CFStringCompare( attribute, kAXSelectedColumnsAttribute, 0 ) == kCFCompareEqualTo )
|| ( CFStringCompare( attribute, kAXVisibleColumnsAttribute, 0 ) == kCFCompareEqualTo )
) {
CFMutableArrayRef children;
// Tell system we don't have any columns
children = CFArrayCreateMutable( kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks );
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
CFRelease( children );
return noErr;
} else if ( CFStringCompare( attribute, kAXOrientationAttribute, 0 ) == kCFCompareEqualTo ) {
CFStringRef orientation = kAXVerticalOrientationValue;
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( orientation ), &orientation );
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 = kAXGroupRole;
if (!isHeader) {
role = snowLeopard ? kAXListRole : kAXOutlineRole;
}
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 role = kAXGroupRole;
if (!isHeader) {
role = snowLeopard ? kAXListRole : kAXOutlineRole;
}
CFStringRef roleDesc = HICopyAccessibilityRoleDescription( role, NULL );
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;
int n = pList->GetItemCount();
if (isHeader) {
str = _("list headers");
} else {
if (pEventLog) {
// To allow localization, we can't just append string
// " is empty" because that assumes English word order.
if (n) {
str = _("list of events");
} else {
str = _("list of events is empty");
}
} else {
if (pView) {
if (n) {
str.Printf(_("list of %s"), pView->GetViewDisplayName().c_str());
} else {
str.Printf(_("list of %s is empty"), pView->GetViewDisplayName().c_str());
}
}
}
}
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;
HISize size;
err = HIViewGetBounds(isHeader ? headerView : bodyView, &r);
size = r.size;
if (!isHeader) {
size.height += pList->m_headerHeight;
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeHISize, sizeof( HISize ), &size );
return noErr;
} else if ( CFStringCompare( attribute, kAXPositionAttribute, 0 ) == kCFCompareEqualTo ) {
HIRect r;
HIPoint pt;
int x, y;
err = HIViewGetBounds(isHeader ? headerView : bodyView, &r);
x = r.origin.x;
y = r.origin.y;
// 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 {
return CallNextEventHandler( inHandlerCallRef, inEvent );
}
#pragma mark kEventAccessibleGetNamedAttribute (row or item)
} else { // End if ( (row < 0) && (col < 0) )
if ( CFStringCompare( attribute, kAXChildrenAttribute, 0 ) == kCFCompareEqualTo ) {
if (col >= 0) {
return eventNotHandledErr;
}
// Create and return an array of AXUIElements describing the children of this view.
CFMutableArrayRef children;
AXUIElementRef child;
int c, n = pList->GetColumnCount();
children = CFArrayCreateMutable( kCFAllocatorDefault, n, &kCFTypeArrayCallBacks );
for ( c = 0; c < n; c++ ) {
// For each column
outIdentifier = makeElementIdentifier(row, c, isHeader);
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
CFArrayAppendValue( children, child );
CFRelease( child );
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
CFRelease( children );
return noErr;
} else if ( CFStringCompare( attribute, kAXDescriptionAttribute, 0 ) == kCFCompareEqualTo ) {
wxString str, buf;
int rowCount;
Boolean isCurrentSortCol = false;
if (isHeader) {
wxListItem headerItem;
int numCols = pList->GetColumnCount();
pList->GetColumn(col, headerItem);
if (pView) {
if (col == pView->m_iSortColumn) {
isCurrentSortCol = true;
}
}
if (isCurrentSortCol) {
if (pView->m_bReverseSort) {
buf.Printf(_("; current sort column %d of %d; descending order; "), col+1, numCols);
} else {
buf.Printf(_("; current sort column %d of %d; ascending order; "), col+1, numCols);
}
} else {
buf.Printf(_("; column %d of %d; "), col+1, numCols);
}
str = headerItem.GetText();
str += buf;
} else { // ! isHeader
rowCount = pList->GetItemCount();
if (rowCount <= 0) {
str = _("list is empty");
} else {
if (col < 0) {
str.Printf(_("; row %d; "), row+1);
} else {
if (pList->GetItemState(row, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED) {
if (col == 0) {
buf.Printf(_("; selected row %d of %d; "), row+1, rowCount);
} else {
buf.Printf(_("; selected row %d ; "), row+1);
}
} else { // Row is not selected
if (col == 0) {
buf.Printf(_("; row %d of %d; "), row+1, rowCount);
} else {
buf.Printf(_("; row %d; "), row+1);
}
}
if (pEventLog) {
str = pEventLog->OnListGetItemText(row, col);
} else {
str = pView->FireOnListGetItemText(row, col);
}
if (str.IsEmpty()) {
str = _("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, kAXValueAttribute, 0 ) == kCFCompareEqualTo ) {
CFStringRef value = CFSTR("");
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( value ), &value );
CFRelease( value );
return noErr;
} else if ( CFStringCompare( attribute, kAXParentAttribute, 0 ) == kCFCompareEqualTo ) {
AXUIElementRef parent;
HIViewRef parentView;
parentView = isHeader ? headerView : bodyView;
if (isHeader) {
outIdentifier = 0; // Parent is entire list
} else { // ! isHeader
if (col < 0) { // Data row
outIdentifier = 0; // Parent is entire list
} else {
outIdentifier = makeElementIdentifier(row, -1, isHeader); // Parent of cell is data row
}
}
parent = AXUIElementCreateWithHIObjectAndIdentifier((HIObjectRef)parentView, outIdentifier);
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;
if (isHeader) {
if (pView) {
subRole = kAXSortButtonSubrole;
} else {
return eventNotHandledErr;
}
} else { // ! isHeader
if (col < 0) {
subRole = kAXOutlineRowSubrole;
} 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;
if (isHeader) {
if (pView) {
role = kAXButtonRole;
} else {
role = kAXStaticTextRole;
}
} else if (col < 0) {
role = kAXRowRole;
} else {
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;
if (isHeader) {
if (pView) {
roleDesc = HICopyAccessibilityRoleDescription( kAXButtonRole, kAXSortButtonSubrole );
} else {
roleDesc = HICopyAccessibilityRoleDescription( kAXStaticTextRole, NULL );
}
} else { // ! isHeader
CFStringRef role = kAXStaticTextRole;
if (col < 0) {
if (snowLeopard) {
role = kAXRowRole;
} else {
role = kAXOutlineRowSubrole;
}
}
roleDesc = HICopyAccessibilityRoleDescription( role, NULL );
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( roleDesc ), &roleDesc );
CFRelease( roleDesc );
return noErr;
} else if ( CFStringCompare( attribute, kAXSortDirectionAttribute, 0 ) == kCFCompareEqualTo ) {
CFStringRef sortDirection;
if (col == pView->m_iSortColumn) {
sortDirection = pView->m_bReverseSort ? kAXDescendingSortDirectionValue : kAXAscendingSortDirectionValue;
} else {
sortDirection = kAXUnknownSortDirectionValue;
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( sortDirection ), &sortDirection );
CFRelease( sortDirection );
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;
if (col < 0) {
size.width = r.width;
}
}
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, xoff = 0, ppux, ppuy;
// 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;
if (col < 0) {
x = r.x;
}
}
xoff = pList->GetScrollPos(wxHORIZONTAL);
if (xoff) {
wxScrolledWindow * win = ((CBOINCListCtrl*)pList)->GetMainWin();
win->GetScrollPixelsPerUnit(&ppux, &ppuy);
x -= (xoff * ppux);
}
// 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;
//TODO: Add kAXFocusedAttribute support?
#if 0
} else if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) {
// Return whether or not this part is focused.
Boolean focused = false;
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( focused ), &focused );
return noErr;
#endif
} else if ( CFStringCompare( attribute, kAXSelectedAttribute, 0 ) == kCFCompareEqualTo ) {
if (col >= 0) {
return eventNotHandledErr;
}
Boolean isSelected = pList->GetItemState(row, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED;
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeBoolean, sizeof( isSelected ), &isSelected );
return noErr;
} else if ( CFStringCompare( attribute, kAXIndexAttribute, 0 ) == kCFCompareEqualTo ) {
if (col >= 0) {
return eventNotHandledErr;
}
int theRow = row + 1;
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeInteger, sizeof( theRow ), &theRow );
return noErr;
} else if ( CFStringCompare( attribute, kAXVisibleChildrenAttribute, 0 ) == kCFCompareEqualTo ) {
//TODO: Actually determine which columns are visible
// Create and return an array of AXUIElements describing the children of this view.
CFMutableArrayRef children;
AXUIElementRef child;
int c, n = pList->GetColumnCount();
children = CFArrayCreateMutable( kCFAllocatorDefault, n, &kCFTypeArrayCallBacks );
if (col >= 0) {
return eventNotHandledErr;
}
for ( c = 0; c < n; c++ ) {
// For each column
outIdentifier = makeElementIdentifier(row, c, isHeader);
child = AXUIElementCreateWithHIObjectAndIdentifier( obj, outIdentifier );
CFArrayAppendValue( children, child );
CFRelease( child );
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeValue, typeCFTypeRef, sizeof( children ), &children );
CFRelease( children );
return noErr;
} else if ( CFStringCompare( attribute, kAXTitleAttribute, 0 ) == kCFCompareEqualTo ) {
// Return the item's text
wxString str;
wxListItem headerItem;
if (pEventLog || !isHeader) {
return eventNotHandledErr;
}
if (col < 0) {
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 // End if (!( (row < 0) && (col < 0) ))
break;
} // End case kEventAccessibleGetNamedAttribute:
#pragma mark kEventAccessibleIsNamedAttributeSettable
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 ((row >= 0) || (col >= 0))
{
if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo )
{
isSettable = true;
}
}
SetEventParameter( inEvent, kEventParamAccessibleAttributeSettable, typeBoolean, sizeof( Boolean ), &isSettable );
return noErr;
}
break;
#pragma mark kEventAccessibleSetNamedAttribute
case kEventAccessibleSetNamedAttribute:
{
return eventNotHandledErr;
}
break;
#pragma mark kEventAccessibleGetAllActionNames
case kEventAccessibleGetAllActionNames:
{
CFMutableArrayRef array;
err = GetEventParameter (inEvent, kEventParamAccessibleActionNames,
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &array);
if (err) return err;
if (pEventLog && isHeader) {
return noErr;
}
if (isHeader && (col >= 0) ) {
// Sortable column header
CFArrayAppendValue( array, kAXPressAction );
}
return noErr;
}
break;
#pragma mark kEventAccessibleGetNamedActionDescription
case kEventAccessibleGetNamedActionDescription:
{
CFStringRef action;
CFMutableStringRef desc;
CFStringRef selfDesc = NULL;
if ( (row < 0) && (col < 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;
#pragma mark kEventAccessiblePerformNamedAction
case kEventAccessiblePerformNamedAction:
{
CFStringRef action;
wxWindowID id = pList->GetId();
if ( (row < 0) && (col < 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 HTMLListAccessibilityEventHandler( EventHandlerCallRef inHandlerCallRef,
EventRef inEvent, void* pData) {
const UInt32 eventClass = GetEventClass(inEvent);
const UInt32 eventKind = GetEventKind(inEvent);
CProjectListCtrlAccessible* pProjectListCtrlAccessible = NULL;
CProjectListCtrl* pProjectListCtrl = NULL;
CNoticeListCtrlAccessible* pNoticeListCtrlAccessible = NULL;
CNoticeListCtrl* pNoticeListCtrl = NULL;
OSStatus err;
pProjectListCtrlAccessible = ((HTMLListAccessibilityHandlerData*)pData)->pProjectListCtrlAccessible;
pNoticeListCtrlAccessible = ((HTMLListAccessibilityHandlerData*)pData)->pNoticeListCtrlAccessible;
if (pProjectListCtrlAccessible != NULL) {
pProjectListCtrl = wxDynamicCast(pProjectListCtrlAccessible->GetWindow(), CProjectListCtrl);
if (pProjectListCtrl == NULL) {
return eventNotHandledErr;
}
} else {
if (pNoticeListCtrlAccessible == NULL) {
return eventNotHandledErr;
}
pNoticeListCtrl = wxDynamicCast(pNoticeListCtrlAccessible->GetWindow(), CNoticeListCtrl);
if (pNoticeListCtrl == 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) {
#pragma mark kEventAccessibleGetChildAtPoint
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);
if (pProjectListCtrlAccessible) {
pProjectListCtrl->ScreenToClient(&p.x, &p.y);
err = pProjectListCtrlAccessible->HitTest(p, &hitRow, NULL);
} else {
pNoticeListCtrl->ScreenToClient(&p.x, &p.y);
err = pNoticeListCtrlAccessible->HitTest(p, &hitRow, NULL);
}
if (err) {
return eventNotHandledErr;
}
if (hitRow >= 0) {
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;
#pragma mark kEventAccessibleGetFocusedChild
case kEventAccessibleGetFocusedChild:
return noErr;
break;
#pragma mark kEventAccessibleGetAllAttributeNames
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;
#pragma mark kEventAccessibleGetAllParameterizedAttributeNames
case kEventAccessibleGetAllParameterizedAttributeNames:
{
CFMutableArrayRef namesArray;
err = GetEventParameter (inEvent, kEventParamAccessibleAttributeNames,
typeCFMutableArrayRef, NULL, sizeof(typeCFMutableArrayRef), NULL, &namesArray);
if (err) return err;
return noErr;
}
break;
#pragma mark kEventAccessibleGetNamedAttribute
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;
if (pProjectListCtrlAccessible) {
err = pProjectListCtrlAccessible->GetLocation(r, row);
} else {
err = pNoticeListCtrlAccessible->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;
if (pProjectListCtrlAccessible) {
err = pProjectListCtrlAccessible->GetLocation(r, row);
} else {
err = pNoticeListCtrlAccessible->GetLocation(r, row);
}
if (err) {
return eventNotHandledErr;
}
pt.x = r.x;
pt.y = r.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;
if (pProjectListCtrlAccessible) {
err = pProjectListCtrlAccessible->GetChildCount(&n);
} else {
err = pNoticeListCtrlAccessible->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");
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;
if (pProjectListCtrlAccessible) {
parentView = HIViewGetSuperview(pProjectListCtrlAccessible->m_listView);
} else {
parentView = HIViewGetSuperview(pNoticeListCtrlAccessible->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;
Boolean selected = false;
if (pProjectListCtrlAccessible) {
err = pProjectListCtrlAccessible->GetChildCount(&n);
} else {
err = pNoticeListCtrlAccessible->GetChildCount(&n);
}
if (err) {
return eventNotHandledErr;
}
if (pProjectListCtrl) {
selected = pProjectListCtrl->IsSelected(row - 1);
} else {
selected = pNoticeListCtrl->IsSelected(row - 1);
}
if (selected) {
str.Printf(_("selected row %d of %d; "), row, n);
} else {
str.Printf(_("row %d of %d; "), row, n);
}
if (pProjectListCtrlAccessible) {
err = pProjectListCtrlAccessible->GetDescription(row, &buf);
} else {
err = pNoticeListCtrlAccessible->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;
if (pProjectListCtrlAccessible) {
parentView = pProjectListCtrlAccessible->m_listView;
} else {
parentView = pNoticeListCtrlAccessible->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 );
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;
//TODO: Add kAXFocusedAttribute support?
#if 0
} else if ( CFStringCompare( attribute, kAXFocusedAttribute, 0 ) == kCFCompareEqualTo ) {
// Return whether or not this part is focused.
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 (pProjectListCtrlAccessible) {
pProjectListCtrlAccessible->GetName(row, &str);
} else {
pNoticeListCtrlAccessible->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:
#pragma mark kEventAccessibleIsNamedAttributeSettable
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;
#pragma mark kEventAccessibleSetNamedAttribute
case kEventAccessibleSetNamedAttribute:
{
return eventNotHandledErr;
}
break;
#pragma mark kEventAccessibleGetAllActionNames
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;
#pragma mark kEventAccessibleGetNamedActionDescription
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;
#pragma mark kEventAccessiblePerformNamedAction
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;
}
if (pProjectListCtrlAccessible) {
err = pProjectListCtrlAccessible->DoDefaultAction(inIdentifier);
} else {
err = pNoticeListCtrlAccessible->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) {
#pragma mark kEventAccessibleGetNamedAttribute
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) {
#pragma mark kEventAccessibleGetNamedAttribute
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;
}