mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-22 18:46:01 +08:00
Added Accessibility to FieldPanel
This commit is contained in:
+35
@@ -0,0 +1,35 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.app.util.viewer.field;
|
||||
|
||||
import docking.widgets.fieldpanel.FieldDescriptionProvider;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
import ghidra.program.util.ProgramLocation;
|
||||
|
||||
public class ListingFieldDescriptionProvider implements FieldDescriptionProvider {
|
||||
|
||||
@Override
|
||||
public String getDescription(FieldLocation loc, Field field) {
|
||||
if (field instanceof ListingField listingField) {
|
||||
FieldFactory fieldFactory = listingField.getFieldFactory();
|
||||
ProgramLocation location = fieldFactory.getProgramLocation(0, 0, listingField);
|
||||
return fieldFactory.getFieldName() + " Field at Address " + location.getAddress() +
|
||||
" text = " + field.getText();
|
||||
}
|
||||
return "Unknown Field";
|
||||
}
|
||||
}
|
||||
+7
-5
@@ -37,8 +37,7 @@ import ghidra.app.plugin.core.codebrowser.LayeredColorModel;
|
||||
import ghidra.app.plugin.core.codebrowser.hover.ListingHoverService;
|
||||
import ghidra.app.services.ButtonPressedListener;
|
||||
import ghidra.app.util.ListingHighlightProvider;
|
||||
import ghidra.app.util.viewer.field.FieldFactory;
|
||||
import ghidra.app.util.viewer.field.ListingField;
|
||||
import ghidra.app.util.viewer.field.*;
|
||||
import ghidra.app.util.viewer.format.FieldHeader;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.app.util.viewer.util.*;
|
||||
@@ -163,7 +162,9 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
|
||||
// extension point
|
||||
protected FieldPanel createFieldPanel(LayoutModel model) {
|
||||
return new FieldPanel(model);
|
||||
FieldPanel fp = new FieldPanel(model, "Listing");
|
||||
fp.setFieldDescriptionProvider(new ListingFieldDescriptionProvider());
|
||||
return fp;
|
||||
}
|
||||
|
||||
// extension point
|
||||
@@ -887,7 +888,7 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
ListingField field = (ListingField) fieldPanel.getFieldAt(point.x, point.y, dropLoc);
|
||||
if (field != null) {
|
||||
return field.getFieldFactory()
|
||||
.getProgramLocation(dropLoc.getRow(), dropLoc.getCol(), field);
|
||||
.getProgramLocation(dropLoc.getRow(), dropLoc.getCol(), field);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1157,7 +1158,8 @@ public class ListingPanel extends JPanel implements FieldMouseListener, FieldLoc
|
||||
}
|
||||
|
||||
public void setFormatManager(FormatManager formatManager) {
|
||||
List<ListingHighlightProvider> highlightProviders = this.formatManager.getHighlightProviders();
|
||||
List<ListingHighlightProvider> highlightProviders =
|
||||
this.formatManager.getHighlightProviders();
|
||||
|
||||
this.formatManager = formatManager;
|
||||
|
||||
|
||||
@@ -392,7 +392,7 @@ public class OptionsGui extends JPanel {
|
||||
* builds the preview panel.
|
||||
*/
|
||||
private JComponent buildPreviewPanel() {
|
||||
fieldPanel = new FieldPanel(new SimpleLayoutModel());
|
||||
fieldPanel = new FieldPanel(new SimpleLayoutModel(), "Preview");
|
||||
IndexedScrollPane scroll = new IndexedScrollPane(fieldPanel);
|
||||
return scroll;
|
||||
}
|
||||
|
||||
+14
-3
@@ -79,7 +79,8 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
||||
*/
|
||||
protected ByteViewerComponent(ByteViewerPanel vpanel, ByteViewerLayoutModel layoutModel,
|
||||
DataFormatModel model, int bytesPerLine, FontMetrics fm) {
|
||||
super(layoutModel);
|
||||
super(layoutModel, "Byte Viewer");
|
||||
setFieldDescriptionProvider((l, f) -> getFieldDescription(l, f));
|
||||
|
||||
this.panel = vpanel;
|
||||
this.model = model;
|
||||
@@ -94,6 +95,17 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
||||
setBackgroundColorModel(new ByteViewerBackgroundColorModel());
|
||||
}
|
||||
|
||||
private String getFieldDescription(FieldLocation fieldLoc, Field field) {
|
||||
ByteBlockInfo info = indexMap.getBlockInfo(fieldLoc.getIndex(), fieldLoc.getFieldNum());
|
||||
if (info != null) {
|
||||
String modelName = model.getName();
|
||||
return modelName + " format at " +
|
||||
info.getBlock().getLocationRepresentation(info.getOffset()) + ", value = " +
|
||||
field.getText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buttonPressed(FieldLocation fieldLocation, Field field, MouseEvent mouseEvent) {
|
||||
if (fieldLocation == null || field == null) {
|
||||
@@ -468,8 +480,7 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
|
||||
else {
|
||||
++endFieldOffset;
|
||||
}
|
||||
fsel.addRange(
|
||||
new FieldLocation(startLoc.getIndex(), startLoc.getFieldNum(), 0, 0),
|
||||
fsel.addRange(new FieldLocation(startLoc.getIndex(), startLoc.getFieldNum(), 0, 0),
|
||||
new FieldLocation(endIndex, endFieldOffset, 0, 0));
|
||||
}
|
||||
return fsel;
|
||||
|
||||
+1
-1
@@ -752,7 +752,7 @@ public class ByteViewerPanel extends JPanel
|
||||
|
||||
// for the index/address column
|
||||
indexFactory = new IndexFieldFactory(fm);
|
||||
indexPanel = new FieldPanel(this);
|
||||
indexPanel = new FieldPanel(this, "Byte Viewer");
|
||||
|
||||
indexPanel.enableSelection(false);
|
||||
indexPanel.setCursorOn(false);
|
||||
|
||||
+5
-1
@@ -1297,7 +1297,11 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
|
||||
private class DecompilerFieldPanel extends FieldPanel {
|
||||
|
||||
public DecompilerFieldPanel(LayoutModel model) {
|
||||
super(model);
|
||||
super(model, "Decompiler");
|
||||
// In the decompiler each field represents a line, so make the field description
|
||||
// simply be the line number
|
||||
setFieldDescriptionProvider(
|
||||
(l, f) -> "line " + (l.getIndex().intValue() + 1) + ", " + f.getText());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-1
@@ -25,6 +25,7 @@ import docking.widgets.fieldpanel.*;
|
||||
import ghidra.app.plugin.core.functiongraph.FGColorProvider;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FGController;
|
||||
import ghidra.app.plugin.core.functiongraph.mvc.FunctionGraphOptions;
|
||||
import ghidra.app.util.viewer.field.ListingFieldDescriptionProvider;
|
||||
import ghidra.app.util.viewer.format.FormatManager;
|
||||
import ghidra.app.util.viewer.listingpanel.*;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
@@ -142,7 +143,8 @@ public class FGVertexListingPanel extends ListingPanel {
|
||||
private class FGVertexFieldPanel extends FieldPanel {
|
||||
|
||||
public FGVertexFieldPanel(LayoutModel model) {
|
||||
super(model);
|
||||
super(model, "Function Graph Listing Vertex");
|
||||
setFieldDescriptionProvider(new ListingFieldDescriptionProvider());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -43,7 +43,7 @@ public class ActionAdapter implements Action, PropertyChangeListener {
|
||||
* <p>Most clients should use {@link #ActionAdapter(DockingActionIf, ActionContextProvider)}
|
||||
* @param dockingAction the action to adapt
|
||||
*/
|
||||
ActionAdapter(DockingActionIf dockingAction) {
|
||||
public ActionAdapter(DockingActionIf dockingAction) {
|
||||
this(dockingAction, null);
|
||||
}
|
||||
|
||||
|
||||
+494
@@ -0,0 +1,494 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.text.BreakIterator;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.text.AttributeSet;
|
||||
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.RowColLocation;
|
||||
|
||||
/**
|
||||
* Implements Accessible interfaces for individual fields in the field panel
|
||||
*/
|
||||
public class AccessibleField extends AccessibleContext
|
||||
implements Accessible, AccessibleComponent, AccessibleText {
|
||||
|
||||
private Field field;
|
||||
private int indexInParent;
|
||||
private Rectangle boundsInParent;
|
||||
private Locale locale;
|
||||
private JComponent parent;
|
||||
private int caretPos = 0;
|
||||
private boolean isSelected = false;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param field the field this is providing accessible access to
|
||||
* @param parent the component containing the field (FieldPanel)
|
||||
* @param indexInParent the number of this field relative to the visible fields on the screen.
|
||||
* @param bounds the bounds of the field relative to the field panel.
|
||||
*/
|
||||
public AccessibleField(Field field, JComponent parent, int indexInParent, Rectangle bounds) {
|
||||
this.field = field;
|
||||
this.parent = parent;
|
||||
this.indexInParent = indexInParent;
|
||||
this.locale = parent.getLocale();
|
||||
this.boundsInParent = bounds;
|
||||
setAccessibleName("Field");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the position of the cursor relative to the text in this field. It is only meaningful
|
||||
* when the corresponding field is the field containing the field panel's actual cursor.
|
||||
* @param caretPos the offset into the text of the field of where the cursor is being displayed
|
||||
* by the field panel.
|
||||
*/
|
||||
public void setCaretPos(int caretPos) {
|
||||
if (caretPos >= 0 && caretPos < field.getText().length()) {
|
||||
this.caretPos = caretPos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets that this field is part of the overall selection.
|
||||
* @param selected true if the field is part of the selection; false otherwise
|
||||
*/
|
||||
public void setSelected(boolean selected) {
|
||||
this.isSelected = selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the field is currently part of a selection.
|
||||
* @return true if the field is currently part of a selection.
|
||||
*/
|
||||
public boolean isSelected() {
|
||||
return isSelected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text of the field
|
||||
* @return the text of the field
|
||||
*/
|
||||
public String getText() {
|
||||
return field.getText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a row,col position to an text offset in the field
|
||||
* @param row the row
|
||||
* @param col the col
|
||||
* @return an offset into the text that represents the row,col position
|
||||
*/
|
||||
public int getTextOffset(int row, int col) {
|
||||
return field.screenLocationToTextOffset(row, col);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field associated with this AccessibleField.
|
||||
* @return the field associated with this AccessibleField
|
||||
*/
|
||||
public Field getField() {
|
||||
return field;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Accessible methods
|
||||
//==================================================================================================
|
||||
|
||||
@Override
|
||||
public AccessibleContext getAccessibleContext() {
|
||||
return this;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// AccessibleContext methods
|
||||
//==================================================================================================
|
||||
|
||||
@Override
|
||||
public AccessibleText getAccessibleText() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleComponent getAccessibleComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleRole getAccessibleRole() {
|
||||
return AccessibleRole.TEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleStateSet getAccessibleStateSet() {
|
||||
AccessibleStateSet states = new AccessibleStateSet();
|
||||
states.add(AccessibleState.MULTI_LINE);
|
||||
states.add(AccessibleState.TRANSIENT);
|
||||
return states;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessibleIndexInParent() {
|
||||
return indexInParent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessibleChildrenCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Accessible getAccessibleChild(int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() throws IllegalComponentStateException {
|
||||
return locale;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// AccessibleText methods
|
||||
//==================================================================================================
|
||||
|
||||
@Override
|
||||
public int getIndexAtPoint(Point p) {
|
||||
// fields are weird, internally their 0 y position is the font baseline, so we
|
||||
// need to compensate for that to find the row. Also, fields internal x position
|
||||
// is relative to the field panel and the p being given here is relative to the field,
|
||||
// we need to add the fields startingX to the given point.
|
||||
int row = field.getRow(p.y - field.getHeightAbove());
|
||||
int col = field.getCol(row, p.x + field.getStartX());
|
||||
int result = field.screenLocationToTextOffset(row, col);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getCharacterBounds(int i) {
|
||||
if (i < 0 || i >= getCharCount()) {
|
||||
return new Rectangle(0, 0, 0, 0);
|
||||
}
|
||||
RowColLocation rowCol = field.textOffsetToScreenLocation(i);
|
||||
int row = rowCol.row();
|
||||
int col = rowCol.col();
|
||||
Rectangle charBounds = field.getCursorBounds(row, col);
|
||||
Rectangle nextCharBounds = field.getCursorBounds(row, col + 1);
|
||||
|
||||
charBounds.width = nextCharBounds.x - charBounds.x;
|
||||
// again the bounds give are relative to the layout and field panel and this method wants
|
||||
// a bounds relative to the field.
|
||||
charBounds.y += field.getHeightAbove();
|
||||
charBounds.x -= field.getStartX();
|
||||
return charBounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCharCount() {
|
||||
return field.getText().length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAtIndex(int part, int index) {
|
||||
String text = field.getText();
|
||||
if (index < 0 || index >= text.length()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (part) {
|
||||
case AccessibleText.CHARACTER:
|
||||
return text.substring(index, index + 1);
|
||||
case AccessibleText.WORD:
|
||||
BreakIterator words = BreakIterator.getWordInstance(locale);
|
||||
words.setText(text);
|
||||
int end = words.following(index);
|
||||
return text.substring(words.previous(), end);
|
||||
case AccessibleText.SENTENCE:
|
||||
BreakIterator sentences = BreakIterator.getSentenceInstance(locale);
|
||||
sentences.setText(text);
|
||||
end = sentences.following(index);
|
||||
return text.substring(sentences.previous(), end);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAfterIndex(int part, int index) {
|
||||
String text = field.getText();
|
||||
if (index < 0 || index >= text.length() - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (part) {
|
||||
case AccessibleText.CHARACTER:
|
||||
return text.substring(index + 1, index + 2);
|
||||
case AccessibleText.WORD:
|
||||
BreakIterator words = BreakIterator.getWordInstance(locale);
|
||||
words.setText(text);
|
||||
int start = words.following(index);
|
||||
if (start == BreakIterator.DONE || start >= text.length()) {
|
||||
return null;
|
||||
}
|
||||
int end = words.following(start);
|
||||
if (end == BreakIterator.DONE || end > text.length()) {
|
||||
return null;
|
||||
}
|
||||
return text.substring(start, end);
|
||||
case AccessibleText.SENTENCE:
|
||||
BreakIterator sentences = BreakIterator.getSentenceInstance(locale);
|
||||
sentences.setText(text);
|
||||
start = sentences.following(index);
|
||||
if (start == BreakIterator.DONE || start > text.length()) {
|
||||
return null;
|
||||
}
|
||||
end = sentences.following(start);
|
||||
if (end == BreakIterator.DONE || end > text.length()) {
|
||||
return null;
|
||||
}
|
||||
return text.substring(start, end);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBeforeIndex(int part, int index) {
|
||||
String text = field.getText();
|
||||
if (index < 1 || index > text.length()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (part) {
|
||||
case AccessibleText.CHARACTER:
|
||||
return text.substring(index - 1, index);
|
||||
case AccessibleText.WORD:
|
||||
BreakIterator words = BreakIterator.getWordInstance(locale);
|
||||
words.setText(text);
|
||||
|
||||
// move to the beginning of the current word so the algorithm
|
||||
// gives us the previous word and not the word we are on. Note: this is needed
|
||||
// because the preceding() method behaves differently if in the middle of a
|
||||
// word than if at the beginning of the word.
|
||||
if (!words.isBoundary(index)) {
|
||||
words.preceding(index);
|
||||
}
|
||||
int start = words.previous();
|
||||
int end = words.next();
|
||||
if (start == BreakIterator.DONE) {
|
||||
return null;
|
||||
}
|
||||
return text.substring(start, end);
|
||||
case AccessibleText.SENTENCE:
|
||||
BreakIterator sentences = BreakIterator.getSentenceInstance(locale);
|
||||
sentences.setText(text);
|
||||
if (!sentences.isBoundary(index)) {
|
||||
sentences.preceding(index);
|
||||
}
|
||||
start = sentences.previous();
|
||||
end = sentences.next();
|
||||
if (start == BreakIterator.DONE) {
|
||||
return null;
|
||||
}
|
||||
return text.substring(start, end);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCaretPosition() {
|
||||
return caretPos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeSet getCharacterAttribute(int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectionStart() {
|
||||
// field selection is all or nothing so this always returns 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectionEnd() {
|
||||
// field selection is all or nothing, so if selected this will return the end of the text
|
||||
// otherwise, return 0 because if selectionStart == selectionEnd means no selection
|
||||
if (isSelected) {
|
||||
return field.getText().length();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectedText() {
|
||||
// selection is all or nothing
|
||||
if (isSelected) {
|
||||
return field.getText();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// AccessibleComponent methods
|
||||
//==================================================================================================
|
||||
|
||||
@Override
|
||||
public Color getBackground() {
|
||||
return parent.getBackground();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBackground(Color c) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getForeground() {
|
||||
return parent.getForeground();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setForeground(Color c) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursor() {
|
||||
return parent.getCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCursor(Cursor cursor) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public Font getFont() {
|
||||
return parent.getFont();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setFont(Font f) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public FontMetrics getFontMetrics(Font f) {
|
||||
return parent.getFontMetrics(f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean b) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVisible() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisible(boolean b) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isShowing() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(Point p) {
|
||||
return (p.x >= 0) && (p.x < field.getWidth()) && (p.y >= 0) && (p.y < field.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getLocationOnScreen() {
|
||||
Point parentLoc = parent.getLocationOnScreen();
|
||||
return new Point(parentLoc.x + boundsInParent.x, parentLoc.y + boundsInParent.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point getLocation() {
|
||||
return boundsInParent.getLocation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLocation(Point p) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getBounds() {
|
||||
return new Rectangle(boundsInParent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(Rectangle r) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getSize() {
|
||||
return new Dimension(field.getWidth(), field.getHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSize(Dimension d) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public Accessible getAccessibleAt(Point p) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocusTraversable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void requestFocus() {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFocusListener(FocusListener l) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeFocusListener(FocusListener l) {
|
||||
// unsupported
|
||||
}
|
||||
|
||||
}
|
||||
+453
@@ -0,0 +1,453 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import static javax.accessibility.AccessibleContext.*;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
|
||||
/**
|
||||
* Contains all the code for implementing the AccessibleFieldPanel which is an inner class in
|
||||
* the FieldPanel class. The AccessibleFieldPanel has to be declared as an inner class because
|
||||
* it needs to extends AccessibleJComponent which is a non-static inner class of JComponent.
|
||||
* However, we did not want to put all the logic in there as FieldPanel is already an
|
||||
* extremely large and complex class. Also, by delegating the the logic, testing is much
|
||||
* easier.
|
||||
* <P>
|
||||
* The model for accessibility for the FieldPanel is a bit complex because
|
||||
* the field panel displays text, but in a 2 dimensional array of fields, where each field
|
||||
* has potentially 2 dimensional text. So for the purpose of accessibility, the FieldPanel
|
||||
* acts as both a text field and a text component.
|
||||
* <P>
|
||||
* To support screen readers reacting to cursor movements in the FieldPanel, the FieldPanel
|
||||
* acts like a text field, but it acts like it only has the text of one inner Field at a time
|
||||
* (The one where the cursor is). The other approach that was considered was to treat the field
|
||||
* panel as a single text document. This would be difficult to implement because of the way fields
|
||||
* are multi-lined. Also, the user of the screen reader would lose all concepts that there are
|
||||
* fields. By maintaining the fields as a concept to the screen reader, it can provide more
|
||||
* meaningful descriptions as the cursor is moved between fields.
|
||||
* <P>
|
||||
* The Field panel also acts as an {@link AccessibleComponent} with virtual children for each of its
|
||||
* visible fields. This is what allows screen readers to read the context of whatever the mouse
|
||||
* is hovering over keeping the data separated by the field boundaries.
|
||||
*/
|
||||
public class AccessibleFieldPanelDelegate {
|
||||
private List<AccessibleLayout> accessibleLayouts;
|
||||
private int totalFieldCount;
|
||||
private AccessibleField[] fieldsCache;
|
||||
private JComponent panel;
|
||||
|
||||
// caret position tracking
|
||||
private FieldLocation cursorLoc;
|
||||
private int caretPos;
|
||||
private AccessibleField cursorField;
|
||||
|
||||
private FieldDescriptionProvider fieldDescriber = (l, f) -> "";
|
||||
private AccessibleContext context;
|
||||
private String description;
|
||||
private FieldSelection currentSelection;
|
||||
|
||||
public AccessibleFieldPanelDelegate(List<AnchoredLayout> layouts, AccessibleContext context,
|
||||
JComponent panel) {
|
||||
this.context = context;
|
||||
this.panel = panel;
|
||||
setLayouts(layouts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whenever the set of visible layouts changes, the field panel rebuilds its info for the
|
||||
* new visible fields and notifies the accessibility system that its children changed.
|
||||
* @param layouts the new set of visible layouts.
|
||||
*/
|
||||
public void setLayouts(List<AnchoredLayout> layouts) {
|
||||
totalFieldCount = 0;
|
||||
accessibleLayouts = new ArrayList<>(layouts.size());
|
||||
for (AnchoredLayout layout : layouts) {
|
||||
AccessibleLayout accessibleLayout = new AccessibleLayout(layout, totalFieldCount);
|
||||
accessibleLayouts.add(accessibleLayout);
|
||||
totalFieldCount += layout.getNumFields();
|
||||
}
|
||||
fieldsCache = new AccessibleField[totalFieldCount];
|
||||
context.firePropertyChange(ACCESSIBLE_INVALIDATE_CHILDREN, null, panel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells this delegate that the cursor moved. It updates its internal state and fires
|
||||
* events to the accessibility system.
|
||||
* @param newCursorLoc the new FieldLoation of the cursor
|
||||
* @param trigger the event trigger
|
||||
*/
|
||||
public void setCaret(FieldLocation newCursorLoc, EventTrigger trigger) {
|
||||
if (cursorField == null || !isSameField(cursorLoc, newCursorLoc)) {
|
||||
AccessibleTextSequence oldSequence = getAccessibleTextSequence(cursorField);
|
||||
cursorLoc = newCursorLoc;
|
||||
cursorField = getAccessibleField(newCursorLoc);
|
||||
AccessibleTextSequence newSequence = getAccessibleTextSequence(cursorField);
|
||||
String oldDescription = description;
|
||||
description = generateDescription();
|
||||
if (trigger == EventTrigger.GUI_ACTION) {
|
||||
context.firePropertyChange(ACCESSIBLE_TEXT_PROPERTY, oldSequence, newSequence);
|
||||
context.firePropertyChange(ACCESSIBLE_DESCRIPTION_PROPERTY, oldDescription,
|
||||
description);
|
||||
}
|
||||
if (currentSelection != null && currentSelection.contains(cursorLoc)) {
|
||||
updateCurrentFieldSelectedState(trigger);
|
||||
}
|
||||
caretPos = -1;
|
||||
}
|
||||
if (cursorField == null) {
|
||||
caretPos = 0;
|
||||
return;
|
||||
}
|
||||
int newCaretPos = cursorField.getTextOffset(newCursorLoc.getRow(), newCursorLoc.getCol());
|
||||
cursorField.setCaretPos(newCaretPos);
|
||||
if (newCaretPos != caretPos && trigger == EventTrigger.GUI_ACTION) {
|
||||
context.firePropertyChange(ACCESSIBLE_CARET_PROPERTY, caretPos, newCaretPos);
|
||||
}
|
||||
caretPos = newCaretPos;
|
||||
cursorLoc = newCursorLoc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells this delegate that the selection has changed. If the current field is in the selection,
|
||||
* it sets the current AccessibleField to be selected. (A field is either entirely selected
|
||||
* or not)
|
||||
* @param currentSelection the new current field panel selection
|
||||
* @param trigger the event trigger
|
||||
*/
|
||||
public void setSelection(FieldSelection currentSelection, EventTrigger trigger) {
|
||||
this.currentSelection = currentSelection;
|
||||
updateCurrentFieldSelectedState(trigger);
|
||||
}
|
||||
|
||||
private void updateCurrentFieldSelectedState(EventTrigger trigger) {
|
||||
if (cursorField == null) {
|
||||
return;
|
||||
}
|
||||
boolean oldIsSelected = cursorField.isSelected();
|
||||
boolean newIsSelected = currentSelection != null && currentSelection.contains(cursorLoc);
|
||||
cursorField.setSelected(newIsSelected);
|
||||
if (oldIsSelected != newIsSelected && trigger == EventTrigger.GUI_ACTION) {
|
||||
context.firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
private String generateDescription() {
|
||||
Field field = cursorField != null ? cursorField.getField() : null;
|
||||
return fieldDescriber.getDescription(cursorLoc, field);
|
||||
}
|
||||
|
||||
private AccessibleTextSequence getAccessibleTextSequence(AccessibleField field) {
|
||||
if (field == null) {
|
||||
return new AccessibleTextSequence(0, 0, "");
|
||||
}
|
||||
String text = field.getField().getText();
|
||||
return new AccessibleTextSequence(0, text.length(), text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the caret position relative the current active field.
|
||||
* @return the caret position relative the current active field
|
||||
*/
|
||||
public int getCaretPosition() {
|
||||
return caretPos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of characters in the current active field.
|
||||
* @return the number of characters in the current active field.
|
||||
*/
|
||||
public int getCharCount() {
|
||||
return cursorField != null ? cursorField.getCharCount() : 0;
|
||||
}
|
||||
|
||||
private boolean isSameField(FieldLocation loc1, FieldLocation loc2) {
|
||||
if (loc1.getIndex() != loc2.getIndex()) {
|
||||
return false;
|
||||
}
|
||||
return loc1.getFieldNum() == loc2.getFieldNum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the n'th AccessibleField that is visible on the screen.
|
||||
* @param fieldNum the number of the field to get
|
||||
* @return the n'th AccessibleField that is visible on the screen
|
||||
*/
|
||||
public AccessibleField getAccessibleField(int fieldNum) {
|
||||
if (fieldNum < 0 || fieldNum >= fieldsCache.length) {
|
||||
return null;
|
||||
}
|
||||
if (fieldsCache[fieldNum] == null) {
|
||||
fieldsCache[fieldNum] = createAccessibleField(fieldNum);
|
||||
}
|
||||
return fieldsCache[fieldNum];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AccessibleField associated with the given field location.
|
||||
* @param loc the FieldLocation to get the visible field for
|
||||
* @return the AccessibleField associated with the given field location
|
||||
*/
|
||||
public AccessibleField getAccessibleField(FieldLocation loc) {
|
||||
int result = Collections.binarySearch(accessibleLayouts, loc.getIndex(),
|
||||
Comparator.comparing(
|
||||
o -> o instanceof AccessibleLayout lh ? lh.getIndex() : (BigInteger) o,
|
||||
BigInteger::compareTo));
|
||||
if (result < 0) {
|
||||
return null;
|
||||
}
|
||||
AccessibleLayout layout = accessibleLayouts.get(result);
|
||||
return getAccessibleField(layout.getStartingFieldNum() + loc.getFieldNum());
|
||||
}
|
||||
|
||||
private AccessibleField createAccessibleField(int fieldNum) {
|
||||
int result = Collections.binarySearch(accessibleLayouts, fieldNum, Comparator.comparingInt(
|
||||
o -> o instanceof AccessibleLayout lh ? lh.getStartingFieldNum() : (Integer) o));
|
||||
if (result < 0) {
|
||||
result = -result - 2;
|
||||
}
|
||||
AccessibleLayout layout = accessibleLayouts.get(result);
|
||||
return layout.createAccessibleField(fieldNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the bounds relative to the field panel for the character at the given index
|
||||
* @param index the index of the character in the active field whose bounds is to be returned.
|
||||
* @return the bounds relative to the field panel for the character at the given index
|
||||
*/
|
||||
public Rectangle getCharacterBounds(int index) {
|
||||
if (cursorField == null) {
|
||||
return null;
|
||||
}
|
||||
Point loc = cursorField.getLocation();
|
||||
Rectangle bounds = cursorField.getCharacterBounds(index);
|
||||
bounds.x += loc.x;
|
||||
bounds.y += loc.y;
|
||||
return bounds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the character index at the given point relative to the FieldPanel. Note this
|
||||
* only returns chars in the active field.
|
||||
* @param p the point to get the character for
|
||||
* @return the character index at the given point relative to the FieldPanel.
|
||||
*/
|
||||
public int getIndexAtPoint(Point p) {
|
||||
if (cursorField == null) {
|
||||
return 0;
|
||||
}
|
||||
Rectangle bounds = cursorField.getBounds();
|
||||
if (!bounds.contains(p)) {
|
||||
return -1;
|
||||
}
|
||||
Point localPoint = new Point(p.x - bounds.x, p.y - bounds.y);
|
||||
return cursorField.getIndexAtPoint(localPoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the char, word, or sentence at the given char index.
|
||||
* @param part specifies char, word or sentence (See {@link AccessibleText})
|
||||
* @param index the character index to get data for
|
||||
* @return the char, word, or sentences at the given char index
|
||||
*/
|
||||
public String getAtIndex(int part, int index) {
|
||||
if (cursorField == null) {
|
||||
return "";
|
||||
}
|
||||
return cursorField.getAtIndex(part, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the char, word, or sentence after the given char index.
|
||||
* @param part specifies char, word or sentence (See {@link AccessibleText})
|
||||
* @param index the character index to get data for
|
||||
* @return the char, word, or sentence after the given char index
|
||||
*/
|
||||
public String getAfterIndex(int part, int index) {
|
||||
if (cursorField == null) {
|
||||
return "";
|
||||
}
|
||||
return cursorField.getAfterIndex(part, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the char, word, or sentence at the given char index.
|
||||
* @param part specifies char, word or sentence (See {@link AccessibleText})
|
||||
* @param index the character index to get data for
|
||||
* @return the char, word, or sentence at the given char index
|
||||
*/
|
||||
public String getBeforeIndex(int part, int index) {
|
||||
if (cursorField == null) {
|
||||
return "";
|
||||
}
|
||||
return cursorField.getBeforeIndex(part, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of visible field showing on the screen in the field panel.
|
||||
* @return the number of visible field showing on the screen in the field panel
|
||||
*/
|
||||
public int getFieldCount() {
|
||||
return totalFieldCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link AccessibleField} that is at the given point relative to the FieldPanel.
|
||||
* @param p the point to get an Accessble child at
|
||||
* @return the {@link AccessibleField} that is at the given point relative to the FieldPanel
|
||||
*/
|
||||
public Accessible getAccessibleAt(Point p) {
|
||||
int result = Collections.binarySearch(accessibleLayouts, p.y, Comparator
|
||||
.comparingInt(o -> o instanceof AccessibleLayout lh ? lh.getYpos() : (Integer) o));
|
||||
|
||||
if (result < 0) {
|
||||
result = -result - 2;
|
||||
}
|
||||
if (result < 0 || result >= accessibleLayouts.size()) {
|
||||
return null;
|
||||
}
|
||||
int fieldNum = accessibleLayouts.get(result).getFieldNum(p);
|
||||
return getAccessibleField(fieldNum);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of the current field
|
||||
* @return a description of the current field
|
||||
*/
|
||||
public String getFieldDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the {@link FieldDescriptionProvider} that can generate descriptions of the current
|
||||
* field.
|
||||
* @param provider the description provider
|
||||
*/
|
||||
public void setFieldDescriptionProvider(FieldDescriptionProvider provider) {
|
||||
fieldDescriber = provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selection character start index. This currently always returns 0 as
|
||||
* selections are all or nothing.
|
||||
* @return the selection character start index.
|
||||
*/
|
||||
public int getSelectionStart() {
|
||||
if (cursorField == null) {
|
||||
return 0;
|
||||
}
|
||||
return cursorField.getSelectionStart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selection character end index. This is either 0, indicating there is no selection
|
||||
* or the index at the end of the text meaning the entire field is selected.
|
||||
* @return the selection character start index.
|
||||
*/
|
||||
public int getSelectionEnd() {
|
||||
if (cursorField == null) {
|
||||
return 0;
|
||||
}
|
||||
return cursorField.getSelectionEnd();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns either null if the field is not selected or the full field text if it is selected.
|
||||
* @return either null if the field is not selected or the full field text if it is selected
|
||||
*/
|
||||
public String getSelectedText() {
|
||||
if (cursorField == null) {
|
||||
return null;
|
||||
}
|
||||
return cursorField.getSelectedText();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps each AnchoredLayout to assist organizing the list of layouts into a single list
|
||||
* of fields.
|
||||
*/
|
||||
private class AccessibleLayout {
|
||||
|
||||
private AnchoredLayout layout;
|
||||
private int startingFieldNum;
|
||||
|
||||
public AccessibleLayout(AnchoredLayout layout, int startingFieldNum) {
|
||||
this.layout = layout;
|
||||
this.startingFieldNum = startingFieldNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the AccessibleField as needed.
|
||||
* @param fieldNum the number of the field to create an AccessibleField for. This number
|
||||
* is relative to all the fields in the field panel and not to this layout.
|
||||
* @return an AccessibleField for the given fieldNum
|
||||
*/
|
||||
public AccessibleField createAccessibleField(int fieldNum) {
|
||||
int fieldNumInLayout = fieldNum - startingFieldNum;
|
||||
Field field = layout.getField(fieldNumInLayout);
|
||||
Rectangle fieldBounds = layout.getFieldBounds(fieldNumInLayout);
|
||||
return new AccessibleField(field, panel, fieldNum, fieldBounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the overall field number of the first field in this layout. For example,
|
||||
* the first layout would have a starting field number of 0 and if it has 5 fields, the
|
||||
* next layout would have a starting field number of 5 and so on.
|
||||
* @return the overall field number of the first field in this layout.
|
||||
*/
|
||||
public int getStartingFieldNum() {
|
||||
return startingFieldNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the overall field number of the field containing the given point.
|
||||
* @param p the point to find the field for
|
||||
* @return the overall field number of the field containing the given point.
|
||||
*/
|
||||
public int getFieldNum(Point p) {
|
||||
return layout.getFieldIndex(p.x, p.y) + startingFieldNum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the y position of this layout relative to the field panel.
|
||||
* @return the y position of this layout relative to the field panel.
|
||||
*/
|
||||
public int getYpos() {
|
||||
return layout.getYPos();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the layout as defined by the client code. The only requirements for
|
||||
* indexes is that the index for a layout is always bigger then the index of the previous
|
||||
* layout.
|
||||
* @return the index of the layout as defined by the client code.
|
||||
*/
|
||||
public BigInteger getIndex() {
|
||||
return layout.getIndex();
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import docking.widgets.fieldpanel.field.Field;
|
||||
import docking.widgets.fieldpanel.support.FieldLocation;
|
||||
|
||||
/**
|
||||
* Provides descriptions for fields in a field panel
|
||||
*/
|
||||
public interface FieldDescriptionProvider {
|
||||
|
||||
/**
|
||||
* Gets a description for the given location and field.
|
||||
* @param loc the FieldLocation to get a description for
|
||||
* @param field the Field to get a description for
|
||||
* @return a String describing the given field location
|
||||
*/
|
||||
public String getDescription(FieldLocation loc, Field field);
|
||||
}
|
||||
+288
-15
@@ -23,10 +23,12 @@ import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import javax.swing.*;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import javax.swing.text.AttributeSet;
|
||||
|
||||
import docking.DockingUtils;
|
||||
import docking.util.GraphicsUtils;
|
||||
@@ -43,7 +45,7 @@ import generic.theme.GThemeDefaults.Colors.Messages;
|
||||
import ghidra.util.*;
|
||||
|
||||
public class FieldPanel extends JPanel
|
||||
implements IndexedScrollable, LayoutModelListener, ChangeListener {
|
||||
implements IndexedScrollable, LayoutModelListener, ChangeListener, Accessible {
|
||||
public static final int MOUSEWHEEL_LINES_TO_SCROLL = 3;
|
||||
|
||||
private LayoutModel model;
|
||||
@@ -81,13 +83,30 @@ public class FieldPanel extends JPanel
|
||||
private int currentViewXpos;
|
||||
|
||||
private JViewport viewport;
|
||||
private String name;
|
||||
|
||||
private FieldDescriptionProvider fieldDescriptionProvider;
|
||||
private AccessibleFieldPanel accessibleFieldPanel;
|
||||
|
||||
public FieldPanel(LayoutModel model) {
|
||||
this(model, "No Name");
|
||||
}
|
||||
|
||||
public FieldPanel(LayoutModel model, String name) {
|
||||
this.model = model;
|
||||
this.name = name;
|
||||
model.addLayoutModelListener(this);
|
||||
layoutHandler = new AnchoredLayoutHandler(model, getHeight());
|
||||
layouts = layoutHandler.positionLayoutsAroundAnchor(BigInteger.ZERO, 0);
|
||||
|
||||
// initialize the focus traversal keys to control Tab to free up the tab key for internal
|
||||
// field panel use. This is the same behavior that text components use.
|
||||
KeyStroke ks = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, InputEvent.CTRL_DOWN_MASK);
|
||||
setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, Set.of(ks));
|
||||
ks = KeyStroke.getKeyStroke(KeyEvent.VK_TAB,
|
||||
InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK);
|
||||
setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, Set.of(ks));
|
||||
|
||||
addKeyListener(new FieldPanelKeyAdapter());
|
||||
addMouseListener(new FieldPanelMouseAdapter());
|
||||
addMouseMotionListener(new FieldPanelMouseMotionAdapter());
|
||||
@@ -130,6 +149,13 @@ public class FieldPanel extends JPanel
|
||||
repaint();
|
||||
}
|
||||
|
||||
public void setFieldDescriptionProvider(FieldDescriptionProvider provider) {
|
||||
fieldDescriptionProvider = provider;
|
||||
if (accessibleFieldPanel != null) {
|
||||
accessibleFieldPanel.setFieldDescriptionProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure the location is completely visible on the screen. If it already is visible, this
|
||||
* routine will do nothing. If the location is above the screen (at an index less than the first
|
||||
@@ -272,6 +298,14 @@ public class FieldPanel extends JPanel
|
||||
cursorHandler.doCursorRight(EventTrigger.API_CALL);
|
||||
}
|
||||
|
||||
public void tabRight() {
|
||||
cursorHandler.doTabRight(API_CALL);
|
||||
}
|
||||
|
||||
public void tabLeft() {
|
||||
cursorHandler.doTabLeft(API_CALL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the cursor to the beginning of the line.
|
||||
*/
|
||||
@@ -302,7 +336,6 @@ public class FieldPanel extends JPanel
|
||||
* Returns true if the given field location is rendered on the screen; false if scrolled
|
||||
* offscreen
|
||||
*
|
||||
* @param location the location to check
|
||||
* @return true if the location is on the screen
|
||||
*/
|
||||
public boolean isLocationVisible(FieldLocation location) {
|
||||
@@ -1088,6 +1121,9 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
private void notifyScrollListenerViewChangedAndRepaint() {
|
||||
if (accessibleFieldPanel != null) {
|
||||
accessibleFieldPanel.updateLayouts();
|
||||
}
|
||||
BigInteger startIndex = BigInteger.ZERO;
|
||||
BigInteger endIndex = startIndex;
|
||||
int startY = 0;
|
||||
@@ -1270,7 +1306,6 @@ public class FieldPanel extends JPanel
|
||||
/**
|
||||
* Finds the layout containing the given y position.
|
||||
*
|
||||
* @param y the y position.
|
||||
* @return the layout.
|
||||
*/
|
||||
AnchoredLayout findLayoutAt(int y) {
|
||||
@@ -1314,6 +1349,10 @@ public class FieldPanel extends JPanel
|
||||
for (FieldSelectionListener l : selectionListeners) {
|
||||
l.selectionChanged(currentSelection, trigger);
|
||||
}
|
||||
if (accessibleFieldPanel != null) {
|
||||
accessibleFieldPanel.selectionChanged(currentSelection, trigger);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1337,9 +1376,148 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleContext getAccessibleContext() {
|
||||
if (accessibleFieldPanel == null) {
|
||||
accessibleFieldPanel = new AccessibleFieldPanel();
|
||||
if (fieldDescriptionProvider != null) {
|
||||
accessibleFieldPanel.setFieldDescriptionProvider(fieldDescriptionProvider);
|
||||
}
|
||||
}
|
||||
return accessibleFieldPanel;
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
// Inner Classes
|
||||
//==================================================================================================
|
||||
// We are forced to declare this as an inner class because AccessibleJComponent is a
|
||||
// non-static inner class. So this is just a stub and defers all its logic to
|
||||
// the AccessibleFieldPanelDelegate.
|
||||
class AccessibleFieldPanel extends AccessibleJComponent implements AccessibleText {
|
||||
private AccessibleFieldPanelDelegate delegate;
|
||||
|
||||
AccessibleFieldPanel() {
|
||||
delegate = new AccessibleFieldPanelDelegate(layouts, this, FieldPanel.this);
|
||||
}
|
||||
|
||||
public void cursorChanged(FieldLocation newCursorLoc, EventTrigger trigger) {
|
||||
delegate.setCaret(newCursorLoc, trigger);
|
||||
}
|
||||
|
||||
public void selectionChanged(FieldSelection currentSelection, EventTrigger trigger) {
|
||||
delegate.setSelection(currentSelection, trigger);
|
||||
}
|
||||
|
||||
public void setFieldDescriptionProvider(FieldDescriptionProvider provider) {
|
||||
delegate.setFieldDescriptionProvider(provider);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessibleDescription() {
|
||||
return delegate.getFieldDescription();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAccessibleName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleText getAccessibleText() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleStateSet getAccessibleStateSet() {
|
||||
AccessibleStateSet accessibleStateSet = super.getAccessibleStateSet();
|
||||
accessibleStateSet.add(AccessibleState.EDITABLE);
|
||||
accessibleStateSet.add(AccessibleState.MULTI_LINE);
|
||||
accessibleStateSet.add(AccessibleState.MANAGES_DESCENDANTS);
|
||||
return accessibleStateSet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessibleChildrenCount() {
|
||||
return delegate.getFieldCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Accessible getAccessibleChild(int i) {
|
||||
AccessibleField field = delegate.getAccessibleField(i);
|
||||
return field;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Accessible getAccessibleAt(Point p) {
|
||||
return delegate.getAccessibleAt(p);
|
||||
}
|
||||
|
||||
public void updateLayouts() {
|
||||
delegate.setLayouts(layouts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleRole getAccessibleRole() {
|
||||
return AccessibleRole.TEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIndexAtPoint(Point p) {
|
||||
return delegate.getIndexAtPoint(p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getCharacterBounds(int i) {
|
||||
return delegate.getCharacterBounds(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCharCount() {
|
||||
return delegate.getCharCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCaretPosition() {
|
||||
return delegate.getCaretPosition();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAtIndex(int part, int index) {
|
||||
return delegate.getAtIndex(part, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAfterIndex(int part, int index) {
|
||||
return delegate.getAfterIndex(part, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBeforeIndex(int part, int index) {
|
||||
return delegate.getBeforeIndex(part, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttributeSet getCharacterAttribute(int i) {
|
||||
// currently unsupported
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectionStart() {
|
||||
return delegate.getSelectionStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSelectionEnd() {
|
||||
return delegate.getSelectionEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectedText() {
|
||||
return delegate.getSelectedText();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class FieldPanelMouseAdapter extends MouseAdapter {
|
||||
|
||||
@@ -1448,39 +1626,71 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
}
|
||||
|
||||
private class TabRightAction implements KeyAction {
|
||||
@Override
|
||||
public void handleKeyEvent(KeyEvent event) {
|
||||
keyHandler.vkTab(event);
|
||||
}
|
||||
}
|
||||
|
||||
private class TabLeftAction implements KeyAction {
|
||||
@Override
|
||||
public void handleKeyEvent(KeyEvent event) {
|
||||
keyHandler.vkShiftTab(event);
|
||||
}
|
||||
}
|
||||
|
||||
private class FieldPanelKeyAdapter extends KeyAdapter {
|
||||
private Map<KeyStroke, KeyAction> actionMap;
|
||||
|
||||
FieldPanelKeyAdapter() {
|
||||
actionMap = new HashMap<>();
|
||||
|
||||
int shift = InputEvent.SHIFT_DOWN_MASK;
|
||||
int control = DockingUtils.CONTROL_KEY_MODIFIER_MASK;
|
||||
//
|
||||
// Arrow Keys
|
||||
// Arrow Keys (with shift pressed or not
|
||||
//
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), new UpKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), new DownKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), new LeftKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), new RightKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, shift), new UpKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, shift), new DownKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, shift), new LeftKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, shift), new RightKeyAction());
|
||||
|
||||
//
|
||||
// Home/End and Control/Command Home/End
|
||||
// Tab Keys
|
||||
//
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0), new TabRightAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, shift), new TabLeftAction());
|
||||
|
||||
//
|
||||
// Home/End and Control/Command Home/End (with shift pressed or not)
|
||||
//
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0), new HomeKeyAction());
|
||||
actionMap.put(
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_HOME, DockingUtils.CONTROL_KEY_MODIFIER_MASK),
|
||||
new HomeKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, control), new HomeKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), new EndKeyAction());
|
||||
actionMap.put(
|
||||
KeyStroke.getKeyStroke(KeyEvent.VK_END, DockingUtils.CONTROL_KEY_MODIFIER_MASK),
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, control), new EndKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, shift), new HomeKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, shift | control),
|
||||
new HomeKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, shift), new EndKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, shift | control),
|
||||
new EndKeyAction());
|
||||
|
||||
//
|
||||
// Page Up/Down
|
||||
// Page Up/Down (with the shift pressed or not)
|
||||
//
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0), new PageUpKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
|
||||
new PageDownKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), new EnterKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, shift),
|
||||
new PageUpKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, shift),
|
||||
new PageDownKeyAction());
|
||||
actionMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, shift), new EnterKeyAction());
|
||||
|
||||
}
|
||||
|
||||
@@ -1491,10 +1701,9 @@ public class FieldPanel extends JPanel
|
||||
return; // let ALT-?? be used for other action key bindings
|
||||
}
|
||||
|
||||
// Shift is handled special, so mask it off in the event before getting the action.
|
||||
// If the shift is being held, the selection is extended while moving the cursor.
|
||||
int keyCode = e.getKeyCode();
|
||||
int modifiers = e.getModifiersEx() & ~InputEvent.SHIFT_DOWN_MASK;
|
||||
int modifiers = e.getModifiersEx();
|
||||
|
||||
KeyEvent maskedEvent = new KeyEvent(e.getComponent(), e.getID(), e.getWhen(), modifiers,
|
||||
keyCode, e.getKeyChar(), e.getKeyLocation());
|
||||
|
||||
@@ -1749,6 +1958,16 @@ public class FieldPanel extends JPanel
|
||||
selectionHandler.updateSelectionSequence(cursorPosition);
|
||||
}
|
||||
|
||||
void vkTab(KeyEvent e) {
|
||||
selectionHandler.endSelectionSequence();
|
||||
cursorHandler.doTabRight(EventTrigger.GUI_ACTION);
|
||||
}
|
||||
|
||||
void vkShiftTab(KeyEvent e) {
|
||||
selectionHandler.endSelectionSequence();
|
||||
cursorHandler.doTabLeft(EventTrigger.GUI_ACTION);
|
||||
}
|
||||
|
||||
void vkEnd(KeyEvent e) {
|
||||
if (DockingUtils.isControlModifier(e)) {
|
||||
doEndOfFile(EventTrigger.GUI_ACTION);
|
||||
@@ -1996,6 +2215,57 @@ public class FieldPanel extends JPanel
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean doTabRight(EventTrigger trigger) {
|
||||
if (!cursorOn) {
|
||||
// if no cursor, nothing to tab from or to
|
||||
return false;
|
||||
}
|
||||
|
||||
scrollToCursor();
|
||||
Layout layout = findLayoutOnScreen(cursorPosition.getIndex());
|
||||
if (layout == null) {
|
||||
return false;
|
||||
}
|
||||
int numFields = layout.getNumFields();
|
||||
if (cursorPosition.fieldNum < numFields - 1) {
|
||||
doSetCursorPosition(cursorPosition.getIndex(), cursorPosition.fieldNum + 1, 0, 0,
|
||||
trigger);
|
||||
}
|
||||
else {
|
||||
BigInteger indexAfter = getIndexAfter(cursorPosition.getIndex());
|
||||
if (indexAfter == null) {
|
||||
return false;
|
||||
}
|
||||
doSetCursorPosition(indexAfter, 0, 0, 0, trigger);
|
||||
}
|
||||
scrollToCursor();
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean doTabLeft(EventTrigger trigger) {
|
||||
if (!cursorOn) {
|
||||
// if no cursor, nothing to tab from or to
|
||||
return false;
|
||||
}
|
||||
if (cursorPosition.fieldNum > 0) {
|
||||
doSetCursorPosition(cursorPosition.getIndex(), cursorPosition.fieldNum - 1, 0, 0,
|
||||
trigger);
|
||||
}
|
||||
else {
|
||||
BigInteger indexBefore = getIndexBefore(cursorPosition.getIndex());
|
||||
if (indexBefore == null) {
|
||||
return false;
|
||||
}
|
||||
Layout layout = model.getLayout(indexBefore);
|
||||
int fieldNum = layout.getNumFields() - 1;
|
||||
doSetCursorPosition(indexBefore, fieldNum, 0, 0, trigger);
|
||||
}
|
||||
scrollToCursor();
|
||||
repaint();
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean doCursorUp(EventTrigger trigger) {
|
||||
if (!cursorOn) {
|
||||
scrollLineUp();
|
||||
@@ -2157,6 +2427,9 @@ public class FieldPanel extends JPanel
|
||||
}
|
||||
|
||||
FieldLocation currentLocation = new FieldLocation(cursorPosition);
|
||||
if (accessibleFieldPanel != null) {
|
||||
accessibleFieldPanel.cursorChanged(currentLocation, trigger);
|
||||
}
|
||||
for (FieldLocationListener l : cursorListeners) {
|
||||
l.fieldLocationChanged(currentLocation, currentField, trigger);
|
||||
}
|
||||
|
||||
@@ -190,4 +190,12 @@ public interface Layout {
|
||||
* @return the smallest possible width of this layout that can display its full contents
|
||||
*/
|
||||
int getCompressableWidth();
|
||||
|
||||
/**
|
||||
* Returns the index of the field at the given coordinates (relative to the layout)
|
||||
* @param x the x coordinate
|
||||
* @param y the y coordinate
|
||||
* @return the index of the field at the given coordinates (relative to the layout)
|
||||
*/
|
||||
int getFieldIndex(int x, int y);
|
||||
}
|
||||
|
||||
+5
-1
@@ -1,6 +1,5 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
* REVIEWED: YES
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -204,4 +203,9 @@ public class AnchoredLayout implements Layout {
|
||||
cursorLoc.setIndex(index);
|
||||
return layout.setCursor(cursorLoc, x, y - yPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldIndex(int x, int y) {
|
||||
return layout.getFieldIndex(x, y - yPos);
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -149,6 +149,18 @@ public class MultiRowLayout implements Layout {
|
||||
return layouts[0].setCursor(cursorLoc, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldIndex(int x, int y) {
|
||||
int offset = 0;
|
||||
for (int i = 0; i < layouts.length; i++) {
|
||||
if (layouts[i].contains(y - offset)) {
|
||||
return layouts[i].getFieldIndex(x, y - offset) + offsets[i];
|
||||
}
|
||||
offset += layouts[i].getHeight();
|
||||
}
|
||||
return layouts[0].getFieldIndex(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rectangle getCursorRect(int fieldNum, int row, int col) {
|
||||
int offset = 0;
|
||||
|
||||
+9
-5
@@ -201,9 +201,7 @@ public class RowLayout implements Layout {
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
Field field = fields[index];
|
||||
|
||||
// y passed-in is 0-based; update y to be relative to our starting position, which is
|
||||
// the tallest field in this group of fields, using that field's height above its font
|
||||
// baseline.
|
||||
@@ -381,9 +379,9 @@ public class RowLayout implements Layout {
|
||||
* Finds the most appropriate field to place the cursor for the given horizontal
|
||||
* position. If the position is between fields, first try to the left and if that
|
||||
* doesn't work, try to the right.
|
||||
* @param x the x value
|
||||
* @param y the y value
|
||||
* @return the index
|
||||
* @param x the x coordinate relative to the field panel
|
||||
* @param y the y coordinate relative to the field panel
|
||||
* @return the index
|
||||
*/
|
||||
int findAppropriateFieldIndex(int x, int y) {
|
||||
y -= heightAbove;
|
||||
@@ -441,4 +439,10 @@ public class RowLayout implements Layout {
|
||||
public int getEndRowFieldNum(int field2) {
|
||||
return getNumFields();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFieldIndex(int x, int y) {
|
||||
int index = this.findAppropriateFieldIndex(x, y);
|
||||
return index >= 0 ? index : 0;
|
||||
}
|
||||
}
|
||||
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.widgets.EventTrigger;
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import docking.widgets.fieldpanel.support.*;
|
||||
|
||||
public class AccessibleFieldPanelDelegateTest {
|
||||
private static final int FIELD_WIDTH = 100;
|
||||
private static final int FIELD_HEIGHT = 100;
|
||||
|
||||
private AccessibleFieldPanelDelegate delegate;
|
||||
private static FontMetrics fontMetrics =
|
||||
new JLabel("Dummy").getFontMetrics(new Font("Monospaced", Font.PLAIN, 12));
|
||||
private List<AnchoredLayout> layouts;
|
||||
private JPanel panel = new JPanel();
|
||||
private TestAccessibleContext testContext = new TestAccessibleContext();
|
||||
private int fieldLineHeight = fontMetrics.getHeight() + 1;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
layouts = List.of(buildAnchoredLayout(0, 0, 3), buildAnchoredLayout(1, FIELD_HEIGHT, 13));
|
||||
delegate = new AccessibleFieldPanelDelegate(layouts, testContext, panel);
|
||||
delegate.setFieldDescriptionProvider(new TestFieldDescriptionProvider());
|
||||
delegate.setCaret(new FieldLocation(BigInteger.ZERO, 0, 0, 0), EventTrigger.API_CALL);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetChildrenCount() {
|
||||
layouts = List.of(buildAnchoredLayout(0, 0, 3), buildAnchoredLayout(1, FIELD_HEIGHT, 5));
|
||||
delegate.setLayouts(layouts);
|
||||
|
||||
assertEquals(8, delegate.getFieldCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleChildFromOrdinal() {
|
||||
assertEquals("Field 0, 0", getId(delegate.getAccessibleField(0)));
|
||||
assertEquals("Field 0, 1", getId(delegate.getAccessibleField(1)));
|
||||
assertEquals("Field 0, 2", getId(delegate.getAccessibleField(2)));
|
||||
assertEquals("Field 1, 0", getId(delegate.getAccessibleField(3)));
|
||||
assertEquals("Field 1, 1", getId(delegate.getAccessibleField(4)));
|
||||
assertEquals("Field 1, 11", getId(delegate.getAccessibleField(14)));
|
||||
assertEquals(null, delegate.getAccessibleField(16));
|
||||
|
||||
assertEquals(null, delegate.getAccessibleField(-1));
|
||||
}
|
||||
|
||||
private String getId(AccessibleField field) {
|
||||
String text = field.getText();
|
||||
int indexOf = text.indexOf(":");
|
||||
return text.substring(0, indexOf);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleChildFromFieldLocation() {
|
||||
assertEquals("Field 0, 0", getId(delegate.getAccessibleField(fieldLoc(0, 0))));
|
||||
assertEquals("Field 0, 1", getId(delegate.getAccessibleField(fieldLoc(0, 1))));
|
||||
assertEquals("Field 0, 2", getId(delegate.getAccessibleField(fieldLoc(0, 2))));
|
||||
assertEquals("Field 1, 0", getId(delegate.getAccessibleField(fieldLoc(1, 0))));
|
||||
assertEquals("Field 1, 1", getId(delegate.getAccessibleField(fieldLoc(1, 1))));
|
||||
assertEquals("Field 1, 2", getId(delegate.getAccessibleField(fieldLoc(1, 2))));
|
||||
assertEquals("Field 1, 12", getId(delegate.getAccessibleField(fieldLoc(1, 12))));
|
||||
assertEquals(null, delegate.getAccessibleField(fieldLoc(15, 0)));
|
||||
assertEquals(null, delegate.getAccessibleField(fieldLoc(-1, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleChildCache() {
|
||||
AccessibleField accessibleField1 = delegate.getAccessibleField(0);
|
||||
assertEquals("Field 0, 0", getId(accessibleField1));
|
||||
|
||||
AccessibleField accessibleField2 = delegate.getAccessibleField(0);
|
||||
assertEquals("Field 0, 0", getId(accessibleField1));
|
||||
|
||||
assertTrue(accessibleField1 == accessibleField2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessbileAt() {
|
||||
AccessibleField accessibleField =
|
||||
(AccessibleField) delegate.getAccessibleAt(new Point(0, 0));
|
||||
assertEquals("Field 0, 0", getId(accessibleField));
|
||||
|
||||
accessibleField = (AccessibleField) delegate.getAccessibleAt(new Point(210, 0));
|
||||
assertEquals("Field 0, 2", getId(accessibleField));
|
||||
|
||||
accessibleField = (AccessibleField) delegate.getAccessibleAt(new Point(220, 112));
|
||||
assertEquals("Field 1, 2", getId(accessibleField));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetFieldDescription() {
|
||||
assertEquals("Description for field: 0, 0", delegate.getFieldDescription());
|
||||
delegate.setCaret(new FieldLocation(BigInteger.ONE, 2, 0, 0), EventTrigger.API_CALL);
|
||||
assertEquals("Description for field: 1, 2", delegate.getFieldDescription());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCaretPosition() {
|
||||
assertEquals(0, delegate.getCaretPosition());
|
||||
delegate.setCaret(new FieldLocation(BigInteger.ONE, 2, 0, 3), EventTrigger.API_CALL);
|
||||
assertEquals(3, delegate.getCaretPosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCharCount() {
|
||||
// the first field is "Field 0, 0: line 1\nField 0, 0: line 2", so length is 37
|
||||
assertEquals(37, delegate.getCharCount());
|
||||
delegate.setCaret(fieldLoc(1, 11), EventTrigger.API_CALL);
|
||||
// the active field is now "Field 1, 10: line 1 Field 1,10: line 2", so length is 39
|
||||
assertEquals(39, delegate.getCharCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCharBounds() {
|
||||
int row = 0;
|
||||
int fieldNum = 0;
|
||||
delegate.setCaret(fieldLoc(row, fieldNum), EventTrigger.API_CALL);
|
||||
|
||||
assertEquals(rect(0, 0, 7, fieldLineHeight), delegate.getCharacterBounds(0));
|
||||
assertEquals(rect(7, 0, 7, fieldLineHeight), delegate.getCharacterBounds(1));
|
||||
assertEquals(rect(14, 0, 7, fieldLineHeight), delegate.getCharacterBounds(2));
|
||||
|
||||
row = 0;
|
||||
fieldNum = 1;
|
||||
delegate.setCaret(fieldLoc(row, fieldNum), EventTrigger.API_CALL);
|
||||
int startX = FIELD_WIDTH * fieldNum;
|
||||
int startY = FIELD_HEIGHT * row;
|
||||
|
||||
assertEquals(rect(startX, startY, 7, fieldLineHeight), delegate.getCharacterBounds(0));
|
||||
assertEquals(rect(startX + 7, startY, 7, fieldLineHeight), delegate.getCharacterBounds(1));
|
||||
assertEquals(rect(startX + 14, startY, 7, fieldLineHeight), delegate.getCharacterBounds(2));
|
||||
|
||||
row = 1;
|
||||
fieldNum = 3;
|
||||
delegate.setCaret(fieldLoc(row, fieldNum), EventTrigger.API_CALL);
|
||||
startX = FIELD_WIDTH * fieldNum;
|
||||
startY = FIELD_HEIGHT * row;
|
||||
|
||||
assertEquals(rect(startX, startY, 7, fieldLineHeight), delegate.getCharacterBounds(0));
|
||||
assertEquals(rect(startX + 7, startY, 7, fieldLineHeight), delegate.getCharacterBounds(1));
|
||||
assertEquals(rect(startX + 14, startY, 7, fieldLineHeight), delegate.getCharacterBounds(2));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIndexAtPoint_1stRow1stFieldActive() {
|
||||
int row = 0;
|
||||
int fieldNum = 0;
|
||||
delegate.setCaret(fieldLoc(row, fieldNum), EventTrigger.API_CALL);
|
||||
// char size is 8 x 16
|
||||
// second line starts at char 19
|
||||
// the field starts at 0,0 and contains:
|
||||
//
|
||||
// Field 0,0: line 1
|
||||
// Field 0,0: line 2
|
||||
|
||||
assertEquals(0, delegate.getIndexAtPoint(new Point(0, 0)));
|
||||
assertEquals(0, delegate.getIndexAtPoint(new Point(3, 3)));
|
||||
assertEquals(1, delegate.getIndexAtPoint(new Point(8, 3)));
|
||||
assertEquals(11, delegate.getIndexAtPoint(new Point(80, 0)));
|
||||
assertEquals(0, delegate.getIndexAtPoint(new Point(0, fieldLineHeight - 1)));
|
||||
assertEquals(19, delegate.getIndexAtPoint(new Point(0, fieldLineHeight)));
|
||||
assertEquals(19, delegate.getIndexAtPoint(new Point(0, fieldLineHeight + 1)));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIndexAtPoint_2ndRow3rdFieldActive() {
|
||||
int row = 1;
|
||||
int fieldNum = 2;
|
||||
delegate.setCaret(fieldLoc(row, fieldNum), EventTrigger.API_CALL);
|
||||
// char size is 8 x 16
|
||||
// second line starts at char 19
|
||||
// field upper left corner is at point 200,100
|
||||
// the field starts at 0,0 and contains:
|
||||
//
|
||||
// Field 0,0: line 1
|
||||
// Field 0,0: line 2
|
||||
|
||||
assertEquals(0, delegate.getIndexAtPoint(new Point(200, 100)));
|
||||
assertEquals(0, delegate.getIndexAtPoint(new Point(203, 103)));
|
||||
assertEquals(1, delegate.getIndexAtPoint(new Point(208, 103)));
|
||||
assertEquals(11, delegate.getIndexAtPoint(new Point(280, 100)));
|
||||
assertEquals(0, delegate.getIndexAtPoint(new Point(200, 100 + fieldLineHeight - 1)));
|
||||
assertEquals(19, delegate.getIndexAtPoint(new Point(200, 100 + fieldLineHeight)));
|
||||
assertEquals(19, delegate.getIndexAtPoint(new Point(200, 100 + fieldLineHeight + 1)));
|
||||
|
||||
assertEquals(-1, delegate.getIndexAtPoint(new Point(0, 0)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAtIndex() {
|
||||
delegate.setCaret(fieldLoc(0, 0), EventTrigger.API_CALL);
|
||||
|
||||
assertEquals("F", delegate.getAtIndex(AccessibleText.CHARACTER, 0));
|
||||
assertEquals("i", delegate.getAtIndex(AccessibleText.CHARACTER, 1));
|
||||
assertEquals("e", delegate.getAtIndex(AccessibleText.CHARACTER, 2));
|
||||
assertEquals("1", delegate.getAtIndex(AccessibleText.CHARACTER, 17));
|
||||
assertEquals("2", delegate.getAtIndex(AccessibleText.CHARACTER, 36));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAfterIndex() {
|
||||
delegate.setCaret(fieldLoc(0, 0), EventTrigger.API_CALL);
|
||||
|
||||
assertEquals("i", delegate.getAfterIndex(AccessibleText.CHARACTER, 0));
|
||||
assertEquals("e", delegate.getAfterIndex(AccessibleText.CHARACTER, 1));
|
||||
assertEquals("l", delegate.getAfterIndex(AccessibleText.CHARACTER, 2));
|
||||
assertEquals("1", delegate.getAfterIndex(AccessibleText.CHARACTER, 16));
|
||||
assertEquals("2", delegate.getAfterIndex(AccessibleText.CHARACTER, 35));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBeforeIndex() {
|
||||
delegate.setCaret(fieldLoc(0, 0), EventTrigger.API_CALL);
|
||||
|
||||
assertEquals(null, delegate.getBeforeIndex(AccessibleText.CHARACTER, 0));
|
||||
assertEquals("F", delegate.getBeforeIndex(AccessibleText.CHARACTER, 1));
|
||||
assertEquals("i", delegate.getBeforeIndex(AccessibleText.CHARACTER, 2));
|
||||
assertEquals("1", delegate.getBeforeIndex(AccessibleText.CHARACTER, 18));
|
||||
assertEquals("2", delegate.getBeforeIndex(AccessibleText.CHARACTER, 37));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSelectionStartEndAndText_noSelection() {
|
||||
delegate.setCaret(fieldLoc(0, 0), EventTrigger.API_CALL);
|
||||
delegate.setSelection(null, EventTrigger.API_CALL);
|
||||
|
||||
assertEquals(0, delegate.getSelectionStart());
|
||||
assertEquals(0, delegate.getSelectionEnd());
|
||||
assertEquals(null, delegate.getSelectedText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSelectionStartEndAndText_withSelection() {
|
||||
delegate.setCaret(fieldLoc(0, 0), EventTrigger.API_CALL);
|
||||
FieldSelection fieldSelection = new FieldSelection();
|
||||
fieldSelection.addRange(0, 1);
|
||||
delegate.setSelection(fieldSelection, EventTrigger.API_CALL);
|
||||
|
||||
assertEquals(0, delegate.getSelectionStart());
|
||||
assertEquals(delegate.getCharCount(), delegate.getSelectionEnd());
|
||||
assertEquals("Field 0, 0: Line 1 Field 0, 0: Line 2", delegate.getSelectedText());
|
||||
}
|
||||
|
||||
private FieldLocation fieldLoc(int index, int fieldNum) {
|
||||
return new FieldLocation(BigInteger.valueOf(index), fieldNum, 0, 0);
|
||||
}
|
||||
|
||||
private Rectangle rect(int x, int y, int w, int h) {
|
||||
return new Rectangle(x, y, w, h);
|
||||
}
|
||||
|
||||
private AnchoredLayout buildAnchoredLayout(int index, int yPos, int numFields) {
|
||||
return new AnchoredLayout(buildLayout(index, numFields), BigInteger.valueOf(index), yPos);
|
||||
}
|
||||
|
||||
private Layout buildLayout(int index, int numFields) {
|
||||
return new DummyLayout(index, numFields);
|
||||
}
|
||||
|
||||
private class DummyLayout extends RowLayout {
|
||||
|
||||
public DummyLayout(int index, int numFields) {
|
||||
super(createFields(index, numFields), 0);
|
||||
}
|
||||
|
||||
private static Field[] createFields(int index, int numFields) {
|
||||
Field[] fields = new Field[numFields];
|
||||
for (int i = 0; i < numFields; i++) {
|
||||
fields[i] = new DummyField(index, i);
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class DummyField extends VerticalLayoutTextField {
|
||||
|
||||
public DummyField(int index, int fieldNum) {
|
||||
super(createSubFields(index, fieldNum), fieldNum * FIELD_WIDTH, FIELD_WIDTH, 2, null);
|
||||
}
|
||||
|
||||
private static List<FieldElement> createSubFields(int index, int fieldNum) {
|
||||
List<FieldElement> list = new ArrayList<>();
|
||||
|
||||
String text = "Field " + index + ", " + fieldNum + ": Line 1";
|
||||
AttributedString as = new AttributedString(text, Color.BLACK, fontMetrics);
|
||||
list.add(new TextFieldElement(as, 0, 0));
|
||||
|
||||
text = "Field " + index + ", " + fieldNum + ": Line 2";
|
||||
as = new AttributedString(text, Color.BLACK, fontMetrics);
|
||||
list.add(new TextFieldElement(as, 1, 0));
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TestFieldDescriptionProvider implements FieldDescriptionProvider {
|
||||
|
||||
@Override
|
||||
public String getDescription(FieldLocation loc, Field field) {
|
||||
return "Description for field: " + loc.getIndex() + ", " + loc.getFieldNum();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class TestAccessibleContext extends AccessibleContext {
|
||||
|
||||
@Override
|
||||
public AccessibleRole getAccessibleRole() {
|
||||
return AccessibleRole.TEXT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessibleStateSet getAccessibleStateSet() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessibleIndexInParent() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAccessibleChildrenCount() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Accessible getAccessibleChild(int i) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Locale getLocale() throws IllegalComponentStateException {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+415
@@ -0,0 +1,415 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package docking.widgets.fieldpanel;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.accessibility.*;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import docking.widgets.fieldpanel.field.*;
|
||||
import generic.test.AbstractGenericTest;
|
||||
|
||||
public class AccessibleFieldTest extends AbstractGenericTest {
|
||||
private static final int PARENT_X = 1000;
|
||||
private static final int PARENT_Y = 1000;
|
||||
private static final int FIELD_X = 100;
|
||||
private static final int FIELD_Y = 100;
|
||||
private static final int FIELD_WIDTH = 75;
|
||||
|
||||
private JPanel parent = new JPanel() {
|
||||
public Point getLocationOnScreen() {
|
||||
return new Point(PARENT_X, PARENT_Y);
|
||||
}
|
||||
};
|
||||
private TestField testField;
|
||||
private AccessibleField accessibleField;
|
||||
private Rectangle boundsInParent;
|
||||
private int fieldHeight;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
testField = new TestField(FIELD_X, FIELD_WIDTH, "line1", "line2");
|
||||
fieldHeight = testField.getHeight();
|
||||
boundsInParent = new Rectangle(FIELD_X, FIELD_Y, FIELD_WIDTH, fieldHeight);
|
||||
accessibleField = new AccessibleField(testField, parent, 0, boundsInParent);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetName() {
|
||||
assertEquals("Field", accessibleField.getAccessibleName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleContext() {
|
||||
assertEquals(accessibleField, accessibleField.getAccessibleContext());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleText() {
|
||||
assertEquals(accessibleField, accessibleField.getAccessibleText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleComponent() {
|
||||
assertEquals(accessibleField, accessibleField.getAccessibleComponent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleRole() {
|
||||
assertEquals(AccessibleRole.TEXT, accessibleField.getAccessibleRole());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAccessibleIndexInParent() {
|
||||
assertEquals(0, accessibleField.getAccessibleIndexInParent());
|
||||
accessibleField = new AccessibleField(testField, parent, 5, boundsInParent);
|
||||
assertEquals(5, accessibleField.getAccessibleIndexInParent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleStateSet() {
|
||||
AccessibleStateSet set = accessibleField.getAccessibleStateSet();
|
||||
assertTrue(set.contains(AccessibleState.MULTI_LINE));
|
||||
assertTrue(set.contains(AccessibleState.TRANSIENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLocale() {
|
||||
assertEquals(parent.getLocale(), accessibleField.getLocale());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleChildCount() {
|
||||
assertEquals(0, accessibleField.getAccessibleChildrenCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAccessibleChild() {
|
||||
assertNull(accessibleField.getAccessibleChild(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBounds() {
|
||||
assertEquals(new Rectangle(FIELD_X, FIELD_Y, FIELD_WIDTH, fieldHeight),
|
||||
accessibleField.getBounds());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetIndexAtPoint() {
|
||||
assertEquals(0, accessibleField.getIndexAtPoint(new Point(0, 0)));
|
||||
assertEquals(3,
|
||||
accessibleField.getIndexAtPoint(new Point(3 * testField.getCharWidth(), 0)));
|
||||
assertEquals(6, accessibleField.getIndexAtPoint(new Point(0, testField.getLineHeight())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCharacterBounds() {
|
||||
// text = "line1 line2"
|
||||
int charWidth = testField.getCharWidth();
|
||||
int lineHeight = testField.getLineHeight();
|
||||
|
||||
assertEquals(new Rectangle(0, 0, charWidth, lineHeight),
|
||||
accessibleField.getCharacterBounds(0));
|
||||
|
||||
assertEquals(new Rectangle(4 * charWidth, 0, charWidth, lineHeight),
|
||||
accessibleField.getCharacterBounds(4));
|
||||
|
||||
// this is the imaginary space char that separates the lines
|
||||
assertEquals(new Rectangle(5 * charWidth, 0, 0, lineHeight),
|
||||
accessibleField.getCharacterBounds(5));
|
||||
|
||||
assertEquals(new Rectangle(0, lineHeight, charWidth, lineHeight),
|
||||
accessibleField.getCharacterBounds(6));
|
||||
|
||||
// this is the last char on the 2nd line
|
||||
assertEquals(new Rectangle(4 * charWidth, lineHeight, charWidth, lineHeight),
|
||||
accessibleField.getCharacterBounds(10));
|
||||
|
||||
// this is just past the last char on the 2nd line
|
||||
assertEquals(new Rectangle(0, 0, 0, 0), accessibleField.getCharacterBounds(11));
|
||||
|
||||
assertEquals(new Rectangle(0, 0, 0, 0), accessibleField.getCharacterBounds(12));
|
||||
assertEquals(new Rectangle(0, 0, 0, 0), accessibleField.getCharacterBounds(-1));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetCharCount() {
|
||||
// text = "line1 line2"
|
||||
assertEquals(testField.getText().length(), accessibleField.getCharCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAtIndex_char() {
|
||||
// text = "line1 line2"
|
||||
assertEquals("l", accessibleField.getAtIndex(AccessibleText.CHARACTER, 0));
|
||||
assertEquals("i", accessibleField.getAtIndex(AccessibleText.CHARACTER, 1));
|
||||
assertEquals("n", accessibleField.getAtIndex(AccessibleText.CHARACTER, 2));
|
||||
assertEquals("e", accessibleField.getAtIndex(AccessibleText.CHARACTER, 3));
|
||||
assertEquals("1", accessibleField.getAtIndex(AccessibleText.CHARACTER, 4));
|
||||
assertEquals(" ", accessibleField.getAtIndex(AccessibleText.CHARACTER, 5));
|
||||
assertEquals("l", accessibleField.getAtIndex(AccessibleText.CHARACTER, 6));
|
||||
assertEquals("i", accessibleField.getAtIndex(AccessibleText.CHARACTER, 7));
|
||||
assertEquals("n", accessibleField.getAtIndex(AccessibleText.CHARACTER, 8));
|
||||
assertEquals("e", accessibleField.getAtIndex(AccessibleText.CHARACTER, 9));
|
||||
assertEquals("2", accessibleField.getAtIndex(AccessibleText.CHARACTER, 10));
|
||||
|
||||
assertEquals(null, accessibleField.getAtIndex(AccessibleText.CHARACTER, 11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBeforeIndex_char() {
|
||||
// text = "line1 line2"
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 0));
|
||||
assertEquals("l", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 1));
|
||||
assertEquals("i", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 2));
|
||||
assertEquals("n", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 3));
|
||||
assertEquals("e", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 4));
|
||||
assertEquals("1", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 5));
|
||||
assertEquals(" ", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 6));
|
||||
assertEquals("l", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 7));
|
||||
assertEquals("i", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 8));
|
||||
assertEquals("n", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 9));
|
||||
assertEquals("e", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 10));
|
||||
assertEquals("2", accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 11));
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.CHARACTER, 12));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAfterIndex_char() {
|
||||
// text = "line1 line2"
|
||||
assertEquals("i", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 0));
|
||||
assertEquals("n", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 1));
|
||||
assertEquals("e", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 2));
|
||||
assertEquals("1", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 3));
|
||||
assertEquals(" ", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 4));
|
||||
assertEquals("l", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 5));
|
||||
assertEquals("i", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 6));
|
||||
assertEquals("n", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 7));
|
||||
assertEquals("e", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 8));
|
||||
assertEquals("2", accessibleField.getAfterIndex(AccessibleText.CHARACTER, 9));
|
||||
|
||||
assertEquals(null, accessibleField.getAtIndex(AccessibleText.CHARACTER, 11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAtIndex_word() {
|
||||
// text = "line1 line2"
|
||||
assertEquals("line1", accessibleField.getAtIndex(AccessibleText.WORD, 0));
|
||||
assertEquals("line1", accessibleField.getAtIndex(AccessibleText.WORD, 1));
|
||||
assertEquals("line1", accessibleField.getAtIndex(AccessibleText.WORD, 2));
|
||||
assertEquals("line1", accessibleField.getAtIndex(AccessibleText.WORD, 3));
|
||||
assertEquals("line1", accessibleField.getAtIndex(AccessibleText.WORD, 4));
|
||||
assertEquals(" ", accessibleField.getAtIndex(AccessibleText.WORD, 5));
|
||||
assertEquals("line2", accessibleField.getAtIndex(AccessibleText.WORD, 6));
|
||||
assertEquals("line2", accessibleField.getAtIndex(AccessibleText.WORD, 7));
|
||||
assertEquals("line2", accessibleField.getAtIndex(AccessibleText.WORD, 8));
|
||||
assertEquals("line2", accessibleField.getAtIndex(AccessibleText.WORD, 9));
|
||||
assertEquals("line2", accessibleField.getAtIndex(AccessibleText.WORD, 10));
|
||||
|
||||
assertEquals(null, accessibleField.getAtIndex(AccessibleText.WORD, 11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBeforeIndex_word() {
|
||||
// text = "line1 line2"
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.WORD, 0));
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.WORD, 1));
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.WORD, 2));
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.WORD, 3));
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.WORD, 4));
|
||||
assertEquals("line1", accessibleField.getBeforeIndex(AccessibleText.WORD, 5));
|
||||
assertEquals(" ", accessibleField.getBeforeIndex(AccessibleText.WORD, 6));
|
||||
assertEquals(" ", accessibleField.getBeforeIndex(AccessibleText.WORD, 7));
|
||||
assertEquals(" ", accessibleField.getBeforeIndex(AccessibleText.WORD, 8));
|
||||
assertEquals(" ", accessibleField.getBeforeIndex(AccessibleText.WORD, 9));
|
||||
assertEquals(" ", accessibleField.getBeforeIndex(AccessibleText.WORD, 10));
|
||||
assertEquals("line2", accessibleField.getBeforeIndex(AccessibleText.WORD, 11));
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.WORD, 12));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAfterIndex_word() {
|
||||
// text = "line1 line2"
|
||||
assertEquals(" ", accessibleField.getAfterIndex(AccessibleText.WORD, 0));
|
||||
assertEquals(" ", accessibleField.getAfterIndex(AccessibleText.WORD, 1));
|
||||
assertEquals(" ", accessibleField.getAfterIndex(AccessibleText.WORD, 2));
|
||||
assertEquals(" ", accessibleField.getAfterIndex(AccessibleText.WORD, 3));
|
||||
assertEquals(" ", accessibleField.getAfterIndex(AccessibleText.WORD, 4));
|
||||
assertEquals("line2", accessibleField.getAfterIndex(AccessibleText.WORD, 5));
|
||||
assertEquals(null, accessibleField.getAfterIndex(AccessibleText.WORD, 6));
|
||||
assertEquals(null, accessibleField.getAfterIndex(AccessibleText.WORD, 7));
|
||||
assertEquals(null, accessibleField.getAfterIndex(AccessibleText.WORD, 8));
|
||||
assertEquals(null, accessibleField.getAfterIndex(AccessibleText.WORD, 9));
|
||||
assertEquals(null, accessibleField.getAfterIndex(AccessibleText.WORD, 10));
|
||||
assertEquals(null, accessibleField.getAfterIndex(AccessibleText.WORD, 11));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetAtIndex_sentence() {
|
||||
testField = new TestField(FIELD_X, FIELD_WIDTH, "This line. Why?", "Why not? Wow");
|
||||
accessibleField = new AccessibleField(testField, parent, 0, boundsInParent);
|
||||
assertEquals("This line. ", accessibleField.getAtIndex(AccessibleText.SENTENCE, 0));
|
||||
assertEquals("This line. ", accessibleField.getAtIndex(AccessibleText.SENTENCE, 4));
|
||||
assertEquals("This line. ", accessibleField.getAtIndex(AccessibleText.SENTENCE, 10));
|
||||
|
||||
assertEquals("Why? ", accessibleField.getAtIndex(AccessibleText.SENTENCE, 11));
|
||||
assertEquals("Why? ", accessibleField.getAtIndex(AccessibleText.SENTENCE, 15));
|
||||
|
||||
assertEquals("Why not? ", accessibleField.getAtIndex(AccessibleText.SENTENCE, 16));
|
||||
assertEquals("Why not? ", accessibleField.getAtIndex(AccessibleText.SENTENCE, 19));
|
||||
assertEquals("Why not? ", accessibleField.getAtIndex(AccessibleText.SENTENCE, 23));
|
||||
|
||||
assertEquals(null, accessibleField.getAtIndex(AccessibleText.SENTENCE, 500));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetBeforeIndex_sentence() {
|
||||
testField = new TestField(FIELD_X, FIELD_WIDTH, "This line. Why?", "Why not? Wow");
|
||||
accessibleField = new AccessibleField(testField, parent, 0, boundsInParent);
|
||||
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 0));
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 4));
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 10));
|
||||
|
||||
assertEquals("This line. ", accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 11));
|
||||
assertEquals("This line. ", accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 15));
|
||||
|
||||
assertEquals("Why? ", accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 16));
|
||||
assertEquals("Why? ", accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 19));
|
||||
assertEquals("Why? ", accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 23));
|
||||
|
||||
assertEquals(null, accessibleField.getBeforeIndex(AccessibleText.SENTENCE, 500));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAfterIndex_sentence() {
|
||||
testField = new TestField(FIELD_X, FIELD_WIDTH, "This line. Why?", "Why not? Wow");
|
||||
accessibleField = new AccessibleField(testField, parent, 0, boundsInParent);
|
||||
|
||||
assertEquals("Why? ", accessibleField.getAfterIndex(AccessibleText.SENTENCE, 0));
|
||||
assertEquals("Why? ", accessibleField.getAfterIndex(AccessibleText.SENTENCE, 4));
|
||||
assertEquals("Why? ", accessibleField.getAfterIndex(AccessibleText.SENTENCE, 10));
|
||||
|
||||
assertEquals("Why not? ", accessibleField.getAfterIndex(AccessibleText.SENTENCE, 11));
|
||||
assertEquals("Why not? ", accessibleField.getAfterIndex(AccessibleText.SENTENCE, 15));
|
||||
|
||||
assertEquals(null, accessibleField.getAfterIndex(AccessibleText.SENTENCE, 500));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCaretPos() {
|
||||
assertEquals(0, accessibleField.getCaretPosition());
|
||||
accessibleField.setCaretPos(5);
|
||||
assertEquals(5, accessibleField.getCaretPosition());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSelection() {
|
||||
// text = "line1 line2"
|
||||
assertEquals(0, accessibleField.getSelectionStart());
|
||||
assertEquals(0, accessibleField.getSelectionEnd());
|
||||
assertEquals(null, accessibleField.getSelectedText());
|
||||
|
||||
accessibleField.setSelected(true);
|
||||
assertEquals(0, accessibleField.getSelectionStart());
|
||||
assertEquals(11, accessibleField.getSelectionEnd());
|
||||
assertEquals("line1 line2", accessibleField.getSelectedText());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testContainsPoint() {
|
||||
assertTrue(accessibleField.contains(new Point(0, 0)));
|
||||
int width = testField.getWidth();
|
||||
int height = testField.getHeight();
|
||||
assertTrue(accessibleField.contains(new Point(width - 1, height - 1)));
|
||||
assertFalse(accessibleField.contains(new Point(width, height - 1)));
|
||||
assertFalse(accessibleField.contains(new Point(width - 1, height)));
|
||||
assertFalse(accessibleField.contains(new Point(-1, 0)));
|
||||
assertFalse(accessibleField.contains(new Point(0, -1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLocation() {
|
||||
Point expectedLocationOnScreen = new Point(FIELD_X, FIELD_Y);
|
||||
assertEquals(expectedLocationOnScreen, accessibleField.getLocation());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetLocationOnScreen() {
|
||||
Rectangle boundsRelativeToParent = accessibleField.getBounds();
|
||||
Point expectedLocationOnScreen =
|
||||
new Point(PARENT_X + boundsRelativeToParent.x, PARENT_Y + boundsRelativeToParent.y);
|
||||
assertEquals(expectedLocationOnScreen, accessibleField.getLocationOnScreen());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSize() {
|
||||
assertEquals(new Dimension(FIELD_WIDTH, fieldHeight), accessibleField.getSize());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTextOffset() {
|
||||
assertEquals(0, accessibleField.getTextOffset(0, 0));
|
||||
assertEquals(1, accessibleField.getTextOffset(0, 1));
|
||||
assertEquals(5, accessibleField.getTextOffset(0, 5));
|
||||
assertEquals(6, accessibleField.getTextOffset(1, 0));
|
||||
assertEquals(5, accessibleField.getTextOffset(0, 10));
|
||||
assertEquals(11, accessibleField.getTextOffset(1, 10));
|
||||
}
|
||||
|
||||
private static class TestField extends VerticalLayoutTextField {
|
||||
private static FontMetrics metrics = createFontMetrics();
|
||||
|
||||
private static FontMetrics createFontMetrics() {
|
||||
Font f = new Font("Monospaced", Font.PLAIN, 12);
|
||||
JLabel label = new JLabel("Hey");
|
||||
return label.getFontMetrics(f);
|
||||
}
|
||||
|
||||
public TestField(int startX, int width, String... lines) {
|
||||
super(createElements(lines), startX, width, lines.length, null);
|
||||
}
|
||||
|
||||
public int getLineHeight() {
|
||||
return metrics.getHeight() + 1; // our lines are always 1 more than the font
|
||||
}
|
||||
|
||||
public int getCharWidth() {
|
||||
return metrics.charWidth('a'); // monospace so all chars same width
|
||||
}
|
||||
|
||||
private static List<FieldElement> createElements(String[] lines) {
|
||||
List<FieldElement> fieldElements = new ArrayList<>();
|
||||
int row = 0;
|
||||
for (String line : lines) {
|
||||
AttributedString as = new AttributedString(line, Color.black, metrics);
|
||||
fieldElements.add(new TextFieldElement(as, row++, 0));
|
||||
}
|
||||
return fieldElements;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user