diff --git a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java index dd72252f675..79f28d13c08 100644 --- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java +++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java @@ -376,6 +376,15 @@ public ListViewSkin(final ListView control) { } return null; } + case VISIBLE_ITEM_RANGE: { + ListCell firstVisibleCell = flow.getFirstVisibleCellWithinViewport(); + ListCell lastVisibleCell = flow.getLastVisibleCellWithinViewport(); + if (firstVisibleCell == null || lastVisibleCell == null) { + return new int[] { 0, 0 }; + } + int firstIndex = firstVisibleCell.getIndex(); + return new int[] { firstIndex, lastVisibleCell.getIndex() - firstIndex + 1 }; + } case SELECTED_ITEMS: { MultipleSelectionModel sm = getSkinnable().getSelectionModel(); if (sm == null) { diff --git a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkin.java b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkin.java index d4f3dff7a01..8f6f9acb44d 100644 --- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkin.java +++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkin.java @@ -147,6 +147,10 @@ public void dispose() { /** {@inheritDoc} */ @Override public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { switch (attribute) { + case ROW_AT_INDEX: { + final int rowIndex = (Integer)parameters[0]; + return rowIndex < 0 ? null : flow.getPrivateCell(rowIndex); + } case SELECTED_ITEMS: { List selection = new ArrayList<>(); TableViewSelectionModel sm = getSkinnable().getSelectionModel(); diff --git a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkinBase.java b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkinBase.java index 5cd4bc7293b..657d5798cf0 100644 --- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkinBase.java +++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkinBase.java @@ -1066,6 +1066,15 @@ private boolean isCellFocused(int row) { * look for column headers */ return getTableHeaderRow(); } + case VISIBLE_ITEM_RANGE: { + I firstVisibleCell = flow.getFirstVisibleCellWithinViewport(); + I lastVisibleCell = flow.getLastVisibleCellWithinViewport(); + if (firstVisibleCell == null || lastVisibleCell == null) { + return new int[] { 0, 0 }; + } + int firstIndex = firstVisibleCell.getIndex(); + return new int[] { firstIndex, lastVisibleCell.getIndex() - firstIndex + 1 }; + } case VERTICAL_SCROLLBAR: return flow.getVbar(); case HORIZONTAL_SCROLLBAR: return flow.getHbar(); default: return super.queryAccessibleAttribute(attribute, parameters); diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java index 6651b10af80..0391c300f9a 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,7 +27,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import java.util.function.Function; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -119,6 +121,7 @@ private static enum MacAttribute { AXMenuItemCmdGlyph(ACCELERATOR, MacVariant::createNSNumberForInt), AXMenuItemCmdModifiers(ACCELERATOR, MacVariant::createNSNumberForInt), AXMenuItemMarkChar(SELECTED, MacVariant::createNSString), + AXVisibleItemRange(VISIBLE_ITEM_RANGE, MacVariant::createNSValueForRange), AXDateTimeComponents(null, MacVariant::createNSNumberForInt), // NSAccessibilityMenuRole @@ -1300,6 +1303,31 @@ private MacVariant accessibilityAttributeValue(long attribute) { return null; } Object result = getAttribute(jfxAttr); + if (attr == MacAttribute.NSAccessibilitySelectedRowsAttribute + && role == AccessibleRole.TABLE_VIEW) { + @SuppressWarnings("unchecked") + ObservableList selectedCells = (ObservableList)result; + Set selectedRows = new LinkedHashSet<>(); + if (selectedCells != null) { + for (Node cell : selectedCells) { + Accessible cellAccessible = getAccessible(cell); + if (cellAccessible == null) { + continue; + } + + Integer rowIndex = (Integer)cellAccessible.getAttribute(ROW_INDEX); + if (rowIndex == null) { + continue; + } + + Node row = (Node)getAttribute(ROW_AT_INDEX, rowIndex); + if (row != null) { + selectedRows.add(row); + } + } + } + result = FXCollections.observableArrayList(selectedRows); + } if (result == null) { switch (attr) { case NSAccessibilityParentAttribute: break; @@ -1696,12 +1724,15 @@ private void accessibilitySetValue(long value, long attribute) { if (variant != null && variant.longArray != null && variant.longArray.length > 0) { long[] ids = variant.longArray; ObservableList items = FXCollections.observableArrayList(); + AccessibleRole controlRole = (AccessibleRole)getAttribute(ROLE); for (long id : ids) { MacAccessible acc = GlassAccessibleToMacAccessible(id); if (acc != null) { Integer index = (Integer)acc.getAttribute(INDEX); if (index != null) { - Node cell = (Node)getAttribute(ROW_AT_INDEX, index); + AccessibleAttribute itemAttribute = controlRole == AccessibleRole.LIST_VIEW + ? ITEM_AT_INDEX : ROW_AT_INDEX; + Node cell = (Node)getAttribute(itemAttribute, index); if (cell != null) { items.add(cell); } diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java b/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java index 95cc1900a34..af2ce016819 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java @@ -801,6 +801,20 @@ public enum AccessibleAttribute { */ VERTICAL_SCROLLBAR(Node.class), + /** + * Returns the range of visible items in an indexed control. + *
    + *
  • Used by: ListView and TableView
  • + *
  • Needs notify: no
  • + *
  • Return Type: {@code int[]} containing the first item index and the + * number of visible items
  • + *
  • Parameters:
  • + *
+ * + * @since 28 + */ + VISIBLE_ITEM_RANGE(int[].class), + /** * Returns true if node is visible, otherwise false. *
    diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h index 084dda6e821..0f01329c63b 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h @@ -41,10 +41,13 @@ id jRole; - (id)requestNodeAttribute:(NSString *)attribute; - (id)requestNodeAttribute:(NSString *)attribute forParameter:(id)parameter; - (BOOL)isNodeAttributeSettable:(NSString *)attribute; +- (NSInteger)requestNodeArrayAttributeCount:(NSString *)attribute; +- (NSArray *)requestNodeArrayAttribute:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; - (void)setNodeAttribute:(id)value forAttribute:(NSString *)attribute; - (NSString *)accessibilityPlaceholderValue; - (NSRect)accessibilityFrame; - (id)accessibilityParent; +- (id)accessibilityFocusedUIElement; - (BOOL)isAccessibilityElement; - (BOOL)performAccessibleAction:(NSString*)actionId; + (void) initializeRolesMap; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m index 29d56239a1d..11f5822735e 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m @@ -29,6 +29,7 @@ #import "com_sun_glass_ui_mac_MacAccessible.h" #import "com_sun_glass_ui_mac_MacVariant.h" #import "common.h" +#include static NSMutableDictionary * rolesMap; @@ -50,6 +51,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"JFXImageAccessibility" forKey:@"IMAGE"]; [rolesMap setObject:@"JFXImageAccessibility" forKey:@"IMAGE_VIEW"]; [rolesMap setObject:@"JFXButtonAccessibility" forKey:@"INCREMENT_BUTTON"]; + [rolesMap setObject:@"JFXListAccessibility" forKey:@"LIST_VIEW"]; [rolesMap setObject:@"JFXMenuItemAccessibility" forKey:@"MENU"]; [rolesMap setObject:@"JFXMenuBarAccessibility" forKey:@"MENU_BAR"]; [rolesMap setObject:@"JFXMenuItemAccessibility" forKey:@"MENU_ITEM"]; @@ -63,6 +65,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"JFXButtonAccessibility" forKey:@"SPLIT_MENU_BUTTON"]; [rolesMap setObject:@"JFXRadiobuttonAccessibility" forKey:@"TAB_ITEM"]; [rolesMap setObject:@"JFXTabGroupAccessibility" forKey:@"TAB_PANE"]; + [rolesMap setObject:@"JFXTableAccessibility" forKey:@"TABLE_VIEW"]; [rolesMap setObject:@"JFXStaticTextAccessibility" forKey:@"TEXT"]; [rolesMap setObject:@"JFXNavigableTextAccessibility" forKey:@"TEXT_AREA"]; [rolesMap setObject:@"JFXNavigableTextAccessibility" forKey:@"TEXT_FIELD"]; @@ -151,6 +154,34 @@ - (id)requestNodeAttribute:(NSString *)attribute forParameter:(id)parameter return variantToID(env, jresult); } +- (NSInteger)requestNodeArrayAttributeCount:(NSString *)attribute +{ + GET_MAIN_JENV; + if (env == NULL) { + return -1; + } + jint jresult = (*env)->CallIntMethod(env, [self getJAccessible], + jAccessibilityArrayAttributeCount, + (jlong)attribute); + GLASS_CHECK_EXCEPTION(env); + return jresult; +} + +- (NSArray *)requestNodeArrayAttribute:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount +{ + GET_MAIN_JENV; + if (env == NULL) { + return nil; + } + jlongArray jresult = (jlongArray)(*env)->CallObjectMethod(env, [self getJAccessible], + jAccessibilityArrayAttributeValues, + (jlong)attribute, + (jint)index, + (jint)maxCount); + GLASS_CHECK_EXCEPTION(env); + return jArrayToNSArray(env, jresult, jLongToID); +} + /* * Check whether an accessibility attribute on the JavaFX Node can be set. */ @@ -227,6 +258,16 @@ - (NSArray *)accessibilityChildren return [self requestNodeAttribute:@"AXChildren"]; } +- (id)accessibilityFocusedUIElement +{ + GET_MAIN_JENV; + if (env == NULL) return NULL; + id result = (id)(*env)->CallLongMethod(env, self->jAccessible, + jAccessibilityFocusedUIElement); + GLASS_CHECK_EXCEPTION(env); + return result; +} + - (id)accessibilityRoleDescription { return [self requestNodeAttribute:@"AXRoleDescription"]; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.h b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.h new file mode 100644 index 00000000000..33473d3b7a1 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "AccessibleBase.h" +#import + +@interface JFXListAccessibility : AccessibleBase { +}; + +- (NSAccessibilityRole)accessibilityRole; +- (NSString *)accessibilityLabel; +- (BOOL)isAccessibilityEnabled; +- (NSArray *)accessibilityChildren; +- (id)accessibilityParent; +- (NSRect)accessibilityFrame; +- (NSInteger)accessibilityColumnCount; +- (NSInteger)accessibilityRowCount; +- (NSArray *)accessibilityRows; +- (NSArray *)accessibilitySelectedRows; +- (void)setAccessibilitySelectedRows:(NSArray *)selectedRows; +@end diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m new file mode 100644 index 00000000000..c807921b100 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "JFXListAccessibility.h" + +@interface JFXListRowPlaceholder : NSAccessibilityElement +@end + +@implementation JFXListRowPlaceholder + +- (jobject)getJAccessible +{ + return nil; +} + +@end + +static const NSInteger ACCESSIBILITY_ROW_WINDOW = 10; +static JFXListRowPlaceholder *rowPlaceholder; +static dispatch_once_t rowPlaceholderOnce; + +static id getRowPlaceholder(void) +{ + dispatch_once(&rowPlaceholderOnce, ^{ + rowPlaceholder = [[JFXListRowPlaceholder alloc] init]; + [rowPlaceholder setAccessibilityRole:NSAccessibilityUnknownRole]; + [rowPlaceholder setAccessibilityElement:NO]; + }); + return rowPlaceholder; +} + +@implementation JFXListAccessibility +- (NSAccessibilityRole)accessibilityRole +{ + // This can change to NSAccessibilityListRole in the future. + return NSAccessibilityTableRole; +} + +- (NSString *)accessibilityLabel +{ + return [super accessibilityLabel]; +} + +- (BOOL)isAccessibilityEnabled +{ + id retVal = [self requestNodeAttribute:@"AXEnabled"]; + if (retVal == NULL) { + return YES; + } + return [retVal boolValue]; +} + +- (NSArray *)accessibilityChildren +{ + return [super accessibilityChildren]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (NSInteger)accessibilityColumnCount +{ + id retVal = [self requestNodeAttribute:@"AXColumnCount"]; + return retVal != NULL ? [retVal integerValue] : 1; +} + +- (NSInteger)accessibilityRowCount +{ + id retVal = [self requestNodeAttribute:@"AXRowCount"]; + if (retVal == NULL) { + return [self accessibilityArrayCountForAttribute:@"AXRows"]; + } + return [retVal integerValue]; +} + +- (NSArray *)accessibilityRows +{ + NSInteger count = [self accessibilityArrayCountForAttribute:@"AXRows"]; + if (count < 0) { + return nil; + } + + NSMutableArray *rows = [NSMutableArray arrayWithCapacity:(NSUInteger)count]; + id placeholder = getRowPlaceholder(); + for (NSInteger index = 0; index < count; index++) { + [rows addObject:placeholder]; + } + + if (count > 0) { + NSInteger firstRequestedRow = 0; + NSInteger lastRequestedRow = MIN(count - 1, ACCESSIBILITY_ROW_WINDOW - 1); + NSRange visibleRange = [[self requestNodeAttribute:@"AXVisibleItemRange"] rangeValue]; + if (visibleRange.length > 0 && visibleRange.location < (NSUInteger)count) { + NSInteger firstVisibleRow = (NSInteger)visibleRange.location; + NSInteger lastVisibleRow = MIN(count - 1, + firstVisibleRow + (NSInteger)visibleRange.length - 1); + firstRequestedRow = MAX(0, firstVisibleRow - ACCESSIBILITY_ROW_WINDOW); + lastRequestedRow = MIN(count - 1, lastVisibleRow + ACCESSIBILITY_ROW_WINDOW); + } + NSUInteger requestedRowCount = (NSUInteger)(lastRequestedRow - firstRequestedRow + 1); + NSArray *requestedRows = [self requestNodeArrayAttribute:@"AXRows" + index:(NSUInteger)firstRequestedRow + maxCount:requestedRowCount]; + for (NSUInteger index = 0; index < requestedRows.count; index++) { + [rows replaceObjectAtIndex:(NSUInteger)firstRequestedRow + index + withObject:[requestedRows objectAtIndex:index]]; + } + } + return rows; +} + +- (NSArray *)accessibilitySelectedRows +{ + return [self requestNodeAttribute:@"AXSelectedRows"]; +} + +- (void)setAccessibilitySelectedRows:(NSArray *)selectedRows +{ + [self setNodeAttribute:selectedRows forAttribute:@"AXSelectedRows"]; +} + +- (NSArray *)accessibilityArrayForAttribute:(NSString *)attribute +{ + NSInteger count = [self accessibilityArrayCountForAttribute:attribute]; + if (count < 0) { + return nil; + } + if (count == 0) { + return @[]; + } + NSArray *result = [self requestNodeArrayAttribute:attribute index:0 maxCount:(NSUInteger)count]; + if (result == nil) { + result = [super accessibilityArrayAttributeValues:attribute index:0 maxCount:(NSUInteger)count]; + } + return result; +} + +- (NSInteger)accessibilityArrayCountForAttribute:(NSString *)attribute +{ + NSInteger count = [self requestNodeArrayAttributeCount:attribute]; + if (count < 0) { + count = [super accessibilityArrayAttributeCount:attribute]; + } + return count; +} + +@end diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.h b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.h new file mode 100644 index 00000000000..abf01bc40ca --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "AccessibleBase.h" +#import + +@interface JFXTableAccessibility : AccessibleBase { +}; + +- (NSAccessibilityRole)accessibilityRole; +- (NSString *)accessibilityLabel; +- (BOOL)isAccessibilityEnabled; +- (NSArray *)accessibilityChildren; +- (id)accessibilityParent; +- (NSRect)accessibilityFrame; +- (id)accessibilityHeader; +- (NSInteger)accessibilityColumnCount; +- (NSInteger)accessibilityRowCount; +- (NSArray *)accessibilityColumns; +- (NSArray *)accessibilityRows; +- (NSArray *)accessibilitySelectedRows; +- (void)setAccessibilitySelectedRows:(NSArray *)selectedRows; +- (NSArray *)accessibilitySelectedCells; +- (void)setAccessibilitySelectedCells:(NSArray *)selectedCells; +- (id)accessibilityCellForColumn:(NSInteger)column row:(NSInteger)row; +@end diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m new file mode 100644 index 00000000000..5b2a71a97fc --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code 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 General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#import "JFXTableAccessibility.h" + +@interface JFXTableRowPlaceholder : NSAccessibilityElement +@end + +@implementation JFXTableRowPlaceholder + +- (jobject)getJAccessible +{ + return nil; +} + +@end + +static const NSInteger ACCESSIBILITY_ROW_WINDOW = 10; +static JFXTableRowPlaceholder *rowPlaceholder; +static dispatch_once_t rowPlaceholderOnce; + +static id getRowPlaceholder(void) +{ + dispatch_once(&rowPlaceholderOnce, ^{ + rowPlaceholder = [[JFXTableRowPlaceholder alloc] init]; + [rowPlaceholder setAccessibilityRole:NSAccessibilityUnknownRole]; + [rowPlaceholder setAccessibilityElement:NO]; + }); + return rowPlaceholder; +} + +@implementation JFXTableAccessibility +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityTableRole; +} + +- (NSString *)accessibilityLabel +{ + return [super accessibilityLabel]; +} + +- (BOOL)isAccessibilityEnabled +{ + id retVal = [self requestNodeAttribute:@"AXEnabled"]; + if (retVal == NULL) { + return YES; + } + return [retVal boolValue]; +} + +- (NSArray *)accessibilityChildren +{ + return [super accessibilityChildren]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityHeader +{ + return [self requestNodeAttribute:@"AXHeader"]; +} + +- (NSInteger)accessibilityColumnCount +{ + id retVal = [self requestNodeAttribute:@"AXColumnCount"]; + if (retVal == NULL) { + return [self accessibilityArrayCountForAttribute:@"AXColumns"]; + } + return [retVal integerValue]; +} + +- (NSInteger)accessibilityRowCount +{ + id retVal = [self requestNodeAttribute:@"AXRowCount"]; + if (retVal == NULL) { + return [self accessibilityArrayCountForAttribute:@"AXRows"]; + } + return [retVal integerValue]; +} + +- (NSArray *)accessibilityColumns +{ + return [self accessibilityArrayForAttribute:@"AXColumns"]; +} + +- (NSArray *)accessibilityRows +{ + NSInteger count = [self accessibilityArrayCountForAttribute:@"AXRows"]; + if (count < 0) { + return nil; + } + + NSMutableArray *rows = [NSMutableArray arrayWithCapacity:(NSUInteger)count]; + id placeholder = getRowPlaceholder(); + for (NSInteger index = 0; index < count; index++) { + [rows addObject:placeholder]; + } + + if (count > 0) { + NSInteger firstRequestedRow = 0; + NSInteger lastRequestedRow = MIN(count - 1, ACCESSIBILITY_ROW_WINDOW - 1); + NSRange visibleRange = [[self requestNodeAttribute:@"AXVisibleItemRange"] rangeValue]; + if (visibleRange.length > 0 && visibleRange.location < (NSUInteger)count) { + NSInteger firstVisibleRow = (NSInteger)visibleRange.location; + NSInteger lastVisibleRow = MIN(count - 1, + firstVisibleRow + (NSInteger)visibleRange.length - 1); + firstRequestedRow = MAX(0, firstVisibleRow - ACCESSIBILITY_ROW_WINDOW); + lastRequestedRow = MIN(count - 1, lastVisibleRow + ACCESSIBILITY_ROW_WINDOW); + } + NSUInteger requestedRowCount = (NSUInteger)(lastRequestedRow - firstRequestedRow + 1); + NSArray *requestedRows = [self requestNodeArrayAttribute:@"AXRows" + index:(NSUInteger)firstRequestedRow + maxCount:requestedRowCount]; + for (NSUInteger index = 0; index < requestedRows.count; index++) { + [rows replaceObjectAtIndex:(NSUInteger)firstRequestedRow + index + withObject:[requestedRows objectAtIndex:index]]; + } + } + return rows; +} + +- (NSArray *)accessibilitySelectedRows +{ + return [self requestNodeAttribute:@"AXSelectedRows"]; +} + +- (void)setAccessibilitySelectedRows:(NSArray *)selectedRows +{ + [self setNodeAttribute:selectedRows forAttribute:@"AXSelectedRows"]; +} + +- (NSArray *)accessibilitySelectedCells +{ + return [self requestNodeAttribute:@"AXSelectedCells"]; +} + +- (void)setAccessibilitySelectedCells:(NSArray *)selectedCells +{ + [self setNodeAttribute:selectedCells forAttribute:@"AXSelectedCells"]; +} + +- (id)accessibilityCellForColumn:(NSInteger)column row:(NSInteger)row +{ + NSArray *parameter = @[[NSNumber numberWithInteger:column], + [NSNumber numberWithInteger:row]]; + return [self requestNodeAttribute:@"AXCellForColumnAndRow" forParameter:parameter]; +} + +- (NSArray *)accessibilityArrayForAttribute:(NSString *)attribute +{ + NSInteger count = [self accessibilityArrayCountForAttribute:attribute]; + if (count < 0) { + return nil; + } + if (count == 0) { + return @[]; + } + NSArray *result = [self requestNodeArrayAttribute:attribute index:0 maxCount:(NSUInteger)count]; + if (result == nil) { + result = [super accessibilityArrayAttributeValues:attribute index:0 maxCount:(NSUInteger)count]; + } + return result; +} + +- (NSInteger)accessibilityArrayCountForAttribute:(NSString *)attribute +{ + NSInteger count = [self requestNodeArrayAttributeCount:attribute]; + if (count < 0) { + count = [super accessibilityArrayAttributeCount:attribute]; + } + return count; +} + +@end