mirror of https://github.com/BOINC/boinc.git
1507 lines
51 KiB
Plaintext
1507 lines
51 KiB
Plaintext
// This file is part of BOINC.
|
|
// http://boinc.berkeley.edu
|
|
// Copyright (C) 2013 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.mm
|
|
|
|
#include "MacGUI.pch"
|
|
#include <Cocoa/Cocoa.h>
|
|
|
|
#include "BOINCBaseFrame.h"
|
|
#include "MainDocument.h"
|
|
#include "BOINCListCtrl.h"
|
|
#include "DlgEventLogListCtrl.h"
|
|
#include "ViewStatistics.h"
|
|
#include "wxPieCtrl.h"
|
|
|
|
#import <AppKit/NSAccessibility.h>
|
|
|
|
// I have not been able to get it to work when this is FALSE
|
|
#define RETAIN_KIDS_OF_ROWS TRUE
|
|
|
|
#if !wxOSX_USE_NATIVE_FLIPPED
|
|
#error: must convert to Quartz coordinates in accessibilityHitTest and NSAccessibilityPositionAttribute
|
|
#endif
|
|
|
|
#pragma mark === CBOINCListCtrl & CDlgEventLogListCtrl Accessibility Shared Code ===
|
|
|
|
#if USE_NATIVE_LISTCONTROL
|
|
#error: This code assumes wxGenericListCtrl
|
|
#endif
|
|
|
|
#if (DLG_LISTCTRL_BASE != wxGenericListCtrl)
|
|
#error: This code assumes wxGenericListCtrl
|
|
#endif
|
|
|
|
// Allowable column numbers are 0 to 15, but 15 is reserved to indicate a full row.
|
|
#define MAX_LIST_COL 16
|
|
|
|
#define isHeaderFlag (1<<0)
|
|
#define isEventLogFlag (1<<1)
|
|
#define isRowFlag (1<<2)
|
|
|
|
static UInt32 makeElementIdentifier(SInt32 row, SInt32 col) {
|
|
UInt32 id = (((row + 1) * MAX_LIST_COL) + (col + 1)) * 2;
|
|
return id;
|
|
}
|
|
|
|
static void wxRectToNSRect(wxRect &wxr, NSRect &nsr) {
|
|
nsr.origin.x = wxr.x;
|
|
nsr.origin.y = wxr.y;
|
|
nsr.size.width = wxr.width;
|
|
nsr.size.height = wxr.height;
|
|
}
|
|
|
|
|
|
@interface EventLogCellUIElement : NSObject {
|
|
NSInteger row, col;
|
|
NSInteger listFlags;
|
|
BOOL isHeader;
|
|
BOOL isEventLog;
|
|
BOOL isRow; // is this needed?
|
|
wxGenericListCtrl *pList;
|
|
id parent;
|
|
NSView *listControlView;
|
|
NSInteger headerHeight;
|
|
CBOINCBaseView *BOINCView;
|
|
|
|
|
|
}
|
|
- (id)initWithRow:(NSInteger)aRow column:(NSInteger)acol listFlags:(NSInteger)flags listCtrl:(wxGenericListCtrl *)aListCtrl parent:(id)aParent BOINCView:(CBOINCBaseView *)aBOINCView;
|
|
+ (EventLogCellUIElement *)elementWithRow:(NSInteger)aRow column:(NSInteger)acol listFlags:(NSInteger)flags listCtrl:(wxGenericListCtrl *)aListCtrl parent:(id)aParent BOINCView:(CBOINCBaseView *)aBOINCView;
|
|
- (NSInteger)row;
|
|
- (NSInteger)col;
|
|
|
|
@end
|
|
|
|
|
|
@implementation EventLogCellUIElement
|
|
|
|
- (id)initWithRow:(NSInteger)aRow column:(NSInteger)acol listFlags:(NSInteger)flags listCtrl:(wxGenericListCtrl *)aListCtrl parent:(id)aParent BOINCView:(CBOINCBaseView *)aBOINCView {
|
|
if (self = [super init]) {
|
|
row = aRow;
|
|
col = acol;
|
|
listFlags = flags;
|
|
isHeader = (flags & isHeaderFlag) ? YES : NO;
|
|
isEventLog = (flags & isEventLogFlag) ? YES : NO;
|
|
isRow = (flags & isRowFlag) ? YES : NO; // is this needed?
|
|
pList = aListCtrl;
|
|
parent = aParent;
|
|
listControlView = pList->GetHandle();
|
|
headerHeight = ((wxWindow *)(pList->m_headerWin))->GetSize().y;
|
|
BOINCView = aBOINCView;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
+ (EventLogCellUIElement *)elementWithRow:(NSInteger)aRow column:(NSInteger)acol listFlags:(NSInteger)flags listCtrl:(wxGenericListCtrl *)aListCtrl parent:(id)aParent BOINCView:(CBOINCBaseView *)aBOINCView {
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
return [[[EventLogCellUIElement alloc] initWithRow:aRow column:acol listFlags:flags listCtrl:aListCtrl parent:aParent BOINCView:aBOINCView] autorelease];
|
|
#else
|
|
return [[[self alloc] initWithRow:aRow column:acol listFlags:flags listCtrl:aListCtrl parent:aParent BOINCView:aBOINCView] autorelease];
|
|
#endif
|
|
}
|
|
|
|
- (BOOL)accessibilityIsIgnored {
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)isEqual:(id)object {
|
|
if ([object isKindOfClass:[EventLogCellUIElement self]]) {
|
|
EventLogCellUIElement *other = object;
|
|
return (row == other->row) && (col == other->col) && (listFlags == other->listFlags) && [super isEqual:object];
|
|
} else {
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
//TODO: Is this ever actually called?
|
|
- (id)accessibilityHitTest:(NSPoint)point {
|
|
NSPoint windowPoint = [[listControlView window] convertScreenToBase:point];
|
|
NSPoint localPoint = [listControlView convertPointFromBase:windowPoint];
|
|
|
|
NSInteger i, x = 0, yoff;
|
|
// First get the position relative to the ListCtrl
|
|
pList->CalcScrolledPosition(0, 0, &x, &yoff);
|
|
for (i=0; i<col; i++) {
|
|
x += pList->GetColumnWidth(i);
|
|
}
|
|
NSRect r;
|
|
r = [listControlView bounds];
|
|
r.origin.x = x;
|
|
r.size.width = pList->GetColumnWidth(col);
|
|
if (isHeader) {
|
|
r.size.height = headerHeight;
|
|
} else {
|
|
wxRect wxr;
|
|
pList->GetItemRect(row, wxr);
|
|
r.origin.y = wxr.y;
|
|
// r.origin.y = r.size.height - wxr.y; // Convert to Quartz coordinates
|
|
r.size.height = wxr.height;
|
|
}
|
|
if (NSPointInRect(localPoint, r)){
|
|
return self;
|
|
}
|
|
return nil;
|
|
}
|
|
|
|
- (NSUInteger)hash {
|
|
// Equal objects must hash the same.
|
|
return [super hash] + makeElementIdentifier(isHeader ? row : row + 1, col);
|
|
}
|
|
|
|
- (NSInteger)row {
|
|
return row;
|
|
}
|
|
|
|
- (NSInteger)col {
|
|
return col;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[super dealloc];
|
|
}
|
|
|
|
//
|
|
// accessibility protocol
|
|
//
|
|
|
|
// attributes
|
|
|
|
- (NSArray *)accessibilityAttributeNames {
|
|
static NSArray *attributes = nil;
|
|
if (attributes == nil) {
|
|
attributes = [[NSArray alloc] initWithObjects:
|
|
NSAccessibilityWindowAttribute,
|
|
NSAccessibilityTopLevelUIElementAttribute,
|
|
NSAccessibilityDescriptionAttribute,
|
|
NSAccessibilitySizeAttribute,
|
|
NSAccessibilityPositionAttribute,
|
|
NSAccessibilityTitleAttribute,
|
|
NSAccessibilityEnabledAttribute,
|
|
NSAccessibilityFocusedAttribute,
|
|
NSAccessibilityRoleAttribute,
|
|
NSAccessibilityRoleDescriptionAttribute,
|
|
NSAccessibilityParentAttribute,
|
|
nil];
|
|
}
|
|
if (isHeader && !isEventLog) {
|
|
NSMutableArray *temp = [attributes mutableCopy];
|
|
[temp addObject:NSAccessibilitySubroleAttribute];
|
|
[temp addObject:NSAccessibilitySortDirectionAttribute];
|
|
return [temp copy];
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
|
if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
|
|
return NSAccessibilityUnignoredAncestor(parent);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
|
|
return [listControlView window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
|
|
return [listControlView window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
|
|
NSString *desc;
|
|
wxString s;
|
|
BOOL isCurrentSortCol = false;
|
|
|
|
if (isHeader) {
|
|
int numCols = pList->GetColumnCount();
|
|
if ((!isEventLog) && (BOINCView != nil)) {
|
|
if (col == BOINCView->m_iSortColumn) {
|
|
isCurrentSortCol = YES;
|
|
}
|
|
}
|
|
if (isCurrentSortCol) {
|
|
if (BOINCView->m_bReverseSort) {
|
|
s.Printf(_("(current sort column %d of %d; descending order)"), col+1, numCols);
|
|
} else {
|
|
s.Printf(_("(current sort column %d of %d; ascending order)"), col+1, numCols);
|
|
}
|
|
} else {
|
|
s.Printf(_("(column %d of %d)"), col+1, numCols);
|
|
}
|
|
} else {
|
|
if (pList->GetItemState(row, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED) {
|
|
if (col == 0) {
|
|
s.Printf(_("(selected row %d of %d)"), row+1, pList->GetItemCount());
|
|
} else {
|
|
s.Printf(_("(selected row %d)"), row+1);
|
|
}
|
|
} else {
|
|
if (col == 0) { // Row is not selected
|
|
s.Printf(_("(row %d of %d)"), row+1, pList->GetItemCount());
|
|
} else {
|
|
s.Printf(_("(row %d)"), row+1);
|
|
}
|
|
}
|
|
}
|
|
desc = [NSString stringWithUTF8String:(char *)(s.utf8_str().data())];
|
|
return desc;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
|
|
NSSize sz;
|
|
sz.width = pList->GetColumnWidth(col);
|
|
if (isHeader) {
|
|
sz.height = headerHeight;
|
|
} else {
|
|
wxRect r;
|
|
pList->GetItemRect(row, r);
|
|
sz.height = r.height;
|
|
}
|
|
return [NSValue valueWithSize:sz];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
|
|
int i, xoff, yoff;
|
|
NSPoint pt;
|
|
pt.x = 0;
|
|
pt.y = 0;
|
|
// First get the position relative to the ListCtrl
|
|
for (i=0; i<col; i++) {
|
|
pt.x += pList->GetColumnWidth(i);
|
|
}
|
|
if (!isHeader) {
|
|
wxRect r;
|
|
pList->GetItemRect(row, r);
|
|
pt.y = r.y;
|
|
}
|
|
pList->CalcScrolledPosition(0, 0, &xoff, &yoff);
|
|
pt.x += xoff;
|
|
pt.y += headerHeight;
|
|
// pt.y = [listControlView bounds].size.height - headerHeight - pt.y; // Convert to Quartz coordinates
|
|
|
|
//Convert the point to global (screen) coordinates
|
|
NSPoint windowPoint = [listControlView convertPoint:pt toView: nil];
|
|
pt = [[listControlView window] convertBaseToScreen:windowPoint];
|
|
|
|
return [NSValue valueWithPoint:pt];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
|
|
wxString str;
|
|
if (isHeader) {
|
|
wxListItem headerItem;
|
|
pList->GetColumn(col, headerItem);
|
|
str = headerItem.GetText();
|
|
} else {
|
|
str = pList->GetItemText(row, col);
|
|
if (str.IsEmpty()) {
|
|
str = _("blank");
|
|
}
|
|
}
|
|
char *s = (char *)(str.utf8_str().data());
|
|
NSString *text = [[NSString alloc] initWithUTF8String:s];
|
|
return text;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) {
|
|
return [NSNumber numberWithBool:YES];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
|
|
return NO;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
|
return NSAccessibilityStaticTextRole;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
|
|
return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole,
|
|
(isHeader && !isEventLog) ? NSAccessibilitySortButtonSubrole : nil);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySubroleAttribute]) {
|
|
return NSAccessibilitySortButtonSubrole;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySortDirectionAttribute]) {
|
|
if (col == BOINCView->m_iSortColumn) {
|
|
return BOINCView->m_bReverseSort ?
|
|
NSAccessibilityDescendingSortDirectionValue : NSAccessibilityAscendingSortDirectionValue;
|
|
} else {
|
|
return NSAccessibilityUnknownSortDirectionValue;
|
|
}
|
|
|
|
} else {
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
|
|
// return [super accessibilityIsAttributeSettable:attribute];
|
|
return NO;
|
|
}
|
|
|
|
- (NSArray *)accessibilityActionNames {
|
|
// All cells except EventLog Header accept press action
|
|
if (!isHeader || !isEventLog) {
|
|
return [NSArray arrayWithObject:NSAccessibilityPressAction];
|
|
}
|
|
return [NSArray array];
|
|
}
|
|
|
|
- (NSString *)accessibilityActionDescription:(NSString *)action {
|
|
return NSAccessibilityActionDescription(action);
|
|
}
|
|
|
|
- (void)accessibilityPerformAction:(NSString *)action {
|
|
if (isHeader) {
|
|
wxWindowID id = pList->GetId();
|
|
wxListEvent event(wxEVT_COMMAND_LIST_COL_CLICK, id);
|
|
event.m_col = col;
|
|
pList->GetEventHandler()->AddPendingEvent(event);
|
|
} else {
|
|
int i = -1; // Step through all selected items and deselect each one
|
|
while (1) {
|
|
i = pList->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
if (i < 0) break;
|
|
pList->SetItemState(i, 0, wxLIST_STATE_SELECTED);
|
|
}
|
|
// Select the one row the user "clicked" on
|
|
pList->SetItemState(row, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
|
|
}
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface FauxListRow : NSObject {
|
|
NSInteger row;
|
|
BOOL isEventLog;
|
|
wxGenericListCtrl *pList;
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
NSMutableArray *kids;
|
|
NSInteger numkids;
|
|
#endif
|
|
id parent;
|
|
NSView *listControlView;
|
|
}
|
|
|
|
+ (FauxListRow *)listRowWithRow:(NSInteger)aRow listFlags:(NSInteger)flags listCtrl:(wxGenericListCtrl *)aListCtrl parent:(id)aParent;
|
|
- (id)initWithRow:(NSInteger)aRow listFlags:(NSInteger)flags listCtrl:(wxGenericListCtrl *)aListCtrl parent:(id)aParent;
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
- (void) adjustKidsIfNeeded;
|
|
#endif
|
|
@end
|
|
|
|
|
|
@implementation FauxListRow
|
|
|
|
- (id)initWithRow:(NSInteger)aRow listFlags:(NSInteger)flags listCtrl:(wxGenericListCtrl *)aListCtrl parent:(id)aParent {
|
|
if (self = [super init]) {
|
|
row = aRow;
|
|
pList = aListCtrl;
|
|
isEventLog = (flags & isEventLogFlag) ? YES : NO;
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
numkids = 0;
|
|
kids = nil;
|
|
#endif
|
|
parent = aParent;
|
|
listControlView = pList->GetHandle();
|
|
}
|
|
return self;
|
|
}
|
|
|
|
+ (FauxListRow *)listRowWithRow:(NSInteger)aRow listFlags:(NSInteger)flags listCtrl:(wxGenericListCtrl *)aListCtrl parent:(id)aParent {
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
return [[[FauxListRow alloc]initWithRow:aRow listFlags:flags listCtrl:aListCtrl parent:aParent] autorelease];
|
|
#else
|
|
return [[[self alloc]initWithRow:aRow listFlags:flags listCtrl:aListCtrl parent:aParent] autorelease];
|
|
#endif
|
|
}
|
|
|
|
- (BOOL)accessibilityIsIgnored {
|
|
return NO;
|
|
}
|
|
|
|
- (BOOL)isEqual:(id)object {
|
|
if ([object isKindOfClass:[FauxListRow self]]) {
|
|
FauxListRow *other = object;
|
|
return (row == other->row) && (isEventLog == other->isEventLog) && [super isEqual:object];
|
|
} else {
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
- (id)accessibilityHitTest:(NSPoint)point {
|
|
NSPoint windowPoint = [[listControlView window] convertScreenToBase:point];
|
|
NSPoint localPoint = [listControlView convertPointFromBase:windowPoint];
|
|
|
|
//TODO: should we just generate temporary EventLogCellUIElement objects as needed?
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
[self adjustKidsIfNeeded];
|
|
#endif
|
|
|
|
NSRect r;
|
|
wxRect wxcr = pList->GetClientRect();
|
|
wxRect wxr;
|
|
pList->GetItemRect(row, wxr);
|
|
|
|
NSInteger col, numCols, x = 0, yoff;
|
|
pList->CalcScrolledPosition(0, 0, &x, &yoff);
|
|
|
|
numCols = pList->GetColumnCount();
|
|
for (col=0; col<numCols; col++) {
|
|
// First get the position relative to the ListCtrl
|
|
r = [listControlView bounds];
|
|
r.origin.x = x;
|
|
r.size.width = pList->GetColumnWidth(col);
|
|
r.origin.y = wxr.y;
|
|
// r.origin.y = r.size.height - wxr.y; // Convert to Quartz coordinates
|
|
r.size.height = wxr.height;
|
|
if (NSPointInRect(localPoint, r)){
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
return (EventLogCellUIElement *)[kids objectAtIndex:col];
|
|
#else
|
|
NSInteger myFlags = isRowFlag | (isEventLog ? isEventLogFlag : 0);
|
|
EventLogCellUIElement * cell = [EventLogCellUIElement elementWithRow:row column:col listFlags:myFlags listCtrl:pList parent:self BOINCView:nil];
|
|
return cell;
|
|
#endif
|
|
}
|
|
|
|
x += r.size.width;
|
|
}
|
|
|
|
r = [listControlView bounds];
|
|
r.origin.x = 0;
|
|
r.size.width = wxcr.width;
|
|
r.size.height = wxcr.height;
|
|
r.origin.y = wxr.y;
|
|
// r.origin.y = r.size.height - wxr.y; // Convert to Quartz coordinates
|
|
r.size.height = wxr.height;
|
|
if (NSPointInRect(localPoint, r)){
|
|
return self;
|
|
}
|
|
// return [super accessibilityHitTest:point];
|
|
return nil;
|
|
}
|
|
|
|
|
|
- (NSUInteger)hash {
|
|
// Equal objects must hash the same.
|
|
return [super hash] + makeElementIdentifier(row, MAX_LIST_COL-1);
|
|
}
|
|
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
- (void)adjustKidsIfNeeded {
|
|
if (kids == nil) {
|
|
numkids = pList->GetColumnCount();
|
|
kids = [[NSMutableArray arrayWithCapacity:numkids] retain];
|
|
// The number of columns never changes
|
|
NSInteger myFlags = isRowFlag | (isEventLog ? isEventLogFlag : 0);
|
|
for (NSInteger i = 0; i < numkids; ++i) {
|
|
EventLogCellUIElement * cell = [EventLogCellUIElement elementWithRow:row column:i listFlags:myFlags listCtrl:pList parent:self BOINCView:nil];
|
|
[cell retain];
|
|
[kids addObject:cell];
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// accessibility protocol
|
|
//
|
|
|
|
// attributes
|
|
|
|
- (NSArray *)accessibilityAttributeNames {
|
|
static NSArray *attributes = nil;
|
|
if (attributes == nil) {
|
|
attributes = [[NSArray alloc] initWithObjects:
|
|
NSAccessibilityRoleAttribute,
|
|
NSAccessibilityRoleDescriptionAttribute,
|
|
NSAccessibilitySubroleAttribute,
|
|
// NSAccessibilityDescriptionAttribute,
|
|
NSAccessibilityTitleAttribute,
|
|
NSAccessibilityEnabledAttribute,
|
|
NSAccessibilityFocusedAttribute,
|
|
NSAccessibilityParentAttribute,
|
|
NSAccessibilityWindowAttribute,
|
|
NSAccessibilityTopLevelUIElementAttribute,
|
|
NSAccessibilityPositionAttribute,
|
|
NSAccessibilitySizeAttribute,
|
|
NSAccessibilityChildrenAttribute,
|
|
NSAccessibilityVisibleChildrenAttribute,
|
|
NSAccessibilityWindowAttribute,
|
|
NSAccessibilityIndexAttribute,
|
|
NSAccessibilitySelectedAttribute,
|
|
nil];
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
|
if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
|
return NSAccessibilityRowRole;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
|
|
return NSAccessibilityRoleDescription(NSAccessibilityRowRole, nil);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySubroleAttribute]) {
|
|
return NSAccessibilityTableRowSubrole;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
|
|
return [parent window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
|
|
wxString s;
|
|
s.Printf(_("(row %d)"), row+1);
|
|
NSString *desc = [NSString stringWithUTF8String:(char *)(s.utf8_str().data())];
|
|
return desc;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
|
|
NSSize sz;
|
|
wxRect r;
|
|
pList->GetItemRect(row, r);
|
|
sz.height = r.height;
|
|
sz.width = r.width;
|
|
return [NSValue valueWithSize:sz];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
|
|
int xoff, yoff;
|
|
NSPoint pt;
|
|
// First get the position relative to the ListCtrl
|
|
wxRect r;
|
|
pList->GetItemRect(row, r);
|
|
pList->CalcScrolledPosition(0, 0, &xoff, &yoff);
|
|
pt.x = xoff;
|
|
pt.y = r.y;
|
|
// pt.y = [parent bounds].size.height - pt.y; // Convert to Quartz coordinates
|
|
|
|
//Convert the point to global (screen) coordinates
|
|
NSPoint windowPoint = [parent convertPoint:pt toView: nil];
|
|
pt = [[parent window] convertBaseToScreen:windowPoint];
|
|
|
|
return [NSValue valueWithPoint:pt];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityTitleAttribute]) {
|
|
wxString s;
|
|
s.Printf(_("row %d"), row+1);
|
|
NSString *title = [NSString stringWithUTF8String:(char *)(s.utf8_str().data())];
|
|
return title;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) {
|
|
return [NSNumber numberWithBool:YES];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
|
|
return NO;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
|
|
return NSAccessibilityUnignoredAncestor(parent);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
[self adjustKidsIfNeeded];
|
|
#else
|
|
NSInteger i;
|
|
NSInteger numkids = pList->GetColumnCount();
|
|
|
|
NSMutableArray *kids = [NSMutableArray arrayWithCapacity:numkids]; // autorelease ??
|
|
NSInteger myFlags = isRowFlag | (isEventLog ? isEventLogFlag : 0);
|
|
for (i = 0; i < numkids; ++i) {
|
|
EventLogCellUIElement * cell = [EventLogCellUIElement elementWithRow:row column:i listFlags:myFlags listCtrl:pList parent:self BOINCView:nil]; // Retain???
|
|
[kids addObject:cell];
|
|
}
|
|
#endif
|
|
return kids;
|
|
// return NSAccessibilityUnignoredChildren(kids); // No children are ignored
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityVisibleChildrenAttribute]) {
|
|
int w, h, xoff, yoff;
|
|
pList->GetClientSize(&w, &h);
|
|
pList->CalcUnscrolledPosition(0, 0, &xoff, &yoff);
|
|
int leftVisiblePos = xoff; // Horizontal scrolled offset in pixels
|
|
int rightVisiblePos = leftVisiblePos + w;
|
|
|
|
NSInteger i, cellLeftEdge = 0, cellRightEdge;
|
|
NSMutableArray *visibleChildren = [NSMutableArray array];
|
|
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
[self adjustKidsIfNeeded];
|
|
#else
|
|
NSInteger numkids = pList->GetColumnCount();
|
|
#endif
|
|
for (i = 0; i < numkids; ++i) {
|
|
BOOL isVisible = NO;
|
|
cellRightEdge = cellLeftEdge + pList->GetColumnWidth(i);
|
|
if ((leftVisiblePos <= cellLeftEdge) && rightVisiblePos >= cellLeftEdge) {
|
|
isVisible = YES; // Left edge of cell is in visible area of row
|
|
} else if ((leftVisiblePos <= cellRightEdge) && rightVisiblePos >= cellRightEdge) {
|
|
isVisible = YES; // Right edge of cell is in visible area of row
|
|
} else if ((leftVisiblePos > cellLeftEdge) && (rightVisiblePos < cellRightEdge)) {
|
|
isVisible = YES; // Visible area of row is totally within cell
|
|
}
|
|
|
|
if (isVisible) {
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
[visibleChildren addObject:[kids objectAtIndex:i] ];
|
|
#else
|
|
NSInteger myFlags = isRowFlag | (isEventLog ? isEventLogFlag : 0);
|
|
EventLogCellUIElement * cell = [EventLogCellUIElement elementWithRow:row column:i listFlags:myFlags listCtrl:pList parent:self BOINCView:nil]; // Retain???
|
|
[visibleChildren addObject:cell];
|
|
#endif
|
|
}
|
|
|
|
cellLeftEdge = cellRightEdge;
|
|
}
|
|
|
|
return visibleChildren;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
|
|
return [parent window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityIndexAttribute]) {
|
|
return [NSNumber numberWithInt:row];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySelectedAttribute]) {
|
|
BOOL isSelected = pList->GetItemState(row, wxLIST_STATE_SELECTED) & wxLIST_STATE_SELECTED;
|
|
return [NSNumber numberWithBool:isSelected];
|
|
|
|
} else {
|
|
return nil;
|
|
// return [super accessibilityAttributeValue:attribute];
|
|
}
|
|
}
|
|
|
|
- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
|
|
// return [super accessibilityIsAttributeSettable:attribute];
|
|
return NO;
|
|
}
|
|
|
|
- (NSArray *)accessibilityActionNames {
|
|
return [NSArray array];
|
|
}
|
|
|
|
- (NSString *)accessibilityActionDescription:(NSString *)action {
|
|
return nil;
|
|
}
|
|
|
|
- (void)accessibilityPerformAction:(NSString *)action {
|
|
}
|
|
|
|
- (void)dealloc {
|
|
#if RETAIN_KIDS_OF_ROWS
|
|
for (NSInteger i = 0; i < numkids; ++i) {
|
|
[(EventLogCellUIElement*)[kids objectAtIndex:i] release];
|
|
}
|
|
//TODO: Do we need to delete or free up kids array?
|
|
if (kids) [kids release];
|
|
#endif
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
|
|
@interface FauxListBodyView : NSView {
|
|
BOOL isHeader;
|
|
BOOL isEventLog;
|
|
wxGenericListCtrl *pList;
|
|
NSMutableArray *kids;
|
|
NSInteger numkids;
|
|
id parent;
|
|
CBOINCBaseView *BOINCView;
|
|
}
|
|
|
|
- (id)initWithFrame:(NSRect)frame listCtrl:(wxGenericListCtrl *)aListCtrl listFlags:(NSInteger)flags parent:aParent BOINCView:(CBOINCBaseView *)aBOINCView;
|
|
- (void) adjustKidsIfNeeded;
|
|
|
|
@end
|
|
|
|
|
|
@implementation FauxListBodyView
|
|
|
|
- (id)initWithFrame:(NSRect)frame listCtrl:(wxGenericListCtrl *)aListCtrl listFlags:(NSInteger)flags parent:aParent BOINCView:(CBOINCBaseView *)aBOINCView {
|
|
if ((self = [super initWithFrame:frame]) != nil) {
|
|
pList = aListCtrl;
|
|
isEventLog = (flags & isEventLogFlag) ? YES : NO;
|
|
numkids = 0;
|
|
kids = nil;
|
|
parent = aParent;
|
|
BOINCView = aBOINCView;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)isFlipped {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)accessibilityIsIgnored {
|
|
return NO;
|
|
}
|
|
|
|
- (id)accessibilityHitTest:(NSPoint)point {
|
|
NSInteger rowNumber;
|
|
id item;
|
|
|
|
[self adjustKidsIfNeeded];
|
|
|
|
NSPoint windowPoint = [[parent window] convertScreenToBase:point]; // parent == listControlView
|
|
NSPoint localPoint = [parent convertPointFromBase:windowPoint];
|
|
|
|
// The scroll bars are among the list control's subviews.
|
|
// If we are outside the list control's client area, determine
|
|
// which scroll bar we are over and return the info for it.
|
|
wxRect wxcr = pList->GetClientRect();
|
|
NSRect cr;
|
|
wxRectToNSRect(wxcr, cr);
|
|
if (!NSPointInRect(localPoint, cr)) {
|
|
NSArray *mySubViews = [parent subviews];
|
|
NSInteger nSubViews = [mySubViews count];
|
|
for (NSInteger i=0; i<nSubViews; i++) {
|
|
NSView *aView = [mySubViews objectAtIndex:i];
|
|
if ([aView isKindOfClass:[NSScroller class]]) {
|
|
if (NSPointInRect(localPoint, [aView frame ])) {
|
|
return [[mySubViews objectAtIndex:i] accessibilityHitTest:point];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (kids) {
|
|
// Check only rows that are visible
|
|
NSInteger firstVisibleRow = pList->GetTopItem();
|
|
NSInteger numVisibleRows = pList->GetCountPerPage();
|
|
|
|
if (numkids <= (firstVisibleRow + numVisibleRows)) {
|
|
numVisibleRows = numkids - firstVisibleRow;
|
|
}
|
|
// This allows for an additional partially visible row
|
|
NSInteger lastVisibleRow = firstVisibleRow + numVisibleRows;
|
|
if (lastVisibleRow > (numkids - 1)) {
|
|
lastVisibleRow = (numkids - 1);
|
|
}
|
|
|
|
for (rowNumber = firstVisibleRow; rowNumber <= lastVisibleRow; ++rowNumber) {
|
|
NSRect r;
|
|
r = [parent bounds];
|
|
r.origin.x = 0;
|
|
r.size.width = wxcr.width;
|
|
r.size.height = wxcr.height;
|
|
|
|
wxRect wxr;
|
|
pList->GetItemRect(rowNumber, wxr);
|
|
r.origin.y = wxr.y;
|
|
// r.origin.y = r.size.height - wxr.y; // Convert to Quartz coordinates
|
|
r.size.height = wxr.height;
|
|
if (!NSPointInRect(localPoint, r)) continue;
|
|
|
|
//TODO: should we just generate a temporary FauxListRow object as needed?
|
|
FauxListRow *row = (FauxListRow *)[kids objectAtIndex:rowNumber];
|
|
item = [row accessibilityHitTest:point];
|
|
if (item != nil) {
|
|
return item;
|
|
}
|
|
}
|
|
}
|
|
|
|
return self;
|
|
// return [super accessibilityHitTest:point];
|
|
}
|
|
|
|
- (void)adjustKidsIfNeeded {
|
|
NSInteger i, newNumKids;
|
|
newNumKids = pList->GetItemCount(); // Number of rows
|
|
|
|
if (kids == nil) {
|
|
kids = [[NSMutableArray arrayWithCapacity:newNumKids] retain];
|
|
}
|
|
if (newNumKids < numkids) {
|
|
for (i = numkids; i > newNumKids; i--) {
|
|
[[kids objectAtIndex:i-1] release];
|
|
[kids removeLastObject];
|
|
}
|
|
numkids = newNumKids;
|
|
} else if (newNumKids > numkids) {
|
|
NSInteger myFlags = isRowFlag | (isEventLog ? isEventLogFlag : 0);
|
|
for (i = numkids; i < newNumKids; ++i) {
|
|
FauxListRow *row = [FauxListRow listRowWithRow:i listFlags:myFlags listCtrl:pList parent:self];
|
|
[row retain];
|
|
[kids addObject:row];
|
|
}
|
|
numkids = newNumKids;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// accessibility protocol
|
|
//
|
|
|
|
// attributes
|
|
|
|
- (NSArray *)accessibilityAttributeNames {
|
|
static NSArray *attributes = nil;
|
|
if (attributes == nil) {
|
|
attributes = [[NSArray alloc] initWithObjects:
|
|
NSAccessibilityRoleAttribute,
|
|
NSAccessibilityRoleDescriptionAttribute,
|
|
NSAccessibilityFocusedAttribute,
|
|
NSAccessibilityParentAttribute,
|
|
NSAccessibilityWindowAttribute,
|
|
NSAccessibilityTopLevelUIElementAttribute,
|
|
NSAccessibilityPositionAttribute,
|
|
NSAccessibilitySizeAttribute,
|
|
NSAccessibilityChildrenAttribute,
|
|
NSAccessibilityVisibleChildrenAttribute,
|
|
NSAccessibilitySelectedChildrenAttribute,
|
|
NSAccessibilitySelectedRowsAttribute,
|
|
NSAccessibilityOrientationAttribute,
|
|
// NSAccessibilityHeaderAttribute,
|
|
// NSAccessibilityColumnsAttribute,
|
|
// NSAccessibilityRowsAttribute,
|
|
// NSAccessibilitySelectedColumnsAttribute,
|
|
// NSAccessibilityVisibleColumnsAttribute
|
|
// NSAccessibilityVisibleRowsAttribute,
|
|
NSAccessibilityDescriptionAttribute,
|
|
nil];
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
|
if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
|
return NSAccessibilityListRole;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
|
|
return NSAccessibilityRoleDescription(NSAccessibilityListRole, nil);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
|
|
return NO;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
|
|
return NSAccessibilityUnignoredAncestor(parent);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
|
|
// We're in the same window as our parent.
|
|
return [parent window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
|
|
return [parent window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
|
|
// To allow localization, we can't just append string
|
|
// " is empty" because that assumes English word order.
|
|
wxString s;
|
|
if (isEventLog) {
|
|
if (numkids) {
|
|
s = _("Event Log");
|
|
} else {
|
|
s = _("Event Log is empty");
|
|
}
|
|
} else {
|
|
wxString viewName = BOINCView->GetViewDisplayName();
|
|
if (numkids) {
|
|
s.Printf(_("%s"), viewName.c_str());
|
|
} else {
|
|
s.Printf(_("You currently have no %s"), viewName.c_str());
|
|
}
|
|
}
|
|
NSString *desc = [NSString stringWithUTF8String:(char *)(s.utf8_str().data())];
|
|
return desc;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
|
|
return [NSValue valueWithSize:[parent frame].size];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
|
|
NSPoint pt = [parent bounds].origin;
|
|
pt.y += [parent bounds].size.height; // We need the bottom left corner
|
|
//Convert the point to global (screen) coordinates
|
|
NSPoint windowPoint = [parent convertPoint:pt toView: nil];
|
|
pt = [[parent window] convertBaseToScreen:windowPoint];
|
|
|
|
return [NSValue valueWithPoint:pt];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
|
|
[self adjustKidsIfNeeded];
|
|
return kids;
|
|
// return NSAccessibilityUnignoredChildren(kids); // No children are ignored
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityVisibleChildrenAttribute]) {
|
|
NSInteger i;
|
|
NSInteger firstVisibleKid = pList->GetTopItem();
|
|
NSInteger numVisibleKids = pList->GetCountPerPage();
|
|
NSMutableArray *visibleChildren = [NSMutableArray array];
|
|
|
|
[self adjustKidsIfNeeded];
|
|
|
|
//TODO: allow for additional partially visible rows at bottom??
|
|
if (numkids <= (firstVisibleKid + numVisibleKids)) {
|
|
numVisibleKids = numkids - firstVisibleKid;
|
|
}
|
|
for (i = 0; i < numVisibleKids; ++i) {
|
|
[visibleChildren addObject:[kids objectAtIndex:i+firstVisibleKid] ];
|
|
}
|
|
|
|
return visibleChildren;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySelectedChildrenAttribute] ||
|
|
[attribute isEqualToString:NSAccessibilitySelectedRowsAttribute]) {
|
|
NSMutableArray *selectedChildren = [NSMutableArray array];
|
|
|
|
int i = -1; // Step through all selected items
|
|
while (1) {
|
|
i = pList->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
if (i < 0) break;
|
|
[selectedChildren addObject:[kids objectAtIndex:i] ];
|
|
}
|
|
return selectedChildren;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityOrientationAttribute]) {
|
|
return NSAccessibilityVerticalOrientationValue;
|
|
|
|
} else {
|
|
return [super accessibilityAttributeValue:attribute];
|
|
}
|
|
}
|
|
|
|
- (NSArray *)accessibilityActionNames {
|
|
return [NSArray arrayWithObject:NSAccessibilityPressAction];
|
|
}
|
|
|
|
- (NSString *)accessibilityActionDescription:(NSString *)action {
|
|
return NSAccessibilityActionDescription(action);
|
|
}
|
|
|
|
// If user does a simulated click below last row, deselect all rows
|
|
- (void)accessibilityPerformAction:(NSString *)action {
|
|
if ([self accessibilityHitTest:[NSEvent mouseLocation]] == self) {
|
|
int i = -1; // Step through all selected items and deselect each one
|
|
while (1) {
|
|
i = pList->GetNextItem(i, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
|
|
if (i < 0) break;
|
|
pList->SetItemState(i, 0, wxLIST_STATE_SELECTED);
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void)dealloc {
|
|
//TODO: remove this if we don't retain rows in kids
|
|
for (NSInteger i = 0; i < numkids; ++i) {
|
|
[(FauxListRow *)[kids objectAtIndex:i] release];
|
|
}
|
|
//TODO: Do we need to delete or free up kids array?
|
|
if (kids) [kids release];
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
|
|
@interface FauxListHeaderView : NSView {
|
|
wxGenericListCtrl *pList;
|
|
BOOL isEventLog;
|
|
NSMutableArray *kids;
|
|
NSInteger numkids;
|
|
id parent;
|
|
CBOINCBaseView *BOINCView;
|
|
}
|
|
|
|
- (id)initWithFrame:(NSRect)frame listCtrl:(wxGenericListCtrl *)aListCtrl listFlags:(NSInteger)flags parent:aParent BOINCView:(CBOINCBaseView *)aBOINCView;
|
|
|
|
@end
|
|
|
|
|
|
@implementation FauxListHeaderView
|
|
|
|
- (id)initWithFrame:(NSRect)frame listCtrl:(wxGenericListCtrl *)aListCtrl listFlags:(NSInteger)flags parent:aParent BOINCView:(CBOINCBaseView *)aBOINCView
|
|
{
|
|
if ((self = [super initWithFrame:frame]) != nil) {
|
|
pList = aListCtrl;
|
|
isEventLog = (flags & isEventLogFlag) ? YES : NO;
|
|
numkids = 0;
|
|
kids = nil;
|
|
parent = aParent;
|
|
BOINCView = aBOINCView;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)isFlipped {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)accessibilityIsIgnored {
|
|
return NO;
|
|
}
|
|
|
|
- (id)accessibilityHitTest:(NSPoint)point {
|
|
NSPoint windowPoint = [[parent window] convertScreenToBase:point]; // parent == listControlView
|
|
NSPoint localPoint = [parent convertPointFromBase:windowPoint];
|
|
|
|
// The scroll bars are among the list control's subviews.
|
|
// If we are outside the list control's client area, determine
|
|
// which scroll bar we are over and return the info for it.
|
|
wxRect wxcr = pList->GetClientRect();
|
|
NSRect cr;
|
|
wxRectToNSRect(wxcr, cr);
|
|
if (!NSPointInRect(localPoint, cr)) {
|
|
NSArray *mySubViews = [parent subviews];
|
|
NSInteger nSubViews = [mySubViews count];
|
|
for (NSInteger i=0; i<nSubViews; i++) {
|
|
NSView *aView = [mySubViews objectAtIndex:i];
|
|
if ([aView isKindOfClass:[NSScroller class]]) {
|
|
if (NSPointInRect(localPoint, [aView frame ])) {
|
|
return [[mySubViews objectAtIndex:i] accessibilityHitTest:point];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (kids) {
|
|
NSInteger col, numCols, x = 0, yoff;
|
|
pList->CalcScrolledPosition(0, 0, &x, &yoff);
|
|
|
|
numCols = pList->GetColumnCount();
|
|
for (col=0; col<numCols; col++) {
|
|
// First get the position relative to the ListCtrl
|
|
NSRect r = [parent bounds];
|
|
r.origin.x = x;
|
|
r.size.width = pList->GetColumnWidth(col);
|
|
r.size.height = [self bounds].size.height;
|
|
if (NSPointInRect(localPoint, r)){
|
|
return (EventLogCellUIElement *)[kids objectAtIndex:col];
|
|
}
|
|
x += r.size.width;
|
|
}
|
|
}
|
|
return self;
|
|
// return [super accessibilityHitTest:point];
|
|
}
|
|
|
|
//
|
|
// accessibility protocol
|
|
//
|
|
|
|
// attributes
|
|
|
|
- (NSArray *)accessibilityAttributeNames {
|
|
static NSArray *attributes = nil;
|
|
if (attributes == nil) {
|
|
attributes = [[NSArray alloc] initWithObjects:
|
|
NSAccessibilityRoleAttribute,
|
|
NSAccessibilityRoleDescriptionAttribute,
|
|
NSAccessibilityFocusedAttribute,
|
|
NSAccessibilityParentAttribute,
|
|
NSAccessibilityWindowAttribute,
|
|
NSAccessibilityTopLevelUIElementAttribute,
|
|
NSAccessibilityPositionAttribute,
|
|
NSAccessibilitySizeAttribute,
|
|
NSAccessibilityChildrenAttribute,
|
|
//TODO: Why does NSAccessibilityDescriptionAttribute for header view prevent
|
|
//TODO: its NSAccessibilityChildrenAttribute from ever being called ???
|
|
// NSAccessibilityDescriptionAttribute,
|
|
nil];
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
|
if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
|
return NSAccessibilityGroupRole;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
|
|
return NSAccessibilityRoleDescription(NSAccessibilityGroupRole, nil);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
|
|
return NO;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
|
|
return NSAccessibilityUnignoredAncestor(parent);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
|
|
// We're in the same window as our parent.
|
|
return [parent window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
|
|
return [parent window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
|
|
return [NSValue valueWithSize:[self frame].size];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
|
|
NSPoint pt = [self bounds].origin;
|
|
pt.y += [self bounds].size.height; // We need the bottom left corner
|
|
//Convert the point to global (screen) coordinates
|
|
// NSPoint windowPoint = [self convertPoint:pt toView: nil];
|
|
NSPoint windowPoint = [parent convertPoint:pt toView: nil];
|
|
pt = [[parent window] convertBaseToScreen:windowPoint];
|
|
|
|
return [NSValue valueWithPoint:pt];
|
|
|
|
//TODO: Why does NSAccessibilityDescriptionAttribute for header view prevent
|
|
//TODO: its NSAccessibilityChildrenAttribute from ever being called ???
|
|
} else if ([attribute isEqualToString:NSAccessibilityDescriptionAttribute]) {
|
|
wxString s;
|
|
if (isEventLog) {
|
|
s = _("Header for Event Log");
|
|
} else {
|
|
wxString viewName = BOINCView->GetViewDisplayName();
|
|
s.Printf(_("Header for %s"), viewName.c_str());
|
|
}
|
|
NSString *desc = [NSString stringWithUTF8String:(char *)(s.utf8_str().data())];
|
|
return desc;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
|
|
if (kids == nil) {
|
|
numkids = pList->GetColumnCount();
|
|
kids = [[NSMutableArray arrayWithCapacity:numkids] retain];
|
|
// The number of columns never changes
|
|
NSInteger myFlags = isHeaderFlag | (isEventLog ? isEventLogFlag : 0);
|
|
for (NSInteger i = 0; i < numkids; ++i) {
|
|
EventLogCellUIElement *cell = [EventLogCellUIElement elementWithRow:0 column:i listFlags:myFlags listCtrl:pList parent:self BOINCView:BOINCView];
|
|
[cell retain];
|
|
[kids addObject:cell];
|
|
}
|
|
}
|
|
|
|
return kids;
|
|
// return NSAccessibilityUnignoredChildren(kids); // No children are ignored
|
|
|
|
} else {
|
|
return [super accessibilityAttributeValue:attribute];
|
|
}
|
|
}
|
|
|
|
- (void)dealloc {
|
|
for (NSInteger i = 0; i < numkids; ++i) {
|
|
[(EventLogCellUIElement *)[kids objectAtIndex:i] release];
|
|
}
|
|
//TODO: Do we need to delete or free up kids array?
|
|
if (kids) [kids release];
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark === CDlgEventLogListCtrl Accessibility Support ===
|
|
|
|
void CDlgEventLogListCtrl::SetupMacAccessibilitySupport() {
|
|
NSView *listControlView = GetHandle();
|
|
NSRect r = [ listControlView bounds ];
|
|
FauxListHeaderView *fauxHeaderView = [ FauxListHeaderView alloc ];
|
|
FauxListBodyView *fauxBodyView = [ FauxListBodyView alloc ];
|
|
|
|
NSRect rh = r;
|
|
rh.size.height = ((wxWindow *)m_headerWin)->GetSize().y;
|
|
|
|
[fauxHeaderView initWithFrame:rh listCtrl:this listFlags:(isHeaderFlag | isEventLogFlag) parent:listControlView BOINCView:nil];
|
|
[listControlView addSubview:fauxHeaderView ];
|
|
|
|
NSRect rb = r;
|
|
rb.origin.y += ((wxWindow *)m_headerWin)->GetSize().y;
|
|
rb.size.height -= ((wxWindow *)m_headerWin)->GetSize().y;
|
|
[fauxBodyView initWithFrame:rb listCtrl:this listFlags:(isEventLogFlag) parent:listControlView BOINCView:nil];
|
|
[listControlView addSubview:fauxBodyView ];
|
|
|
|
m_fauxHeaderView = fauxHeaderView;
|
|
m_fauxBodyView = fauxBodyView;
|
|
}
|
|
|
|
|
|
void CDlgEventLogListCtrl::OnSize(wxSizeEvent& event) {
|
|
NSView *listControlView = GetHandle();
|
|
NSRect r = [ listControlView bounds ];
|
|
FauxListHeaderView *fauxHeaderView = (FauxListHeaderView *)m_fauxHeaderView;
|
|
FauxListBodyView *fauxBodyView = (FauxListBodyView *)m_fauxBodyView;
|
|
|
|
if (fauxHeaderView) {
|
|
NSRect rh = r;
|
|
rh.size.height = ((wxWindow *)m_headerWin)->GetSize().y;
|
|
[fauxHeaderView setFrame:rh];
|
|
}
|
|
|
|
if (fauxBodyView) {
|
|
NSRect rb = r;
|
|
rb.origin.y += ((wxWindow *)m_headerWin)->GetSize().y;
|
|
rb.size.height -= ((wxWindow *)m_headerWin)->GetSize().y;
|
|
wxString s = wxEmptyString;
|
|
[fauxBodyView setFrame:rb];
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
void CDlgEventLogListCtrl::RemoveMacAccessibilitySupport() {
|
|
[(FauxListHeaderView *)m_fauxHeaderView release];
|
|
m_fauxHeaderView = nil;
|
|
[(FauxListBodyView *)m_fauxBodyView release];
|
|
m_fauxBodyView = nil;
|
|
}
|
|
|
|
#pragma mark === CBOINCListCtrl Accessibility Support ===
|
|
|
|
void CBOINCListCtrl::SetupMacAccessibilitySupport() {
|
|
NSView *listControlView = GetHandle();
|
|
NSRect r = [ listControlView bounds ];
|
|
FauxListHeaderView *fauxHeaderView = [ FauxListHeaderView alloc ];
|
|
FauxListBodyView *fauxBodyView = [ FauxListBodyView alloc ];
|
|
|
|
NSRect rh = r;
|
|
rh.size.height = ((wxWindow *)m_headerWin)->GetSize().y;
|
|
|
|
[fauxHeaderView initWithFrame:rh listCtrl:this listFlags:isHeaderFlag parent:listControlView BOINCView:m_pParentView ];
|
|
[listControlView addSubview:fauxHeaderView ];
|
|
|
|
NSRect rb = r;
|
|
rb.origin.y += ((wxWindow *)m_headerWin)->GetSize().y;
|
|
rb.size.height -= ((wxWindow *)m_headerWin)->GetSize().y;
|
|
[fauxBodyView initWithFrame:rb listCtrl:this listFlags:0 parent:listControlView BOINCView:m_pParentView];
|
|
[listControlView addSubview:fauxBodyView ];
|
|
|
|
m_fauxHeaderView = fauxHeaderView;
|
|
m_fauxBodyView = fauxBodyView;
|
|
}
|
|
|
|
|
|
void CBOINCListCtrl::OnSize(wxSizeEvent& event) {
|
|
NSView *listControlView = GetHandle();
|
|
NSRect r = [ listControlView bounds ];
|
|
FauxListHeaderView *fauxHeaderView = (FauxListHeaderView *)m_fauxHeaderView;
|
|
FauxListBodyView *fauxBodyView = (FauxListBodyView *)m_fauxBodyView;
|
|
|
|
if (fauxHeaderView) {
|
|
NSRect rh = r;
|
|
rh.size.height = ((wxWindow *)m_headerWin)->GetSize().y;
|
|
[fauxHeaderView setFrame:rh];
|
|
}
|
|
|
|
if (fauxBodyView) {
|
|
NSRect rb = r;
|
|
rb.origin.y += ((wxWindow *)m_headerWin)->GetSize().y;
|
|
rb.size.height -= ((wxWindow *)m_headerWin)->GetSize().y;
|
|
wxString s = wxEmptyString;
|
|
[fauxBodyView setFrame:rb];
|
|
}
|
|
|
|
event.Skip();
|
|
}
|
|
|
|
|
|
void CBOINCListCtrl::RemoveMacAccessibilitySupport() {
|
|
[(FauxListHeaderView *)m_fauxHeaderView release];
|
|
m_fauxHeaderView = nil;
|
|
[(FauxListBodyView *)m_fauxBodyView release];
|
|
m_fauxBodyView = nil;
|
|
}
|
|
|
|
#pragma mark === CPaintStatistics & wxPieCtrl Accessibility Shared Code ===
|
|
|
|
#define statisticsPage 1
|
|
#define resourcesPage 2
|
|
|
|
@interface FauxGeneralView : NSView {
|
|
id parent;
|
|
NSInteger viewPage;
|
|
void* theClass;
|
|
}
|
|
|
|
- (id)initWithFrame:(NSRect)frame whichViewPage:(NSInteger)aViewPage callingClass:(void*)aClass parent:aParent;
|
|
- (NSString*) getValue;
|
|
@end
|
|
|
|
|
|
@implementation FauxGeneralView
|
|
|
|
- (id)initWithFrame:(NSRect)frame whichViewPage:(NSInteger)aViewPage callingClass:(void*)aClass parent:aParent
|
|
{
|
|
[super initWithFrame:frame];
|
|
parent = aParent;
|
|
viewPage = aViewPage;
|
|
theClass = aClass;
|
|
return self;
|
|
}
|
|
|
|
- (BOOL)isFlipped {
|
|
return YES;
|
|
}
|
|
|
|
- (BOOL)accessibilityIsIgnored {
|
|
return NO;
|
|
}
|
|
|
|
- (NSString*) getValue {
|
|
wxString s;
|
|
|
|
switch (viewPage) {
|
|
case statisticsPage:
|
|
s = _("This panel contains graphs showing user totals for projects");
|
|
break;
|
|
case resourcesPage:
|
|
{
|
|
wxPieCtrl* pPieCtrl = (wxPieCtrl*)theClass;
|
|
s = pPieCtrl->GetLabel();
|
|
unsigned int i;
|
|
|
|
for(i=0; i<pPieCtrl->m_Series.Count(); i++) {
|
|
s += wxT("; ");
|
|
s += pPieCtrl->m_Series[i].GetLabel();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
s = wxEmptyString;
|
|
break;
|
|
}
|
|
NSString *desc = [NSString stringWithUTF8String:(char *)(s.utf8_str().data())];
|
|
return desc;
|
|
}
|
|
|
|
|
|
- (NSArray *)accessibilityAttributeNames {
|
|
static NSArray *attributes = nil;
|
|
if (attributes == nil) {
|
|
attributes = [[NSArray alloc] initWithObjects:
|
|
NSAccessibilityEnabledAttribute,
|
|
NSAccessibilityFocusedAttribute,
|
|
NSAccessibilityNumberOfCharactersAttribute,
|
|
NSAccessibilityParentAttribute,
|
|
NSAccessibilityPositionAttribute,
|
|
NSAccessibilityRoleAttribute,
|
|
NSAccessibilityRoleDescriptionAttribute,
|
|
NSAccessibilitySelectedTextAttribute,
|
|
NSAccessibilitySelectedTextRangeAttribute,
|
|
NSAccessibilityValueAttribute,
|
|
NSAccessibilityVisibleCharacterRangeAttribute,
|
|
NSAccessibilitySizeAttribute,
|
|
NSAccessibilityTopLevelUIElementAttribute,
|
|
NSAccessibilityWindowAttribute,
|
|
nil];
|
|
}
|
|
return attributes;
|
|
}
|
|
|
|
- (id)accessibilityAttributeValue:(NSString *)attribute {
|
|
if ([attribute isEqualToString:NSAccessibilityEnabledAttribute]) {
|
|
return [NSNumber numberWithBool:YES];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
|
|
return NO;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute]) {
|
|
NSString *s = [self getValue];
|
|
NSUInteger n = [s lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
|
|
return [NSNumber numberWithUnsignedInt:n];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityParentAttribute]) {
|
|
return NSAccessibilityUnignoredAncestor(parent);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityPositionAttribute]) {
|
|
NSPoint pt = [self bounds].origin;
|
|
pt.y += [self bounds].size.height; // We need the bottom left corner
|
|
//Convert the point to global (screen) coordinates
|
|
// NSPoint windowPoint = [self convertPoint:pt toView: nil];
|
|
NSPoint windowPoint = [parent convertPoint:pt toView: nil];
|
|
pt = [[parent window] convertBaseToScreen:windowPoint];
|
|
|
|
return [NSValue valueWithPoint:pt];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityRoleAttribute]) {
|
|
return NSAccessibilityStaticTextRole;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
|
|
return NSAccessibilityRoleDescription(NSAccessibilityStaticTextRole, nil);
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
|
|
NSString *s = [NSString init];
|
|
return s;
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
|
|
NSRange range = NSMakeRange(0, 0);
|
|
return [NSValue valueWithRange:range];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
|
|
return [self getValue];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) {
|
|
NSString *s = [self getValue];
|
|
NSRange range = NSMakeRange(0, [s length]);
|
|
return [NSValue valueWithRange:range];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilitySizeAttribute]) {
|
|
return [NSValue valueWithSize:[self frame].size];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute]) {
|
|
return [parent window];
|
|
|
|
} else if ([attribute isEqualToString:NSAccessibilityWindowAttribute]) {
|
|
// We're in the same window as our parent.
|
|
return [parent window];
|
|
|
|
} else {
|
|
return [super accessibilityAttributeValue:attribute];
|
|
}
|
|
}
|
|
|
|
- (void)dealloc {
|
|
[super dealloc];
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark === CPaintStatistics Accessibility Support ===
|
|
|
|
void CPaintStatistics::SetupMacAccessibilitySupport() {
|
|
NSView *statisticsView = GetHandle();
|
|
NSRect r = [ statisticsView bounds ];
|
|
FauxGeneralView *fauxStatisticsView = [FauxGeneralView alloc ];
|
|
[fauxStatisticsView initWithFrame:r whichViewPage:statisticsPage callingClass:this parent:statisticsView];
|
|
[ statisticsView addSubview:fauxStatisticsView ];
|
|
m_fauxStatisticsView = fauxStatisticsView;
|
|
}
|
|
|
|
|
|
void CPaintStatistics::ResizeMacAccessibilitySupport() {
|
|
NSView *statisticsView = GetHandle();
|
|
NSRect r = [ statisticsView bounds ];
|
|
FauxGeneralView *fauxStatisticsView = (FauxGeneralView *)m_fauxStatisticsView;
|
|
if (fauxStatisticsView) {
|
|
[ fauxStatisticsView setFrame:r ];
|
|
}
|
|
}
|
|
|
|
void CPaintStatistics::RemoveMacAccessibilitySupport() {
|
|
[(FauxGeneralView *)m_fauxStatisticsView release];
|
|
m_fauxStatisticsView = nil;
|
|
}
|
|
|
|
#pragma mark === wxPieCtrl Accessibility Support ===
|
|
|
|
void wxPieCtrl::SetupMacAccessibilitySupport() {
|
|
NSView *resourcesView = GetHandle();
|
|
NSRect r = [ resourcesView bounds ];
|
|
FauxGeneralView *fauxResourcesView = [FauxGeneralView alloc ];
|
|
[fauxResourcesView initWithFrame:r whichViewPage:resourcesPage callingClass:this parent:resourcesView];
|
|
[ resourcesView addSubview:fauxResourcesView ];
|
|
m_fauxResourcesView = fauxResourcesView;
|
|
}
|
|
|
|
|
|
void wxPieCtrl::ResizeMacAccessibilitySupport() {
|
|
NSView *resourcesView = GetHandle();
|
|
NSRect r = [ resourcesView bounds ];
|
|
FauxGeneralView *fauxResourcesView = (FauxGeneralView *)m_fauxResourcesView;
|
|
if (fauxResourcesView) {
|
|
[ fauxResourcesView setFrame:r ];
|
|
}
|
|
}
|
|
|
|
void wxPieCtrl::RemoveMacAccessibilitySupport() {
|
|
[(FauxGeneralView *)m_fauxResourcesView release];
|
|
m_fauxResourcesView = nil;
|
|
}
|