diff --git a/src/main/java/io/github/dsheirer/gui/control/AccessibilityUtil.java b/src/main/java/io/github/dsheirer/gui/control/AccessibilityUtil.java
new file mode 100644
index 00000000..9d895bd1
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/gui/control/AccessibilityUtil.java
@@ -0,0 +1,84 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2026 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.gui.control;
+
+import javafx.collections.ListChangeListener;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToggleButton;
+import org.controlsfx.control.SegmentedButton;
+
+/**
+ * Screen reader (accessibility) utilities for JavaFX controls.
+ */
+public final class AccessibilityUtil
+{
+ private AccessibilityUtil()
+ {
+ //Not instantiable
+ }
+
+ /**
+ * Provides an accessible name for each toggle button in a segmented button group, combining the group label with
+ * the button text (e.g. "Channel Bandwidth: 12.5 kHz").
+ *
+ * A segmented button is a container, so a label can't identify it via Label.setLabelFor() in a way that screen
+ * readers announce: each toggle button receives focus individually and would otherwise be announced by its own text
+ * only, without the context of the group. Buttons added to the group later are also named.
+ *
+ * @param groupLabel that visually identifies the segmented button group
+ * @param segmentedButton containing the toggle buttons
+ */
+ public static void labelGroup(Label groupLabel, SegmentedButton segmentedButton)
+ {
+ String groupName = groupLabel.getText() == null ? "" : groupLabel.getText().trim();
+
+ //Remove a trailing colon so that the name doesn't announce "colon colon"
+ if(groupName.endsWith(":"))
+ {
+ groupName = groupName.substring(0, groupName.length() - 1).trim();
+ }
+
+ final String name = groupName;
+
+ for(ToggleButton button: segmentedButton.getButtons())
+ {
+ setGroupName(name, button);
+ }
+
+ segmentedButton.getButtons().addListener((ListChangeListener)change -> {
+ while(change.next())
+ {
+ for(ToggleButton added: change.getAddedSubList())
+ {
+ setGroupName(name, added);
+ }
+ }
+ });
+ }
+
+ /**
+ * Sets the accessible text for the button as the group name and button text.
+ */
+ private static void setGroupName(String groupName, ToggleButton button)
+ {
+ String text = button.getText() == null ? "" : button.getText();
+ button.setAccessibleText(groupName.isEmpty() ? text : groupName + ": " + text);
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/gui/control/AccessibleToggleSwitch.java b/src/main/java/io/github/dsheirer/gui/control/AccessibleToggleSwitch.java
new file mode 100644
index 00000000..805cb6dc
--- /dev/null
+++ b/src/main/java/io/github/dsheirer/gui/control/AccessibleToggleSwitch.java
@@ -0,0 +1,80 @@
+/*
+ * *****************************************************************************
+ * Copyright (C) 2014-2026 Dennis Sheirer
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
+ * ****************************************************************************
+ */
+
+package io.github.dsheirer.gui.control;
+
+import javafx.scene.AccessibleAction;
+import javafx.scene.AccessibleAttribute;
+import javafx.scene.AccessibleRole;
+import org.controlsfx.control.ToggleSwitch;
+
+/**
+ * ControlsFX toggle switch that exposes its role and on/off state to screen readers.
+ *
+ * The ControlsFX toggle switch doesn't provide an accessible role or selected state, so assistive technology sees it
+ * as a generic node with no state. This version reports itself as a toggle button, reports the selected state, and
+ * supports the accessible fire (toggle) action. The accessible name comes from the switch text, or from a label that
+ * identifies this switch via Label.setLabelFor().
+ */
+public class AccessibleToggleSwitch extends ToggleSwitch
+{
+ /**
+ * Constructs an instance with no text. Use Label.setLabelFor() or setAccessibleText() to provide an accessible name.
+ */
+ public AccessibleToggleSwitch()
+ {
+ this(null);
+ }
+
+ /**
+ * Constructs an instance
+ * @param text to display alongside the switch, also used as the accessible name.
+ */
+ public AccessibleToggleSwitch(String text)
+ {
+ super(text);
+ setAccessibleRole(AccessibleRole.TOGGLE_BUTTON);
+ selectedProperty().addListener((observable, oldValue, newValue) ->
+ notifyAccessibleAttributeChanged(AccessibleAttribute.SELECTED));
+ }
+
+ @Override
+ public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters)
+ {
+ if(attribute == AccessibleAttribute.SELECTED)
+ {
+ return isSelected();
+ }
+
+ return super.queryAccessibleAttribute(attribute, parameters);
+ }
+
+ @Override
+ public void executeAccessibleAction(AccessibleAction action, Object... parameters)
+ {
+ if(action == AccessibleAction.FIRE)
+ {
+ fire();
+ }
+ else
+ {
+ super.executeAccessibleAction(action, parameters);
+ }
+ }
+}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasBulkEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasBulkEditor.java
index b152d121..93377acb 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasBulkEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasBulkEditor.java
@@ -21,6 +21,7 @@ package io.github.dsheirer.gui.playlist.alias;
import com.google.common.collect.Ordering;
import io.github.dsheirer.alias.Alias;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.icon.Icon;
import io.github.dsheirer.playlist.PlaylistManager;
@@ -100,6 +101,7 @@ public class AliasBulkEditor extends Editor>
gridPane.getChildren().add(separator);
Label colorLabel = new Label("Color");
+ colorLabel.setLabelFor(getColorPicker());
GridPane.setHalignment(colorLabel, HPos.RIGHT);
GridPane.setConstraints(colorLabel, 0, ++row);
gridPane.getChildren().add(colorLabel);
@@ -114,6 +116,7 @@ public class AliasBulkEditor extends Editor>
gridPane.getChildren().add(getResetColorButton());
Label iconLabel = new Label("Icon");
+ iconLabel.setLabelFor(getIconNodeComboBox());
GridPane.setHalignment(iconLabel, HPos.RIGHT);
GridPane.setConstraints(iconLabel, 0, ++row);
gridPane.getChildren().add(iconLabel);
@@ -125,6 +128,7 @@ public class AliasBulkEditor extends Editor>
gridPane.getChildren().add(getApplyIconButton());
Label listenLabel = new Label("Listen");
+ listenLabel.setLabelFor(getMonitorAudioToggleSwitch());
GridPane.setHalignment(listenLabel, HPos.RIGHT);
GridPane.setConstraints(listenLabel, 0, ++row);
gridPane.getChildren().add(listenLabel);
@@ -133,6 +137,7 @@ public class AliasBulkEditor extends Editor>
gridPane.getChildren().add(getMonitorAudioToggleSwitch());
Label priorityLabel = new Label("Priority");
+ priorityLabel.setLabelFor(getMonitorPriorityComboBox());
GridPane.setHalignment(priorityLabel, HPos.RIGHT);
GridPane.setConstraints(priorityLabel, 2, row);
gridPane.getChildren().add(priorityLabel);
@@ -144,6 +149,7 @@ public class AliasBulkEditor extends Editor>
gridPane.getChildren().add(getApplyMonitorButton());
Label recordLabel = new Label("Record");
+ recordLabel.setLabelFor(getRecordToggleSwitch());
GridPane.setHalignment(recordLabel, HPos.RIGHT);
GridPane.setConstraints(recordLabel, 0, ++row);
gridPane.getChildren().add(recordLabel);
@@ -235,6 +241,7 @@ public class AliasBulkEditor extends Editor>
if(mApplyColorButton == null)
{
mApplyColorButton = new Button("Apply");
+ mApplyColorButton.setAccessibleText("Apply color to selected aliases");
mApplyColorButton.setOnAction(event ->
{
startChange();
@@ -258,6 +265,7 @@ public class AliasBulkEditor extends Editor>
if(mResetColorButton == null)
{
mResetColorButton = new Button("Reset Color");
+ mResetColorButton.setAccessibleText("Reset color for selected aliases");
mResetColorButton.setOnAction(event ->
{
startChange();
@@ -297,6 +305,7 @@ public class AliasBulkEditor extends Editor>
if(mApplyIconButton == null)
{
mApplyIconButton = new Button("Apply");
+ mApplyIconButton.setAccessibleText("Apply icon to selected aliases");
mApplyIconButton.setDisable(true);
mApplyIconButton.setOnAction(new EventHandler()
{
@@ -327,7 +336,7 @@ public class AliasBulkEditor extends Editor>
{
if(mMonitorAudioToggleSwitch == null)
{
- mMonitorAudioToggleSwitch = new ToggleSwitch();
+ mMonitorAudioToggleSwitch = new AccessibleToggleSwitch();
mMonitorAudioToggleSwitch.setSelected(true);
mMonitorAudioToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -340,7 +349,7 @@ public class AliasBulkEditor extends Editor>
{
if(mRecordToggleSwitch == null)
{
- mRecordToggleSwitch = new ToggleSwitch();
+ mRecordToggleSwitch = new AccessibleToggleSwitch();
mRecordToggleSwitch.setSelected(false);
mRecordToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -374,6 +383,7 @@ public class AliasBulkEditor extends Editor>
if(mApplyMonitorButton == null)
{
mApplyMonitorButton = new Button("Apply");
+ mApplyMonitorButton.setAccessibleText("Apply listen and priority to selected aliases");
mApplyMonitorButton.setOnAction(new EventHandler()
{
@Override
@@ -414,6 +424,7 @@ public class AliasBulkEditor extends Editor>
if(mApplyRecordButton == null)
{
mApplyRecordButton = new Button("Apply");
+ mApplyRecordButton.setAccessibleText("Apply record to selected aliases");
mApplyRecordButton.setOnAction(event -> {
startChange();
boolean record = getRecordToggleSwitch().isSelected();
@@ -456,12 +467,15 @@ public class AliasBulkEditor extends Editor>
{
setText(null);
setGraphic(null);
+ setAccessibleText(null);
}
else
{
textLabel.setText(item.getName());
iconLabel.setGraphic(new ImageView(item.getFxImage()));
setGraphic(gridPane);
+ //The name is shown in a nested label, so expose it as the cell's accessible text
+ setAccessibleText(item.getName());
}
}
};
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasConfigurationEditor.java
index 899bde0f..f2e1924b 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasConfigurationEditor.java
@@ -273,7 +273,9 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
Label listLabel = new Label("Alias List");
+ listLabel.setLabelFor(getAliasListNameComboBox());
Label searchLabel = new Label("Search");
+ searchLabel.setLabelFor(getSearchField());
searchLabel.setAlignment(Pos.CENTER_RIGHT);
HBox searchBox = new HBox();
@@ -451,6 +453,7 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
if(mAliasTableView == null)
{
mAliasTableView = new TableView<>();
+ mAliasTableView.setAccessibleText("Aliases");
TableColumn nameColumn = new TableColumn();
nameColumn.setText("Alias");
@@ -751,15 +754,18 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
UIManager.getColor("TextField.background"));
rectangle.setFill(ColorUtil.fromInteger(readable.equals(color) ? alias.getColor() :
readable.getRGB()));
+ setAccessibleText(String.format("#%06X", alias.getColor() & 0xFFFFFF));
}
else
{
rectangle.setVisible(false);
+ setAccessibleText(null);
}
}
else
{
rectangle.setVisible(false);
+ setAccessibleText(null);
}
}
};
@@ -798,13 +804,15 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
{
super.updateItem(item, empty);
- if(!empty && getTableRow() != null)
+ if(!empty && getTableRow() != null && item != null)
{
iconNode.setVisible(item);
+ setAccessibleText(item ? "Yes" : "No");
}
else
{
iconNode.setVisible(false);
+ setAccessibleText(null);
}
}
};
@@ -919,6 +927,7 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
{
super.updateItem(item, empty);
setAlignment(Pos.CENTER);
+ setAccessibleText(null);
if(empty)
{
@@ -937,6 +946,7 @@ public class AliasConfigurationEditor extends SplitPane implements IAliasListRef
if(icon != null && icon.getFxImage() != null)
{
setGraphic(new ImageView(icon.getFxImage()));
+ setAccessibleText(icon.getName());
}
else
{
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasItemEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasItemEditor.java
index 5cd2ceb1..2515e614 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasItemEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasItemEditor.java
@@ -57,6 +57,7 @@ import io.github.dsheirer.alias.id.talkgroup.TalkgroupRange;
import io.github.dsheirer.alias.id.tone.TonesID;
import io.github.dsheirer.audio.broadcast.ConfiguredBroadcast;
import io.github.dsheirer.eventbus.MyEventBus;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerFormatter;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.gui.playlist.alias.action.ActionEditor;
@@ -574,6 +575,7 @@ public class AliasItemEditor extends Editor
{
mActionsList = new ListView<>(FXCollections.observableArrayList(AliasAction.extractor()));
mActionsList.setPrefHeight(75);
+ mActionsList.setAccessibleText("Alias actions");
mActionsList.setDisable(true);
mActionsList.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> setAction(newValue));
@@ -686,6 +688,7 @@ public class AliasItemEditor extends Editor
mIdentifiersList = new ListView<>(FXCollections.observableArrayList(AliasID.extractor()));
mIdentifiersList.setDisable(true);
mIdentifiersList.setPrefHeight(75);
+ mIdentifiersList.setAccessibleText("Alias identifiers");
mIdentifiersList.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> setIdentifier(newValue));
mIdentifiersList.setCellFactory(param -> new AliasIdentifierCell());
@@ -846,13 +849,17 @@ public class AliasItemEditor extends Editor
buttonBox.setSpacing(5);
buttonBox.getChildren().addAll(new Label(" "), getAddStreamButton(), getRemoveStreamButton());
+ Label availableLabel = new Label("Available");
+ getAvailableStreamsView().setAccessibleText("Available streams");
VBox availableBox = new VBox();
VBox.setVgrow(getAvailableStreamsView(), Priority.ALWAYS);
- availableBox.getChildren().addAll(new Label("Available"), getAvailableStreamsView());
+ availableBox.getChildren().addAll(availableLabel, getAvailableStreamsView());
+ Label selectedLabel = new Label("Selected");
+ getSelectedStreamsView().setAccessibleText("Selected streams");
VBox selectedBox = new VBox();
VBox.setVgrow(getSelectedStreamsView(), Priority.ALWAYS);
- selectedBox.getChildren().addAll(new Label("Selected"), getSelectedStreamsView());
+ selectedBox.getChildren().addAll(selectedLabel, getSelectedStreamsView());
HBox hbox = new HBox();
hbox.setSpacing(10);
@@ -864,6 +871,7 @@ public class AliasItemEditor extends Editor
vbox.setSpacing(10);
VBox.setVgrow(hbox, Priority.ALWAYS);
Label label = new Label("Stream this Alias as Talkgroup:");
+ label.setLabelFor(getStreamAsTalkgroupField());
HBox streamAsHBox = new HBox();
streamAsHBox.setAlignment(Pos.CENTER_LEFT);
streamAsHBox.setSpacing(10);
@@ -964,6 +972,8 @@ public class AliasItemEditor extends Editor
.or(Bindings.isNull(getAvailableStreamsView().getSelectionModel().selectedItemProperty())));
mAddStreamButton.setMaxWidth(Double.MAX_VALUE);
mAddStreamButton.setGraphic(new IconNode(FontAwesome.ANGLE_RIGHT));
+ mAddStreamButton.setAccessibleText("Add selected stream");
+ mAddStreamButton.setTooltip(new Tooltip("Add selected stream"));
mAddStreamButton.setAlignment(Pos.CENTER);
mAddStreamButton.setOnAction(event -> {
String stream = getAvailableStreamsView().getSelectionModel().getSelectedItem();
@@ -990,6 +1000,8 @@ public class AliasItemEditor extends Editor
.or(Bindings.isNull(getSelectedStreamsView().getSelectionModel().selectedItemProperty())));
mRemoveStreamButton.setMaxWidth(Double.MAX_VALUE);
mRemoveStreamButton.setGraphic(new IconNode(FontAwesome.ANGLE_LEFT));
+ mRemoveStreamButton.setAccessibleText("Remove selected stream");
+ mRemoveStreamButton.setTooltip(new Tooltip("Remove selected stream"));
mRemoveStreamButton.setAlignment(Pos.CENTER);
mRemoveStreamButton.setOnAction(event -> {
BroadcastChannel broadcastChannel = getSelectedStreamsView().getSelectionModel().getSelectedItem();
@@ -1050,6 +1062,7 @@ public class AliasItemEditor extends Editor
int row = 0;
Label nameLabel = new Label("Alias");
+ nameLabel.setLabelFor(getNameField());
GridPane.setHalignment(nameLabel, HPos.RIGHT);
GridPane.setConstraints(nameLabel, 0, row);
mTextFieldPane.getChildren().add(nameLabel);
@@ -1058,6 +1071,7 @@ public class AliasItemEditor extends Editor
mTextFieldPane.getChildren().add(getNameField());
Label monitorAudioLabel = new Label("Listen");
+ monitorAudioLabel.setLabelFor(getMonitorAudioToggleSwitch());
GridPane.setHalignment(monitorAudioLabel, HPos.RIGHT);
GridPane.setConstraints(monitorAudioLabel, 2, row);
mTextFieldPane.getChildren().add(monitorAudioLabel);
@@ -1065,6 +1079,7 @@ public class AliasItemEditor extends Editor
mTextFieldPane.getChildren().add(getMonitorAudioToggleSwitch());
Label monitorPriorityLabel = new Label("Priority");
+ monitorPriorityLabel.setLabelFor(getMonitorPriorityComboBox());
GridPane.setHalignment(monitorPriorityLabel, HPos.RIGHT);
GridPane.setConstraints(monitorPriorityLabel, 4, row);
mTextFieldPane.getChildren().add(monitorPriorityLabel);
@@ -1072,6 +1087,7 @@ public class AliasItemEditor extends Editor
mTextFieldPane.getChildren().add(getMonitorPriorityComboBox());
Label colorLabel = new Label("Color");
+ colorLabel.setLabelFor(getColorPicker());
GridPane.setHalignment(colorLabel, HPos.RIGHT);
GridPane.setConstraints(colorLabel, 6, row);
mTextFieldPane.getChildren().add(colorLabel);
@@ -1079,6 +1095,7 @@ public class AliasItemEditor extends Editor
mTextFieldPane.getChildren().add(getColorPicker());
Label groupLabel = new Label("Group");
+ groupLabel.setLabelFor(getGroupField());
GridPane.setHalignment(groupLabel, HPos.RIGHT);
GridPane.setConstraints(groupLabel, 0, ++row);
mTextFieldPane.getChildren().add(groupLabel);
@@ -1087,6 +1104,7 @@ public class AliasItemEditor extends Editor
mTextFieldPane.getChildren().add(getGroupField());
Label recordAudioLabel = new Label("Record");
+ recordAudioLabel.setLabelFor(getRecordAudioToggleSwitch());
GridPane.setHalignment(recordAudioLabel, HPos.RIGHT);
GridPane.setConstraints(recordAudioLabel, 2, row);
mTextFieldPane.getChildren().add(recordAudioLabel);
@@ -1094,6 +1112,7 @@ public class AliasItemEditor extends Editor
mTextFieldPane.getChildren().add(getRecordAudioToggleSwitch());
Label iconLabel = new Label("Icon");
+ iconLabel.setLabelFor(getIconNodeComboBox());
GridPane.setHalignment(iconLabel, HPos.RIGHT);
GridPane.setConstraints(iconLabel, 4, row);
mTextFieldPane.getChildren().add(iconLabel);
@@ -1108,7 +1127,7 @@ public class AliasItemEditor extends Editor
{
if(mMonitorAudioToggleSwitch == null)
{
- mMonitorAudioToggleSwitch = new ToggleSwitch();
+ mMonitorAudioToggleSwitch = new AccessibleToggleSwitch();
mMonitorAudioToggleSwitch.setDisable(true);
mMonitorAudioToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -1141,7 +1160,7 @@ public class AliasItemEditor extends Editor
{
if(mRecordAudioToggleSwitch == null)
{
- mRecordAudioToggleSwitch = new ToggleSwitch();
+ mRecordAudioToggleSwitch = new AccessibleToggleSwitch();
mRecordAudioToggleSwitch.setDisable(true);
mRecordAudioToggleSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -1820,12 +1839,15 @@ public class AliasItemEditor extends Editor
{
setText(null);
setGraphic(null);
+ setAccessibleText(null);
}
else
{
textLabel.setText(item.getName());
iconLabel.setGraphic(new ImageView(item.getFxImage()));
setGraphic(gridPane);
+ //The name is shown in a nested label, so expose it as the cell's accessible text
+ setAccessibleText(item.getName());
}
}
};
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByIdentifierEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByIdentifierEditor.java
index 04d7553e..46adf598 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByIdentifierEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByIdentifierEditor.java
@@ -97,6 +97,7 @@ public class AliasViewByIdentifierEditor extends VBox
gridPane.setHgap(10);
Label aliasListLabel = new Label("Alias List");
+ aliasListLabel.setLabelFor(getAliasListNameComboBox());
GridPane.setHalignment(aliasListLabel, HPos.RIGHT);
GridPane.setConstraints(aliasListLabel, 0, 0);
gridPane.getChildren().add(aliasListLabel);
@@ -106,6 +107,7 @@ public class AliasViewByIdentifierEditor extends VBox
gridPane.getChildren().add(getAliasListNameComboBox());
Label identifierLabel = new Label("Identifier Type");
+ identifierLabel.setLabelFor(getAliasIDTypeComboBox());
GridPane.setHalignment(identifierLabel, HPos.RIGHT);
GridPane.setConstraints(identifierLabel, 0, 1);
gridPane.getChildren().add(identifierLabel);
@@ -210,6 +212,7 @@ public class AliasViewByIdentifierEditor extends VBox
if(mAliasAndIdentifierTableView == null)
{
mAliasAndIdentifierTableView = new TableView<>(FXCollections.observableArrayList(AliasAndIdentifier.extractor()));
+ mAliasAndIdentifierTableView.setAccessibleText("Aliases by identifier");
mAliasAndIdentifierTableView.setPlaceholder(new Label("No aliases or identifiers available"));
mIdentifierColumn = new TableColumn<>("Identifier");
mIdentifierColumn.setCellValueFactory(param -> new ReadOnlyObjectWrapper<>(param.getValue().getAliasIdentifier()));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByRecordingEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByRecordingEditor.java
index fd1dce41..a7ce7c93 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByRecordingEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/AliasViewByRecordingEditor.java
@@ -34,6 +34,7 @@ import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
+import javafx.scene.control.Tooltip;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
@@ -157,12 +158,14 @@ public class AliasViewByRecordingEditor extends VBox
aliasListBox.setAlignment(Pos.CENTER);
aliasListBox.setSpacing(5);
Label aliasListLabel = new Label("Alias List");
+ aliasListLabel.setLabelFor(getAliasListNameComboBox());
aliasListBox.getChildren().addAll(aliasListLabel, getAliasListNameComboBox());
HBox searchBox = new HBox();
searchBox.setAlignment(Pos.CENTER);
searchBox.setSpacing(5);
Label searchLabel = new Label("Search");
+ searchLabel.setLabelFor(getSearchField());
searchBox.getChildren().addAll(searchLabel, getSearchField());
mTopBox.getChildren().addAll(aliasListBox, searchBox);
@@ -228,6 +231,7 @@ public class AliasViewByRecordingEditor extends VBox
if(mNoRecordAliasTableView == null)
{
mNoRecordAliasTableView = new TableView<>();
+ mNoRecordAliasTableView.setAccessibleText("Don't record audio aliases");
mNoRecordAliasTableView.setMaxHeight(Double.MAX_VALUE);
mNoRecordAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
@@ -266,6 +270,7 @@ public class AliasViewByRecordingEditor extends VBox
if(mRecordAliasTableView == null)
{
mRecordAliasTableView = new TableView<>();
+ mRecordAliasTableView.setAccessibleText("Record audio aliases");
mRecordAliasTableView.setMaxHeight(Double.MAX_VALUE);
mRecordAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
@@ -307,6 +312,8 @@ public class AliasViewByRecordingEditor extends VBox
mAddButton.setDisable(true);
mAddButton.setMaxWidth(Double.MAX_VALUE);
mAddButton.setGraphic(new IconNode(FontAwesome.ANGLE_RIGHT));
+ mAddButton.setAccessibleText("Record audio for selected alias");
+ mAddButton.setTooltip(new Tooltip("Record audio for selected alias"));
mAddButton.setAlignment(Pos.CENTER);
mAddButton.setOnAction(event -> {
Alias selectedAlias = getNoRecordAliasTableView().getSelectionModel().getSelectedItem();
@@ -330,6 +337,8 @@ public class AliasViewByRecordingEditor extends VBox
mAddAllButton.setDisable(true);
mAddAllButton.setMaxWidth(Double.MAX_VALUE);
mAddAllButton.setGraphic(new IconNode(FontAwesome.ANGLE_DOUBLE_RIGHT));
+ mAddAllButton.setAccessibleText("Record audio for all selected aliases");
+ mAddAllButton.setTooltip(new Tooltip("Record audio for all selected aliases"));
mAddAllButton.setAlignment(Pos.CENTER);
mAddAllButton.setOnAction(event -> {
List selectedAliases =
@@ -358,6 +367,8 @@ public class AliasViewByRecordingEditor extends VBox
mRemoveButton.setDisable(true);
mRemoveButton.setMaxWidth(Double.MAX_VALUE);
mRemoveButton.setGraphic(new IconNode(FontAwesome.ANGLE_LEFT));
+ mRemoveButton.setAccessibleText("Don't record audio for selected alias");
+ mRemoveButton.setTooltip(new Tooltip("Don't record audio for selected alias"));
mRemoveButton.setAlignment(Pos.CENTER);
mRemoveButton.setOnAction(event -> {
Alias selectedAlias = getRecordAliasTableView().getSelectionModel().getSelectedItem();
@@ -381,6 +392,8 @@ public class AliasViewByRecordingEditor extends VBox
mRemoveAllButton.setDisable(true);
mRemoveAllButton.setMaxWidth(Double.MAX_VALUE);
mRemoveAllButton.setGraphic(new IconNode(FontAwesome.ANGLE_DOUBLE_LEFT));
+ mRemoveAllButton.setAccessibleText("Don't record audio for all selected aliases");
+ mRemoveAllButton.setTooltip(new Tooltip("Don't record audio for all selected aliases"));
mRemoveAllButton.setAlignment(Pos.CENTER);
mRemoveAllButton.setOnAction(event -> {
List selectedAliases =
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/action/BeepEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/action/BeepEditor.java
index def7a7d8..2557bc83 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/action/BeepEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/action/BeepEditor.java
@@ -66,6 +66,7 @@ public class BeepEditor extends ActionEditor
int row = 0;
Label intervalLabel = new Label(("Beep"));
+ intervalLabel.setLabelFor(getIntervalComboBox());
GridPane.setHalignment(intervalLabel, HPos.RIGHT);
GridPane.setConstraints(intervalLabel, 0, row);
gridPane.getChildren().add(intervalLabel);
@@ -80,6 +81,7 @@ public class BeepEditor extends ActionEditor
GridPane.setConstraints(getSecondsLabel(), 3, row);
GridPane.setHgrow(getSecondsLabel(), Priority.ALWAYS);
gridPane.getChildren().add(getSecondsLabel());
+ getSecondsLabel().setLabelFor(getPeriodTextField());
GridPane.setConstraints(getTestButton(), 4, row);
gridPane.getChildren().add(getTestButton());
@@ -155,6 +157,7 @@ public class BeepEditor extends ActionEditor
if(mTestButton == null)
{
mTestButton = new Button("Test");
+ mTestButton.setAccessibleText("Test beep");
mTestButton.setMaxWidth(Double.MAX_VALUE);
mTestButton.setOnAction(event -> test());
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/action/ClipEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/action/ClipEditor.java
index e88452ce..dd9120a0 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/action/ClipEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/action/ClipEditor.java
@@ -71,6 +71,7 @@ public class ClipEditor extends ActionEditor
int row = 0;
Label fileLabel = new Label("Audio File");
+ fileLabel.setLabelFor(getFilePath());
GridPane.setHalignment(fileLabel, HPos.RIGHT);
GridPane.setConstraints(fileLabel, 0, row);
gridPane.getChildren().add(fileLabel);
@@ -83,6 +84,7 @@ public class ClipEditor extends ActionEditor
gridPane.getChildren().add(getFileChooser());
Label intervalLabel = new Label(("Play Audio Clip"));
+ intervalLabel.setLabelFor(getIntervalComboBox());
GridPane.setHalignment(intervalLabel, HPos.RIGHT);
GridPane.setConstraints(intervalLabel, 0, ++row);
gridPane.getChildren().add(intervalLabel);
@@ -97,6 +99,7 @@ public class ClipEditor extends ActionEditor
GridPane.setConstraints(getSecondsLabel(), 3, row);
GridPane.setHgrow(getSecondsLabel(), Priority.ALWAYS);
gridPane.getChildren().add(getSecondsLabel());
+ getSecondsLabel().setLabelFor(getPeriodTextField());
GridPane.setConstraints(getTestButton(), 4, row);
gridPane.getChildren().add(getTestButton());
@@ -223,6 +226,7 @@ public class ClipEditor extends ActionEditor
if(mTestButton == null)
{
mTestButton = new Button("Test");
+ mTestButton.setAccessibleText("Test audio clip");
mTestButton.setMaxWidth(Double.MAX_VALUE);
mTestButton.setDisable(true);
mTestButton.setOnAction(event -> test());
@@ -236,6 +240,7 @@ public class ClipEditor extends ActionEditor
if(mFileChooser == null)
{
mFileChooser = new Button(("Select ..."));
+ mFileChooser.setAccessibleText("Select audio file");
mFileChooser.setMaxWidth(Double.MAX_VALUE);
mFileChooser.setDisable(true);
mFileChooser.setOnAction(event -> {
@@ -278,7 +283,8 @@ public class ClipEditor extends ActionEditor
if(mFilePath == null)
{
mFilePath = new TextField(PLEASE_SELECT_A_FILE);
- mFilePath.setDisable(true);
+ //Read-only rather than disabled so that the selected file remains focusable for screen readers
+ mFilePath.setEditable(false);
mFilePath.setMaxWidth(Double.MAX_VALUE);
mFilePath.textProperty().addListener((observable, oldValue, newValue) -> {
if(newValue != null && !newValue.isEmpty() && !newValue.contentEquals(PLEASE_SELECT_A_FILE))
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/action/ScriptEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/action/ScriptEditor.java
index d5efc61b..7a07760e 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/action/ScriptEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/action/ScriptEditor.java
@@ -71,6 +71,7 @@ public class ScriptEditor extends ActionEditor
int row = 0;
Label fileLabel = new Label("Script File");
+ fileLabel.setLabelFor(getFilePath());
GridPane.setHalignment(fileLabel, HPos.RIGHT);
GridPane.setConstraints(fileLabel, 0, row);
gridPane.getChildren().add(fileLabel);
@@ -83,6 +84,7 @@ public class ScriptEditor extends ActionEditor
gridPane.getChildren().add(getFileChooser());
Label intervalLabel = new Label(("Run Script"));
+ intervalLabel.setLabelFor(getIntervalComboBox());
GridPane.setHalignment(intervalLabel, HPos.RIGHT);
GridPane.setConstraints(intervalLabel, 0, ++row);
gridPane.getChildren().add(intervalLabel);
@@ -97,6 +99,7 @@ public class ScriptEditor extends ActionEditor
GridPane.setConstraints(getSecondsLabel(), 3, row);
GridPane.setHgrow(getSecondsLabel(), Priority.ALWAYS);
gridPane.getChildren().add(getSecondsLabel());
+ getSecondsLabel().setLabelFor(getPeriodTextField());
GridPane.setConstraints(getTestButton(), 4, row);
gridPane.getChildren().add(getTestButton());
@@ -223,6 +226,7 @@ public class ScriptEditor extends ActionEditor
if(mTestButton == null)
{
mTestButton = new Button("Test");
+ mTestButton.setAccessibleText("Test script");
mTestButton.setMaxWidth(Double.MAX_VALUE);
mTestButton.setDisable(true);
mTestButton.setOnAction(event -> test());
@@ -236,6 +240,7 @@ public class ScriptEditor extends ActionEditor
if(mFileChooser == null)
{
mFileChooser = new Button(("Select ..."));
+ mFileChooser.setAccessibleText("Select script file");
mFileChooser.setMaxWidth(Double.MAX_VALUE);
mFileChooser.setDisable(true);
mFileChooser.setOnAction(event -> {
@@ -278,7 +283,8 @@ public class ScriptEditor extends ActionEditor
if(mFilePath == null)
{
mFilePath = new TextField(PLEASE_SELECT_A_FILE);
- mFilePath.setDisable(true);
+ //Read-only rather than disabled so that the selected file remains focusable for screen readers
+ mFilePath.setEditable(false);
mFilePath.setMaxWidth(Double.MAX_VALUE);
mFilePath.textProperty().addListener((observable, oldValue, newValue) -> {
if(newValue != null && !newValue.isEmpty() && !newValue.contentEquals(PLEASE_SELECT_A_FILE))
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/DcsEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/DcsEditor.java
index 48a0e10a..70167150 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/DcsEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/DcsEditor.java
@@ -44,6 +44,7 @@ public class DcsEditor extends IdentifierEditor
gridPane.setHgap(5);
Label typeLabel = new Label("DCS Code");
+ typeLabel.setLabelFor(getDCSCodeComboBox());
GridPane.setConstraints(typeLabel, 0, 0);
gridPane.getChildren().add(typeLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/EsnEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/EsnEditor.java
index e5487a75..baf77a9f 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/EsnEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/EsnEditor.java
@@ -54,6 +54,7 @@ public class EsnEditor extends IdentifierEditor
gridPane.setHgap(5);
Label typeLabel = new Label("ESN");
+ typeLabel.setLabelFor(getEsnField());
GridPane.setConstraints(typeLabel, 0, 0);
gridPane.getChildren().add(typeLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/LojackEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/LojackEditor.java
index 22f0c57f..e0df534d 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/LojackEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/LojackEditor.java
@@ -47,6 +47,7 @@ public class LojackEditor extends IdentifierEditor
gridPane.setHgap(5);
Label typeLabel = new Label("LoJack Function");
+ typeLabel.setLabelFor(getFunctionComboBox());
GridPane.setConstraints(typeLabel, 0, 0);
gridPane.getChildren().add(typeLabel);
@@ -57,6 +58,10 @@ public class LojackEditor extends IdentifierEditor
gridPane.getChildren().add(getIdentifierField());
Label helpLabel = new Label("Format: 5 numbers, characters or (*)wildcard - e.g. AB*12");
+ //The identifier field has no label of its own, so the help label provides its accessible name and help text
+ helpLabel.setLabelFor(getIdentifierField());
+ helpLabel.setAccessibleText("LoJack ID");
+ getIdentifierField().setAccessibleHelp(helpLabel.getText());
GridPane.setConstraints(helpLabel, 3, 0);
gridPane.getChildren().add(helpLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/NXDNTypeDEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/NXDNTypeDEditor.java
index fb5375af..e4a44245 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/NXDNTypeDEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/NXDNTypeDEditor.java
@@ -76,11 +76,13 @@ public class NXDNTypeDEditor extends IdentifierE
gridPane.add(new Label("NXDN Type-D"), 0, 0);
Label homeRepeaterLabel = new Label("Home Repeater");
+ homeRepeaterLabel.setLabelFor(mHomeRepeaterField);
GridPane.setHalignment(homeRepeaterLabel, HPos.RIGHT);
gridPane.add(homeRepeaterLabel, 1, 0);
gridPane.add(mHomeRepeaterField, 2, 0);
Label identifierFieldLabel = new Label(identifierLabel);
+ identifierFieldLabel.setLabelFor(mIdentifierField);
GridPane.setHalignment(identifierFieldLabel, HPos.RIGHT);
gridPane.add(identifierFieldLabel, 3, 0);
gridPane.add(mIdentifierField, 4, 0);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/NXDNTypeDRangeEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/NXDNTypeDRangeEditor.java
index f9a77256..c881bdeb 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/NXDNTypeDRangeEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/NXDNTypeDRangeEditor.java
@@ -89,15 +89,21 @@ public class NXDNTypeDRangeEditor extends I
gridPane.add(new Label("NXDN Type-D"), 0, 0);
Label homeRepeaterLabel = new Label("Home Repeater");
+ homeRepeaterLabel.setLabelFor(mHomeRepeaterField);
GridPane.setHalignment(homeRepeaterLabel, HPos.RIGHT);
gridPane.add(homeRepeaterLabel, 1, 0);
gridPane.add(mHomeRepeaterField, 2, 0);
Label rangeFieldLabel = new Label(rangeLabel);
+ rangeFieldLabel.setLabelFor(mMinField);
+ rangeFieldLabel.setAccessibleText(rangeLabel + " start");
GridPane.setHalignment(rangeFieldLabel, HPos.RIGHT);
gridPane.add(rangeFieldLabel, 3, 0);
gridPane.add(mMinField, 4, 0);
- gridPane.add(new Label("-"), 5, 0);
+ Label dashLabel = new Label("-");
+ dashLabel.setLabelFor(mMaxField);
+ dashLabel.setAccessibleText(rangeLabel + " end");
+ gridPane.add(dashLabel, 5, 0);
gridPane.add(mMaxField, 6, 0);
getChildren().add(gridPane);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/P25FullyQualifiedRadioIdEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/P25FullyQualifiedRadioIdEditor.java
index ae80f177..6557f7f2 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/P25FullyQualifiedRadioIdEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/P25FullyQualifiedRadioIdEditor.java
@@ -70,6 +70,7 @@ public class P25FullyQualifiedRadioIdEditor extends IdentifierEditor
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("Radio ID");
+ valueLabel.setLabelFor(getRadioIdField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/RadioIdRangeEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/RadioIdRangeEditor.java
index 17c3388d..2a45174a 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/RadioIdRangeEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/RadioIdRangeEditor.java
@@ -73,6 +73,8 @@ public class RadioIdRangeEditor extends IdentifierEditor
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("Radio ID Range");
+ valueLabel.setLabelFor(getMinRadioIdField());
+ valueLabel.setAccessibleText("Radio ID Range start");
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
@@ -81,6 +83,8 @@ public class RadioIdRangeEditor extends IdentifierEditor
gridPane.getChildren().add(getMinRadioIdField());
Label dashLabel = new Label("-");
+ dashLabel.setLabelFor(getMaxRadioIdField());
+ dashLabel.setAccessibleText("Radio ID Range end");
GridPane.setConstraints(dashLabel, 3, 0);
gridPane.getChildren().add(dashLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupEditor.java
index 6825167e..80aa39e0 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupEditor.java
@@ -73,6 +73,7 @@ public class TalkgroupEditor extends IdentifierEditor
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("Talkgroup");
+ valueLabel.setLabelFor(getTalkgroupField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupRangeEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupRangeEditor.java
index 9e6e4ec1..024fb001 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupRangeEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TalkgroupRangeEditor.java
@@ -75,6 +75,8 @@ public class TalkgroupRangeEditor extends IdentifierEditor
gridPane.getChildren().add(getProtocolLabel());
Label valueLabel = new Label("Talkgroup Range");
+ valueLabel.setLabelFor(getMinTalkgroupField());
+ valueLabel.setAccessibleText("Talkgroup Range start");
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 1, 0);
gridPane.getChildren().add(valueLabel);
@@ -83,6 +85,8 @@ public class TalkgroupRangeEditor extends IdentifierEditor
gridPane.getChildren().add(getMinTalkgroupField());
Label dashLabel = new Label("-");
+ dashLabel.setLabelFor(getMaxTalkgroupField());
+ dashLabel.setAccessibleText("Talkgroup Range end");
GridPane.setConstraints(dashLabel, 3, 0);
gridPane.getChildren().add(dashLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TonesEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TonesEditor.java
index 8ebb0107..60b1de9c 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TonesEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/TonesEditor.java
@@ -73,6 +73,7 @@ public class TonesEditor extends IdentifierEditor
editorsPane.setVgap(10);
Label toneLabel = new Label("Tone");
+ toneLabel.setLabelFor(getAmbeToneComboBox());
GridPane.setConstraints(toneLabel, 0, 0);
GridPane.setHalignment(toneLabel, HPos.RIGHT);
editorsPane.getChildren().add(toneLabel);
@@ -81,6 +82,7 @@ public class TonesEditor extends IdentifierEditor
editorsPane.getChildren().add(getAmbeToneComboBox());
Label durationLabel = new Label("Duration 1-50");
+ durationLabel.setLabelFor(getDurationTextField());
GridPane.setConstraints(durationLabel, 0, 1);
GridPane.setHalignment(durationLabel, HPos.RIGHT);
editorsPane.getChildren().add(durationLabel);
@@ -222,6 +224,7 @@ public class TonesEditor extends IdentifierEditor
iconNode.setIconSize(12);
iconNode.setFill(getMoveUpButton().getTextFill());
mMoveUpButton.setGraphic(iconNode);
+ mMoveUpButton.setAccessibleText("Move tone up");
mMoveUpButton.setMaxWidth(Double.MAX_VALUE);
mMoveUpButton.setDisable(true);
mMoveUpButton.setOnAction(event -> {
@@ -249,6 +252,7 @@ public class TonesEditor extends IdentifierEditor
iconNode.setIconSize(12);
iconNode.setFill(getMoveDownButton().getTextFill());
mMoveDownButton.setGraphic(iconNode);
+ mMoveDownButton.setAccessibleText("Move tone down");
mMoveDownButton.setMaxWidth(Double.MAX_VALUE);
mMoveDownButton.setDisable(true);
mMoveDownButton.setOnAction(event -> {
@@ -274,6 +278,7 @@ public class TonesEditor extends IdentifierEditor
mToneListView = new ListView<>();
mToneListView.setDisable(true);
mToneListView.setPrefHeight(75);
+ mToneListView.setAccessibleText("Tone sequence");
mToneListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
setTone(newValue);
});
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnitStatusEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnitStatusEditor.java
index f5526c3f..e109638e 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnitStatusEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UnitStatusEditor.java
@@ -58,6 +58,7 @@ public class UnitStatusEditor extends IdentifierEditor
gridPane.setVgap(3);
Label valueLabel = new Label("Unit Status");
+ valueLabel.setLabelFor(getUnitStatusField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 0, 0);
gridPane.getChildren().add(valueLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UserStatusEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UserStatusEditor.java
index 41ac75a1..a06a412a 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UserStatusEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/alias/identifier/UserStatusEditor.java
@@ -51,6 +51,7 @@ public class UserStatusEditor extends IdentifierEditor
gridPane.setVgap(3);
Label valueLabel = new Label("User Status");
+ valueLabel.setLabelFor(getUserStatusField());
GridPane.setHalignment(valueLabel, HPos.RIGHT);
GridPane.setConstraints(valueLabel, 0, 0);
gridPane.getChildren().add(valueLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java
index 3801cacc..a707142c 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/AMConfigurationEditor.java
@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.playlist.channel;
+import io.github.dsheirer.gui.control.AccessibilityUtil;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.DbPowerMeter;
import io.github.dsheirer.gui.control.HexFormatter;
import io.github.dsheirer.gui.control.IntegerFormatter;
@@ -134,6 +136,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label bandwidthLabel = new Label("Channel Bandwidth");
+ AccessibilityUtil.labelGroup(bandwidthLabel, getBandwidthButton());
GridPane.setHalignment(bandwidthLabel, HPos.RIGHT);
GridPane.setConstraints(bandwidthLabel, 0, row);
gridPane.getChildren().add(bandwidthLabel);
@@ -142,6 +145,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getBandwidthButton());
Label squelchLabel = new Label("Squelch Threshold");
+ squelchLabel.setLabelFor(getSquelchThresholdField());
GridPane.setHalignment(squelchLabel, HPos.RIGHT);
GridPane.setConstraints(squelchLabel, 0, row);
gridPane.getChildren().add(squelchLabel);
@@ -150,6 +154,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getSquelchThresholdField());
Label autoTrackLabel = new Label("Squelch Auto-Track");
+ autoTrackLabel.setLabelFor(getSquelchAutoTrackSwitch());
GridPane.setHalignment(autoTrackLabel, HPos.RIGHT);
GridPane.setConstraints(autoTrackLabel, 2, row);
gridPane.getChildren().add(autoTrackLabel);
@@ -159,6 +164,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getSquelchAutoTrackSwitch());
Label talkgroupLabel = new Label("Talkgroup To Assign");
+ talkgroupLabel.setLabelFor(getTalkgroupField());
GridPane.setHalignment(talkgroupLabel, HPos.RIGHT);
GridPane.setConstraints(talkgroupLabel, 4, row);
gridPane.getChildren().add(talkgroupLabel);
@@ -210,6 +216,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getBasebandRecordSwitch());
Label recordBasebandLabel = new Label("Channel (Baseband I&Q)");
+ recordBasebandLabel.setLabelFor(getBasebandRecordSwitch());
GridPane.setHalignment(recordBasebandLabel, HPos.LEFT);
GridPane.setConstraints(recordBasebandLabel, 1, 1);
gridPane.getChildren().add(recordBasebandLabel);
@@ -254,7 +261,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
{
if(mBasebandRecordSwitch == null)
{
- mBasebandRecordSwitch = new ToggleSwitch();
+ mBasebandRecordSwitch = new AccessibleToggleSwitch();
mBasebandRecordSwitch.setDisable(true);
mBasebandRecordSwitch.setTextAlignment(TextAlignment.RIGHT);
mBasebandRecordSwitch.selectedProperty()
@@ -348,7 +355,7 @@ public class AMConfigurationEditor extends ChannelConfigurationEditor
{
if(mSquelchAutoTrackSwitch == null)
{
- mSquelchAutoTrackSwitch = new ToggleSwitch();
+ mSquelchAutoTrackSwitch = new AccessibleToggleSwitch();
mSquelchAutoTrackSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java
index f37ffa91..f3dc7a95 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelConfigurationEditor.java
@@ -23,6 +23,7 @@ import io.github.dsheirer.alias.AliasModel;
import io.github.dsheirer.controller.channel.Channel;
import io.github.dsheirer.controller.channel.ChannelException;
import io.github.dsheirer.eventbus.MyEventBus;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.MaxLengthUnaryOperator;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.gui.preference.PreferenceEditorType;
@@ -464,7 +465,7 @@ public abstract class ChannelConfigurationEditor extends Editor
{
if(mAutoStartSwitch == null)
{
- mAutoStartSwitch = new ToggleSwitch();
+ mAutoStartSwitch = new AccessibleToggleSwitch();
mAutoStartSwitch.setDisable(true);
mAutoStartSwitch.setDisable(true);
mAutoStartSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -533,6 +534,7 @@ public abstract class ChannelConfigurationEditor extends Editor
int row = 0;
Label systemLabel = new Label("System");
+ systemLabel.setLabelFor(getSystemField());
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 0, row);
mTextFieldPane.getChildren().add(systemLabel);
@@ -542,6 +544,7 @@ public abstract class ChannelConfigurationEditor extends Editor
mTextFieldPane.getChildren().add(getSystemField());
Label autoStartLabel = new Label("Auto-Start");
+ autoStartLabel.setLabelFor(getAutoStartSwitch());
GridPane.setHalignment(autoStartLabel, HPos.RIGHT);
GridPane.setConstraints(autoStartLabel, 2, row);
mTextFieldPane.getChildren().add(autoStartLabel);
@@ -554,6 +557,7 @@ public abstract class ChannelConfigurationEditor extends Editor
mTextFieldPane.getChildren().add(getPlayButton());
Label siteLabel = new Label("Site");
+ siteLabel.setLabelFor(getSiteField());
GridPane.setHalignment(siteLabel, HPos.RIGHT);
GridPane.setConstraints(siteLabel, 0, ++row);
mTextFieldPane.getChildren().add(siteLabel);
@@ -563,6 +567,7 @@ public abstract class ChannelConfigurationEditor extends Editor
mTextFieldPane.getChildren().add(getSiteField());
Label autoStartOrderLabel = new Label("Start Order");
+ autoStartOrderLabel.setLabelFor(getAutoStartOrderSpinner());
GridPane.setHalignment(autoStartOrderLabel, HPos.RIGHT);
GridPane.setConstraints(autoStartOrderLabel, 2, row);
mTextFieldPane.getChildren().add(autoStartOrderLabel);
@@ -572,6 +577,7 @@ public abstract class ChannelConfigurationEditor extends Editor
mTextFieldPane.getChildren().add(getAutoStartOrderSpinner());
Label nameLabel = new Label("Name");
+ nameLabel.setLabelFor(getNameField());
GridPane.setHalignment(nameLabel, HPos.RIGHT);
GridPane.setConstraints(nameLabel, 0, ++row);
mTextFieldPane.getChildren().add(nameLabel);
@@ -581,6 +587,7 @@ public abstract class ChannelConfigurationEditor extends Editor
mTextFieldPane.getChildren().add(getNameField());
Label aliasListLabel = new Label("Alias List");
+ aliasListLabel.setLabelFor(getAliasListComboBox());
GridPane.setHalignment(aliasListLabel, HPos.RIGHT);
GridPane.setConstraints(aliasListLabel, 2, row);
mTextFieldPane.getChildren().add(aliasListLabel);
@@ -708,20 +715,25 @@ public abstract class ChannelConfigurationEditor extends Editor
audioPane.add(new Label(title), 0, 0);
Label noiseGainLabel = new Label("Comfort Noise Volume:");
+ noiseGainLabel.setLabelFor(noiseGainSlider);
GridPane.setHalignment(noiseGainLabel, HPos.RIGHT);
audioPane.add(noiseGainLabel, 1, 0);
- audioPane.add(createSliderWithReset(noiseGainSlider, DecodeConfigJMBE.DEFAULT_COMFORT_NOISE_GAIN), 2, 0);
+ audioPane.add(createSliderWithReset(noiseGainSlider, DecodeConfigJMBE.DEFAULT_COMFORT_NOISE_GAIN,
+ "Reset comfort noise volume"), 2, 0);
Label agcLabel = new Label("AGC:");
+ agcLabel.setLabelFor(agcSwitch);
agcLabel.setTooltip(agcSwitch.getTooltip());
GridPane.setHalignment(agcLabel, HPos.RIGHT);
audioPane.add(agcLabel, 3, 0);
audioPane.add(agcSwitch, 4, 0);
Label toneGainLabel = new Label("Tone Volume:");
+ toneGainLabel.setLabelFor(toneGainSlider);
GridPane.setHalignment(toneGainLabel, HPos.RIGHT);
audioPane.add(toneGainLabel, 1, 1);
- audioPane.add(createSliderWithReset(toneGainSlider, DecodeConfigJMBE.DEFAULT_TONE_GAIN), 2, 1);
+ audioPane.add(createSliderWithReset(toneGainSlider, DecodeConfigJMBE.DEFAULT_TONE_GAIN, "Reset tone volume"), 2, 1);
Label legacyToneLevelsLabel = new Label("Use Legacy Tone Levels:");
+ legacyToneLevelsLabel.setLabelFor(legacyToneLevelsSwitch);
legacyToneLevelsLabel.setTooltip(legacyToneLevelsSwitch.getTooltip());
GridPane.setHalignment(legacyToneLevelsLabel, HPos.RIGHT);
audioPane.add(legacyToneLevelsLabel, 3, 1);
@@ -739,9 +751,10 @@ public abstract class ChannelConfigurationEditor extends Editor
* current slider value and a small reset button. The label and button track the disabled state of the slider.
* @param slider to wrap
* @param defaultValue to apply to the slider when the reset button is clicked
+ * @param resetName accessible name for the reset button, announced by screen readers
* @return box containing the slider, the value label and the reset button
*/
- private HBox createSliderWithReset(Slider slider, double defaultValue)
+ private HBox createSliderWithReset(Slider slider, double defaultValue, String resetName)
{
IconNode resetIcon = new IconNode(FontAwesome.UNDO);
resetIcon.setIconSize(10);
@@ -750,6 +763,7 @@ public abstract class ChannelConfigurationEditor extends Editor
resetIcon.fillProperty().bind(resetButton.textFillProperty());
resetButton.setGraphic(resetIcon);
resetButton.setTooltip(new Tooltip("Reset to default value"));
+ resetButton.setAccessibleText(resetName);
resetButton.disableProperty().bind(slider.disableProperty());
resetButton.setOnAction(event -> slider.setValue(defaultValue));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java
index 961ce7dc..ad7b3e23 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/ChannelEditor.java
@@ -21,6 +21,7 @@ package io.github.dsheirer.gui.playlist.channel;
import com.google.common.base.Joiner;
import io.github.dsheirer.controller.channel.Channel;
+import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.playlist.IAliasListRefreshListener;
import io.github.dsheirer.module.decode.DecoderFactory;
import io.github.dsheirer.module.decode.DecoderType;
@@ -336,6 +337,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
searchBox.setSpacing(5);
searchBox.setAlignment(Pos.CENTER);
Label searchLabel = new Label("Search:");
+ searchLabel.setLabelFor(getSearchField());
searchLabel.setAlignment(Pos.CENTER_RIGHT);
searchBox.getChildren().addAll(searchLabel, getSearchField());
@@ -343,6 +345,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
viewBox.setSpacing(5);
viewBox.setAlignment(Pos.CENTER);
Label viewLabel = new Label("View Channels:");
+ AccessibilityUtil.labelGroup(viewLabel, getViewSegmentedButton());
viewBox.getChildren().addAll(viewLabel, getViewSegmentedButton());
mSearchAndViewBox.getChildren().addAll(searchBox, viewBox);
@@ -409,6 +412,7 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
if(mChannelTableView == null)
{
mChannelTableView = new TableView<>();
+ mChannelTableView.setAccessibleText("Channels");
TableColumn playingColumn = new TableColumn("Playing");
playingColumn.setPrefWidth(75);
@@ -425,12 +429,14 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
if(empty || item == null || !item)
{
setGraphic(null);
+ setAccessibleText(empty ? null : "No");
}
else
{
IconNode iconNode = new IconNode(FontAwesome.CHECK);
iconNode.setFill(Color.GREEN);
setGraphic(iconNode);
+ setAccessibleText("Yes");
}
}
};
@@ -453,12 +459,14 @@ public class ChannelEditor extends SplitPane implements IFilterProcessor, IAlias
if(empty || item == null || !item)
{
setGraphic(null);
+ setAccessibleText(empty ? null : "No");
}
else
{
IconNode iconNode = new IconNode(FontAwesome.CHECK);
iconNode.setFill(Color.GREEN);
setGraphic(iconNode);
+ setAccessibleText("Yes");
}
}
};
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java
index 94ba3634..1a7f8415 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/DMRConfigurationEditor.java
@@ -19,6 +19,7 @@
package io.github.dsheirer.gui.playlist.channel;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -142,6 +143,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label poolSizeLabel = new Label("Max Traffic Channels");
+ poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 0, row);
gridPane.getChildren().add(poolSizeLabel);
@@ -153,6 +155,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getIgnoreDataCallsButton());
Label ignoreDataLabel = new Label("Ignore Data Calls");
+ ignoreDataLabel.setLabelFor(getIgnoreDataCallsButton());
GridPane.setHalignment(ignoreDataLabel, HPos.LEFT);
GridPane.setConstraints(ignoreDataLabel, 3, row);
gridPane.getChildren().add(ignoreDataLabel);
@@ -161,6 +164,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getIgnoreCRCChecksumsButton());
Label ignoreCRCLabel = new Label("Ignore CRC Checksums (RAS)");
+ ignoreCRCLabel.setLabelFor(getIgnoreCRCChecksumsButton());
GridPane.setHalignment(ignoreCRCLabel, HPos.LEFT);
GridPane.setConstraints(ignoreCRCLabel, 5, row);
gridPane.getChildren().add(ignoreCRCLabel);
@@ -169,6 +173,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getUseCompressedTalkgroupsToggle());
Label useCompressedTalkgroupsLabel = new Label("Use Compressed Talkgroups");
+ useCompressedTalkgroupsLabel.setLabelFor(getUseCompressedTalkgroupsToggle());
GridPane.setHalignment(useCompressedTalkgroupsLabel, HPos.LEFT);
GridPane.setConstraints(useCompressedTalkgroupsLabel, 7, row);
gridPane.getChildren().add(useCompressedTalkgroupsLabel);
@@ -196,9 +201,11 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
editorBox.setSpacing(5);
Label lcnLabel = new Label("LCN");
+ lcnLabel.setLabelFor(getLogicalChannelNumberField());
editorBox.getChildren().addAll(lcnLabel, getLogicalChannelNumberField());
Label downlinkLabel = new Label("Frequency (MHz)");
+ downlinkLabel.setLabelFor(getDownlinkFrequencyField());
downlinkLabel.setPadding(new Insets(0,0,0,5));
editorBox.getChildren().addAll(downlinkLabel,getDownlinkFrequencyField());
@@ -286,6 +293,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
if(mTimeslotFrequencyTable == null)
{
mTimeslotFrequencyTable = new TableView<>(FXCollections.observableArrayList(TimeslotFrequency.extractor()));
+ mTimeslotFrequencyTable.setAccessibleText("Logical channel number (LCN) to frequency map");
mTimeslotFrequencyTable.setPrefHeight(100.0);
TableColumn numberColumn = new TableColumn("LCN");
@@ -346,6 +354,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
if(mAddTimeslotFrequencyButton == null)
{
mAddTimeslotFrequencyButton = new Button("Add");
+ mAddTimeslotFrequencyButton.setAccessibleText("Add LCN frequency");
mAddTimeslotFrequencyButton.setMaxWidth(Double.MAX_VALUE);
mAddTimeslotFrequencyButton.setOnAction(event -> addTimeslot());
}
@@ -394,6 +403,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
if(mDeleteTimeslotFrequencyButton == null)
{
mDeleteTimeslotFrequencyButton = new Button("Delete");
+ mDeleteTimeslotFrequencyButton.setAccessibleText("Delete LCN frequency");
mDeleteTimeslotFrequencyButton.setDisable(true);
mDeleteTimeslotFrequencyButton.setMaxWidth(Double.MAX_VALUE);
mDeleteTimeslotFrequencyButton.setOnAction(new EventHandler()
@@ -501,7 +511,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreDataCallsButton == null)
{
- mIgnoreDataCallsButton = new ToggleSwitch();
+ mIgnoreDataCallsButton = new AccessibleToggleSwitch();
mIgnoreDataCallsButton.setDisable(true);
mIgnoreDataCallsButton.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -514,7 +524,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreCRCChecksumsButton == null)
{
- mIgnoreCRCChecksumsButton = new ToggleSwitch();
+ mIgnoreCRCChecksumsButton = new AccessibleToggleSwitch();
mIgnoreCRCChecksumsButton.setDisable(true);
mIgnoreCRCChecksumsButton.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -532,7 +542,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mUseCompressedTalkgroupsToggle == null)
{
- mUseCompressedTalkgroupsToggle = new ToggleSwitch();
+ mUseCompressedTalkgroupsToggle = new AccessibleToggleSwitch();
mUseCompressedTalkgroupsToggle.setTooltip(new Tooltip("Use Compressed Talkgroups. This is only for Hytera Tier-III Trunked Systems"));
mUseCompressedTalkgroupsToggle.setDisable(true);
mUseCompressedTalkgroupsToggle.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
@@ -563,7 +573,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeAGCButton == null)
{
- mJmbeAGCButton = new ToggleSwitch();
+ mJmbeAGCButton = new AccessibleToggleSwitch();
mJmbeAGCButton.setTooltip(new Tooltip("Automatic Gain Control to automatically adjust call audio volume"));
mJmbeAGCButton.setDisable(true);
mJmbeAGCButton.selectedProperty().addListener((o, old, n) -> modifiedProperty().set(true));
@@ -576,7 +586,7 @@ public class DMRConfigurationEditor extends ChannelConfigurationEditor
{
if(mLegacyToneLevelsButton == null)
{
- mLegacyToneLevelsButton = new ToggleSwitch();
+ mLegacyToneLevelsButton = new AccessibleToggleSwitch();
mLegacyToneLevelsButton.setTooltip(new Tooltip(
"Restore JMBE 1.x AMBE tone levels. Requires a supporting JMBE library; " +
"stock libraries ignore this option. Tone Volume still adjusts the selected level."));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java
index e2da48a9..92589c51 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRConfigurationEditor.java
@@ -19,6 +19,7 @@
package io.github.dsheirer.gui.playlist.channel;
+import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.playlist.decoder.AuxDecoderConfigurationEditor;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -146,6 +147,7 @@ public class LTRConfigurationEditor extends ChannelConfigurationEditor
gridPane.setHgap(10);
Label directionLabel = new Label("Direction");
+ AccessibilityUtil.labelGroup(directionLabel, getDirectionButton());
GridPane.setHalignment(directionLabel, HPos.LEFT);
GridPane.setConstraints(directionLabel, 0, 0);
gridPane.getChildren().add(directionLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java
index e681d6e4..12095ddd 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/LTRNetConfigurationEditor.java
@@ -19,6 +19,7 @@
package io.github.dsheirer.gui.playlist.channel;
+import io.github.dsheirer.gui.control.AccessibilityUtil;
import io.github.dsheirer.gui.playlist.decoder.AuxDecoderConfigurationEditor;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -146,6 +147,7 @@ public class LTRNetConfigurationEditor extends ChannelConfigurationEditor
gridPane.setHgap(10);
Label directionLabel = new Label("Direction");
+ AccessibilityUtil.labelGroup(directionLabel, getDirectionButton());
GridPane.setHalignment(directionLabel, HPos.LEFT);
GridPane.setConstraints(directionLabel, 0, 0);
gridPane.getChildren().add(directionLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java
index 58885d72..97c25891 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/MPT1327ConfigurationEditor.java
@@ -126,6 +126,7 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
gridPane.setVgap(10);
Label channelMapLabel = new Label("Channel Map");
+ channelMapLabel.setLabelFor(getChannelMapComboBox());
GridPane.setHalignment(channelMapLabel, HPos.RIGHT);
GridPane.setConstraints(channelMapLabel, 0, 0);
gridPane.getChildren().add(channelMapLabel);
@@ -138,6 +139,7 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getChannelMapEditButton());
Label poolSizeLabel = new Label("Max Traffic Channels");
+ poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 0, 1);
gridPane.getChildren().add(poolSizeLabel);
@@ -146,6 +148,7 @@ public class MPT1327ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getTrafficChannelPoolSizeSpinner());
Label callTimeoutLabel = new Label("Call Timeout Seconds");
+ callTimeoutLabel.setLabelFor(getCallTimeoutSpinner());
GridPane.setHalignment(callTimeoutLabel, HPos.RIGHT);
GridPane.setConstraints(callTimeoutLabel, 2, 1);
gridPane.getChildren().add(callTimeoutLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java
index 4af3f85d..b31d3707 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/NBFMConfigurationEditor.java
@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.playlist.channel;
+import io.github.dsheirer.gui.control.AccessibilityUtil;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.HexFormatter;
import io.github.dsheirer.gui.control.IntegerFormatter;
import io.github.dsheirer.gui.playlist.decoder.AuxDecoderConfigurationEditor;
@@ -135,6 +137,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
// channel bandwidth
Label bandwidthLabel = new Label("Channel Bandwidth");
+ AccessibilityUtil.labelGroup(bandwidthLabel, getBandwidthButton());
GridPane.setHalignment(bandwidthLabel, HPos.RIGHT);
GridPane.setConstraints(bandwidthLabel, 0, 0);
gridPane.getChildren().add(bandwidthLabel);
@@ -144,6 +147,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
//De-emphasis
Label deemphasisLabel = new Label("De-emphasis");
+ deemphasisLabel.setLabelFor(getDeemphasisCombo());
GridPane.setHalignment(deemphasisLabel, HPos.RIGHT);
GridPane.setConstraints(deemphasisLabel, 0, 1);
gridPane.getChildren().add(deemphasisLabel);
@@ -154,6 +158,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
// Note: normally the label and its toggle switch are one cell. They were split to make the next row
// look better. The label is manually created and placed here. Might want to create a new grid pane instead.
Label HPFLabel = new Label("High-pass Audio Filter (300 Hz)");
+ HPFLabel.setLabelFor(getAudioFilterEnable());
GridPane.setHalignment(HPFLabel, HPos.RIGHT);
GridPane.setConstraints(HPFLabel, 2, 1);
gridPane.getChildren().add(HPFLabel);
@@ -172,6 +177,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
*/
// talkgroup
Label talkgroupLabel = new Label("Talkgroup To Assign");
+ talkgroupLabel.setLabelFor(getTalkgroupField());
GridPane.setHalignment(talkgroupLabel, HPos.RIGHT);
GridPane.setConstraints(talkgroupLabel, 0, 2);
gridPane.getChildren().add(talkgroupLabel);
@@ -198,6 +204,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
modifiedProperty().set(true);
});
GridPane.setConstraints(mSquelchTypeCombo, 3, 2);
+ typeLabel.setLabelFor(mSquelchTypeCombo);
gridPane.getChildren().add(mSquelchTypeCombo);
// CTCSS code selector
@@ -318,6 +325,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getBasebandRecordSwitch());
Label recordBasebandLabel = new Label("Channel (Baseband I&Q)");
+ recordBasebandLabel.setLabelFor(getBasebandRecordSwitch());
GridPane.setHalignment(recordBasebandLabel, HPos.LEFT);
GridPane.setConstraints(recordBasebandLabel, 1, 0);
gridPane.getChildren().add(recordBasebandLabel);
@@ -381,7 +389,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
// removed label from toggle switch so that the label and the switch can be in different columns
// to make things look better on the next line. Perhaps make a new grid pane instead.
//mAudioFilterEnable = new ToggleSwitch("High-Pass Audio Filter (300 Hz)");
- mAudioFilterEnable = new ToggleSwitch("");
+ mAudioFilterEnable = new AccessibleToggleSwitch("");
mAudioFilterEnable.setTooltip(new Tooltip("High-pass filter to remove DC offset and sub-audible signalling"));
mAudioFilterEnable.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
@@ -396,7 +404,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
{
if(mALCEnable == null)
{
- mALCEnable = new ToggleSwitch("Automatic Level Control");
+ mALCEnable = new AccessibleToggleSwitch("Automatic Level Control");
mALCEnable.setTooltip(new Tooltip("Automatic Level Control (ALC). Tries to make the volume of each received transmission the same."));
mALCEnable.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
@@ -518,7 +526,7 @@ public class NBFMConfigurationEditor extends ChannelConfigurationEditor
{
if(mBasebandRecordSwitch == null)
{
- mBasebandRecordSwitch = new ToggleSwitch();
+ mBasebandRecordSwitch = new AccessibleToggleSwitch();
mBasebandRecordSwitch.setDisable(true);
mBasebandRecordSwitch.setTextAlignment(TextAlignment.RIGHT);
mBasebandRecordSwitch.selectedProperty()
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/NXDNConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/NXDNConfigurationEditor.java
index e455f906..81366077 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/NXDNConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/NXDNConfigurationEditor.java
@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.playlist.channel;
+import io.github.dsheirer.gui.control.AccessibilityUtil;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -148,6 +150,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label mode = new Label("Mode:");
+ AccessibilityUtil.labelGroup(mode, getTransmissionModeButton());
GridPane.setHalignment(mode, HPos.RIGHT);
GridPane.setConstraints(mode, 0, row);
gridPane.getChildren().add(mode);
@@ -156,6 +159,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getTransmissionModeButton());
Label encoding = new Label("Talker Alias Encoding:");
+ encoding.setLabelFor(getEncodingComboBox());
GridPane.setHalignment(encoding, HPos.RIGHT);
GridPane.setConstraints(encoding, 2, row);
gridPane.getChildren().add(encoding);
@@ -164,6 +168,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getEncodingComboBox());
Label poolSizeLabel = new Label("Max Traffic Channels");
+ poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 4, row);
gridPane.getChildren().add(poolSizeLabel);
@@ -205,9 +210,11 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
editorBox.setSpacing(5);
Label lcnLabel = new Label("Channel Number");
+ lcnLabel.setLabelFor(getChannelField());
editorBox.getChildren().addAll(lcnLabel, getChannelField());
Label downlinkLabel = new Label("Downlink Frequency (MHz)");
+ downlinkLabel.setLabelFor(getDownlinkFrequencyField());
downlinkLabel.setPadding(new Insets(0,0,0,5));
editorBox.getChildren().addAll(downlinkLabel,getDownlinkFrequencyField());
@@ -312,6 +319,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
if(mChannelMapTable == null)
{
mChannelMapTable = new TableView<>(FXCollections.observableArrayList(ObservableChannelFrequency.extractor()));
+ mChannelMapTable.setAccessibleText("Channel number (LCN) to frequency map");
mChannelMapTable.setPrefHeight(100.0);
TableColumn channelColumn = new TableColumn<>("Channel");
@@ -401,6 +409,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
if(mAddTimeslotFrequencyButton == null)
{
mAddTimeslotFrequencyButton = new Button("Add");
+ mAddTimeslotFrequencyButton.setAccessibleText("Add channel frequency");
mAddTimeslotFrequencyButton.setMaxWidth(Double.MAX_VALUE);
mAddTimeslotFrequencyButton.setOnAction(event -> addChannelFrequency());
}
@@ -425,6 +434,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
if(mDeleteButton == null)
{
mDeleteButton = new Button("Delete");
+ mDeleteButton.setAccessibleText("Delete channel frequency");
mDeleteButton.setDisable(true);
mDeleteButton.setMaxWidth(Double.MAX_VALUE);
mDeleteButton.setOnAction(event -> {
@@ -519,7 +529,8 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreDataCallsToggle == null)
{
- mIgnoreDataCallsToggle = new ToggleSwitch("Data Calls");
+ mIgnoreDataCallsToggle = new AccessibleToggleSwitch("Data Calls");
+ mIgnoreDataCallsToggle.setAccessibleText("Ignore data calls");
mIgnoreDataCallsToggle.setDisable(true);
mIgnoreDataCallsToggle.selectedProperty().addListener((o, ov, nv) -> modifiedProperty().set(true));
}
@@ -534,7 +545,8 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreEncryptedCallsToggle == null)
{
- mIgnoreEncryptedCallsToggle = new ToggleSwitch("Encrypted Calls");
+ mIgnoreEncryptedCallsToggle = new AccessibleToggleSwitch("Encrypted Calls");
+ mIgnoreEncryptedCallsToggle.setAccessibleText("Ignore encrypted calls");
mIgnoreEncryptedCallsToggle.setDisable(true);
mIgnoreEncryptedCallsToggle.selectedProperty().addListener((o, ov, nv) -> modifiedProperty().set(true));
}
@@ -546,7 +558,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeAGCButton == null)
{
- mJmbeAGCButton = new ToggleSwitch();
+ mJmbeAGCButton = new AccessibleToggleSwitch();
mJmbeAGCButton.setTooltip(new Tooltip("Automatic Gain Control to automatically adjust call audio volume"));
mJmbeAGCButton.setDisable(true);
mJmbeAGCButton.selectedProperty().addListener((o, old, n) -> modifiedProperty().set(true));
@@ -559,7 +571,7 @@ public class NXDNConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeLegacyToneLevelsButton == null)
{
- mJmbeLegacyToneLevelsButton = new ToggleSwitch();
+ mJmbeLegacyToneLevelsButton = new AccessibleToggleSwitch();
mJmbeLegacyToneLevelsButton.setTooltip(new Tooltip(
"Restore JMBE 1.x AMBE tone levels. Requires a supporting JMBE library; " +
"stock libraries ignore this option. Tone Volume still adjusts the selected level."));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java
index c82ac9be..af6daf4e 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P1ConfigurationEditor.java
@@ -19,6 +19,8 @@
package io.github.dsheirer.gui.playlist.channel;
+import io.github.dsheirer.gui.control.AccessibilityUtil;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
import io.github.dsheirer.gui.playlist.source.FrequencyEditor;
@@ -129,6 +131,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label modulationLabel = new Label("Modulation");
+ AccessibilityUtil.labelGroup(modulationLabel, getModulationSegmentedButton());
GridPane.setHalignment(modulationLabel, HPos.RIGHT);
GridPane.setConstraints(modulationLabel, 0, row);
gridPane.getChildren().add(modulationLabel);
@@ -137,6 +140,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().addAll(getModulationSegmentedButton());
Label poolSizeLabel = new Label("Max Traffic Channels");
+ poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 2, row);
gridPane.getChildren().add(poolSizeLabel);
@@ -148,6 +152,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getIgnoreDataCallsButton());
Label directionLabel = new Label("Ignore Data Calls");
+ directionLabel.setLabelFor(getIgnoreDataCallsButton());
GridPane.setHalignment(directionLabel, HPos.LEFT);
GridPane.setConstraints(directionLabel, 5, row);
gridPane.getChildren().add(directionLabel);
@@ -268,7 +273,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeAGCButton == null)
{
- mJmbeAGCButton = new ToggleSwitch();
+ mJmbeAGCButton = new AccessibleToggleSwitch();
mJmbeAGCButton.setTooltip(new Tooltip("Automatic Gain Control to automatically adjust call audio volume"));
mJmbeAGCButton.setDisable(true);
mJmbeAGCButton.selectedProperty().addListener((o, old, n) -> modifiedProperty().set(true));
@@ -281,7 +286,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
{
if(mLegacyToneLevelsButton == null)
{
- mLegacyToneLevelsButton = new ToggleSwitch();
+ mLegacyToneLevelsButton = new AccessibleToggleSwitch();
mLegacyToneLevelsButton.setTooltip(new Tooltip(
"Restore JMBE 1.x AMBE tone levels. Requires a supporting JMBE library; " +
"stock libraries ignore this option. Tone Volume still adjusts the selected level."));
@@ -342,7 +347,7 @@ public class P25P1ConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreDataCallsButton == null)
{
- mIgnoreDataCallsButton = new ToggleSwitch();
+ mIgnoreDataCallsButton = new AccessibleToggleSwitch();
mIgnoreDataCallsButton.setDisable(true);
mIgnoreDataCallsButton.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java
index f0161a82..916e1302 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channel/P25P2ConfigurationEditor.java
@@ -19,6 +19,7 @@
package io.github.dsheirer.gui.playlist.channel;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.gui.playlist.eventlog.EventLogConfigurationEditor;
import io.github.dsheirer.gui.playlist.record.RecordConfigurationEditor;
@@ -124,6 +125,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
int row = 0;
Label poolSizeLabel = new Label("Max Traffic Channels");
+ poolSizeLabel.setLabelFor(getTrafficChannelPoolSizeSpinner());
GridPane.setHalignment(poolSizeLabel, HPos.RIGHT);
GridPane.setConstraints(poolSizeLabel, 0, row);
gridPane.getChildren().add(poolSizeLabel);
@@ -135,11 +137,13 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getIgnoreDataCallsButton());
Label directionLabel = new Label("Ignore Data Calls");
+ directionLabel.setLabelFor(getIgnoreDataCallsButton());
GridPane.setHalignment(directionLabel, HPos.LEFT);
GridPane.setConstraints(directionLabel, 3, row);
gridPane.getChildren().add(directionLabel);
Label wacnLabel = new Label("WACN");
+ wacnLabel.setLabelFor(getWacnTextField());
GridPane.setHalignment(wacnLabel, HPos.RIGHT);
GridPane.setConstraints(wacnLabel, 0, ++row);
gridPane.getChildren().add(wacnLabel);
@@ -148,6 +152,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getWacnTextField());
Label systemLabel = new Label("System");
+ systemLabel.setLabelFor(getSystemTextField());
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 2, row);
gridPane.getChildren().add(systemLabel);
@@ -156,6 +161,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
gridPane.getChildren().add(getSystemTextField());
Label nacLabel = new Label("NAC");
+ nacLabel.setLabelFor(getNacTextField());
GridPane.setHalignment(nacLabel, HPos.RIGHT);
GridPane.setConstraints(nacLabel, 4, row);
gridPane.getChildren().add(nacLabel);
@@ -183,7 +189,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
{
if(mJmbeAGCButton == null)
{
- mJmbeAGCButton = new ToggleSwitch();
+ mJmbeAGCButton = new AccessibleToggleSwitch();
mJmbeAGCButton.setTooltip(new Tooltip("Automatic Gain Control to automatically adjust call audio volume"));
mJmbeAGCButton.setDisable(true);
mJmbeAGCButton.selectedProperty().addListener((o, old, n) -> modifiedProperty().set(true));
@@ -196,7 +202,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
{
if(mLegacyToneLevelsButton == null)
{
- mLegacyToneLevelsButton = new ToggleSwitch();
+ mLegacyToneLevelsButton = new AccessibleToggleSwitch();
mLegacyToneLevelsButton.setTooltip(new Tooltip(
"Restore JMBE 1.x AMBE tone levels. Requires a supporting JMBE library; " +
"stock libraries ignore this option. Tone Volume still adjusts the selected level."));
@@ -298,7 +304,7 @@ public class P25P2ConfigurationEditor extends ChannelConfigurationEditor
{
if(mIgnoreDataCallsButton == null)
{
- mIgnoreDataCallsButton = new ToggleSwitch();
+ mIgnoreDataCallsButton = new AccessibleToggleSwitch();
mIgnoreDataCallsButton.setDisable(true);
mIgnoreDataCallsButton.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/channelMap/ChannelMapEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/channelMap/ChannelMapEditor.java
index 06fa576e..ca5c2a9e 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/channelMap/ChannelMapEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/channelMap/ChannelMapEditor.java
@@ -276,6 +276,7 @@ public class ChannelMapEditor extends SplitPane
if(mChannelMapListView == null)
{
mChannelMapListView = new ListView<>();
+ mChannelMapListView.setAccessibleText("Channel maps");
mChannelMapListView.setItems(mChannelMapModel.getChannelMaps());
mChannelMapListView.getSelectionModel().selectedItemProperty()
.addListener((observable, oldValue, newValue) -> {
@@ -390,6 +391,7 @@ public class ChannelMapEditor extends SplitPane
mChannelRangePane.setHgap(5);
Label firstLabel = new Label("First Channel");
+ firstLabel.setLabelFor(getFirstField());
GridPane.setHalignment(firstLabel, HPos.RIGHT);
GridPane.setConstraints(firstLabel, 0, 0);
mChannelRangePane.getChildren().add(firstLabel);
@@ -399,6 +401,7 @@ public class ChannelMapEditor extends SplitPane
mChannelRangePane.getChildren().add(getFirstField());
Label lastLabel = new Label("Last Channel");
+ lastLabel.setLabelFor(getLastField());
GridPane.setHalignment(lastLabel, HPos.RIGHT);
GridPane.setConstraints(lastLabel, 0, 1);
mChannelRangePane.getChildren().add(lastLabel);
@@ -408,6 +411,7 @@ public class ChannelMapEditor extends SplitPane
mChannelRangePane.getChildren().add(getLastField());
Label baseLabel = new Label("Base Frequency (Hz)");
+ baseLabel.setLabelFor(getBaseFrequencyField());
GridPane.setHalignment(baseLabel, HPos.RIGHT);
GridPane.setConstraints(baseLabel, 0, 2);
mChannelRangePane.getChildren().add(baseLabel);
@@ -417,6 +421,7 @@ public class ChannelMapEditor extends SplitPane
mChannelRangePane.getChildren().add(getBaseFrequencyField());
Label stepSizeLabel = new Label("Step Size (Hz)");
+ stepSizeLabel.setLabelFor(getStepSizeField());
GridPane.setHalignment(stepSizeLabel, HPos.RIGHT);
GridPane.setConstraints(stepSizeLabel, 0, 3);
mChannelRangePane.getChildren().add(stepSizeLabel);
@@ -445,7 +450,9 @@ public class ChannelMapEditor extends SplitPane
nameBox.setSpacing(10);
nameBox.setAlignment(Pos.CENTER);
HBox.setHgrow(getNameField(), Priority.ALWAYS);
- nameBox.getChildren().addAll(new Label("Name"), getNameField());
+ Label nameLabel = new Label("Name");
+ nameLabel.setLabelFor(getNameField());
+ nameBox.getChildren().addAll(nameLabel, getNameField());
VBox contentsBox = new VBox();
VBox.setVgrow(nameBox, Priority.NEVER);
@@ -599,6 +606,7 @@ public class ChannelMapEditor extends SplitPane
if(mChannelRangeTableView == null)
{
mChannelRangeTableView = new TableView<>();
+ mChannelRangeTableView.setAccessibleText("Channel ranges");
TableColumn firstColumn = new TableColumn("First");
firstColumn.setCellValueFactory(new PropertyValueFactory("firstChannel"));
@@ -790,6 +798,8 @@ public class ChannelMapEditor extends SplitPane
item == null || empty);
this.setStyle(invalidRange ? BACKGROUND_COLOR_RED : BACKGROND_COLOR_NONE);
+ //Invalid ranges are only shown in red, so also report them to screen readers
+ setAccessibleText(invalidRange && item != null && !empty ? item + ", invalid or overlapping range" : null);
}
@Override
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/decoder/AuxDecoderConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/decoder/AuxDecoderConfigurationEditor.java
index 499a470a..207fdc55 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/decoder/AuxDecoderConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/decoder/AuxDecoderConfigurationEditor.java
@@ -22,6 +22,7 @@
package io.github.dsheirer.gui.playlist.decoder;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.module.decode.DecoderType;
import io.github.dsheirer.module.decode.config.AuxDecodeConfiguration;
@@ -121,6 +122,7 @@ public class AuxDecoderConfigurationEditor extends Editor modifiedProperty().set(true));
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/eventlog/EventLogConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/eventlog/EventLogConfigurationEditor.java
index 55ad9870..43afea59 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/eventlog/EventLogConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/eventlog/EventLogConfigurationEditor.java
@@ -22,6 +22,7 @@
package io.github.dsheirer.gui.playlist.eventlog;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.module.log.EventLogType;
import io.github.dsheirer.module.log.config.EventLogConfiguration;
@@ -117,6 +118,7 @@ public class EventLogConfigurationEditor extends Editor
getChildren().add(getToggleSwitch());
Label label = new Label(mEventLogType.getDisplayString());
+ label.setLabelFor(getToggleSwitch());
GridPane.setHalignment(label, HPos.LEFT);
GridPane.setConstraints(label, 1, 0);
getChildren().add(label);
@@ -126,7 +128,7 @@ public class EventLogConfigurationEditor extends Editor
{
if(mToggleSwitch == null)
{
- mToggleSwitch = new ToggleSwitch();
+ mToggleSwitch = new AccessibleToggleSwitch();
mToggleSwitch.setDisable(true);
mToggleSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/manager/PlaylistManagerEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/manager/PlaylistManagerEditor.java
index 1c8c6e9c..fb987cf3 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/manager/PlaylistManagerEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/manager/PlaylistManagerEditor.java
@@ -127,6 +127,7 @@ public class PlaylistManagerEditor extends HBox
if(mPlaylistTableView == null)
{
mPlaylistTableView = new TableView<>();
+ mPlaylistTableView.setAccessibleText("Playlists");
TableColumn iconColumn = new TableColumn("Selected");
iconColumn.setPrefWidth(100);
iconColumn.setCellFactory(new Callback,TableCell>()
@@ -144,6 +145,7 @@ public class PlaylistManagerEditor extends HBox
if(empty || getTableRow() == null || getTableRow().getItem() == null)
{
setGraphic(null);
+ setAccessibleText(null);
}
else
{
@@ -154,16 +156,19 @@ public class PlaylistManagerEditor extends HBox
IconNode iconNode = new IconNode(FontAwesome.CHECK);
iconNode.setFill(Color.GREEN);
setGraphic(iconNode);
+ setAccessibleText("Selected");
}
else if(!path.toFile().exists())
{
IconNode iconNode = new IconNode(FontAwesome.TIMES);
iconNode.setFill(Color.RED);
setGraphic(iconNode);
+ setAccessibleText("File not found");
}
else
{
setGraphic(null);
+ setAccessibleText("Not selected");
}
}
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyEditor.java
index ee86b213..5b2884fe 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyEditor.java
@@ -138,6 +138,7 @@ public class AgencyEditor extends VBox
if(mAgencyListView == null)
{
mAgencyListView = new ListView<>();
+ mAgencyListView.setAccessibleText("Agencies");
mAgencyListView.setPrefHeight(200);
mAgencyListView.setCellFactory(param -> new AgencyListCell());
mAgencyListView.getSelectionModel().selectedItemProperty()
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyFrequencyEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyFrequencyEditor.java
index 03d47ea3..5fa8acac 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyFrequencyEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/AgencyFrequencyEditor.java
@@ -94,6 +94,7 @@ public class AgencyFrequencyEditor extends GridPane
getColumnConstraints().addAll(column1, column2, column3);
Label categoryLabel = new Label("Category");
+ categoryLabel.setLabelFor(getCategoryComboBox());
GridPane.setConstraints(categoryLabel, 0, row);
GridPane.setHalignment(categoryLabel, HPos.RIGHT);
getChildren().add(categoryLabel);
@@ -103,6 +104,7 @@ public class AgencyFrequencyEditor extends GridPane
getChildren().add(getCategoryComboBox());
Label subCategoryLabel = new Label("Sub-Category");
+ subCategoryLabel.setLabelFor(getSubCategoryComboBox());
GridPane.setConstraints(subCategoryLabel, 0, ++row);
GridPane.setHalignment(subCategoryLabel, HPos.RIGHT);
getChildren().add(subCategoryLabel);
@@ -207,6 +209,7 @@ public class AgencyFrequencyEditor extends GridPane
if(mFrequencyTableView == null)
{
mFrequencyTableView = new TableView<>();
+ mFrequencyTableView.setAccessibleText("Agency frequencies");
mFrequencyTableView.setMaxHeight(Double.MAX_VALUE);
mFrequencyTableView.setPlaceholder(getPlaceholderLabel());
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/FrequencyEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/FrequencyEditor.java
index 8b028c7c..86e3b8e0 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/FrequencyEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/FrequencyEditor.java
@@ -97,6 +97,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(detailsLabel);
Label alphaLabel = new Label("Alpha Tag");
+ alphaLabel.setLabelFor(getAlphaTagTextField());
GridPane.setHalignment(alphaLabel, HPos.RIGHT);
GridPane.setConstraints(alphaLabel, 0, ++row);
gridPane.getChildren().add(alphaLabel);
@@ -106,6 +107,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getAlphaTagTextField());
Label toneLabel = new Label("Tone");
+ toneLabel.setLabelFor(getToneTextField());
GridPane.setHalignment(toneLabel, HPos.RIGHT);
GridPane.setConstraints(toneLabel, 0, ++row);
gridPane.getChildren().add(toneLabel);
@@ -115,6 +117,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getToneTextField());
Label modeLabel = new Label("Mode");
+ modeLabel.setLabelFor(getModeTextField());
GridPane.setHalignment(modeLabel, HPos.RIGHT);
GridPane.setConstraints(modeLabel, 0, ++row);
gridPane.getChildren().add(modeLabel);
@@ -124,6 +127,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getModeTextField());
Label frequencyLabel = new Label("Frequency");
+ frequencyLabel.setLabelFor(getFrequencyTextField());
GridPane.setHalignment(frequencyLabel, HPos.RIGHT);
GridPane.setConstraints(frequencyLabel, 0, ++row);
gridPane.getChildren().add(frequencyLabel);
@@ -141,6 +145,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(createLabel);
Label systemLabel = new Label("System");
+ systemLabel.setLabelFor(getSystemTextField());
GridPane.setHalignment(systemLabel, HPos.RIGHT);
GridPane.setConstraints(systemLabel, 0, ++row);
gridPane.getChildren().add(systemLabel);
@@ -150,6 +155,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getSystemTextField());
Label siteLabel = new Label("Site");
+ siteLabel.setLabelFor(getSiteTextField());
GridPane.setHalignment(siteLabel, HPos.RIGHT);
GridPane.setConstraints(siteLabel, 0, ++row);
gridPane.getChildren().add(siteLabel);
@@ -159,6 +165,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getSiteTextField());
Label nameLabel = new Label("Name");
+ nameLabel.setLabelFor(getNameTextField());
GridPane.setHalignment(nameLabel, HPos.RIGHT);
GridPane.setConstraints(nameLabel, 0, ++row);
gridPane.getChildren().add(nameLabel);
@@ -168,6 +175,7 @@ public class FrequencyEditor extends VBox
gridPane.getChildren().add(getNameTextField());
Label decoderLabel = new Label("Decoder");
+ decoderLabel.setLabelFor(getDecoderTextField());
GridPane.setHalignment(decoderLabel, HPos.RIGHT);
GridPane.setConstraints(decoderLabel, 0, ++row);
gridPane.getChildren().add(decoderLabel);
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/LoginDialog.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/LoginDialog.java
index f30ef455..4ab81c1c 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/LoginDialog.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/LoginDialog.java
@@ -25,6 +25,7 @@ import io.github.dsheirer.rrapi.response.Fault;
import io.github.dsheirer.rrapi.type.AuthorizationInformation;
import io.github.dsheirer.service.radioreference.RadioReference;
import javafx.application.Platform;
+import javafx.beans.binding.Bindings;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.HPos;
@@ -128,6 +129,7 @@ public class LoginDialog extends Dialog
mGridPane.setVgap(5.0);
Label userNameLabel = new Label("User Name:");
+ userNameLabel.setLabelFor(getUserNameText());
GridPane.setHalignment(userNameLabel, HPos.RIGHT);
GridPane.setConstraints(userNameLabel, 0, 0);
mGridPane.getChildren().add(userNameLabel);
@@ -137,6 +139,8 @@ public class LoginDialog extends Dialog
mGridPane.getChildren().add(getUserNameText());
Label passwordLabel = new Label("Password:");
+ passwordLabel.labelForProperty().bind(Bindings.when(getPasswordText().visibleProperty())
+ .then((Node)getPasswordText()).otherwise(getPasswordField()));
GridPane.setHalignment(passwordLabel, HPos.RIGHT);
GridPane.setConstraints(passwordLabel, 0, 1);
mGridPane.getChildren().add(passwordLabel);
@@ -229,6 +233,7 @@ public class LoginDialog extends Dialog
if(mShowPasswordCheckBox == null)
{
mShowPasswordCheckBox = new CheckBox("Show");
+ mShowPasswordCheckBox.setAccessibleText("Show password");
mShowPasswordCheckBox.setSelected(mUserPreferences.getRadioReferencePreference().getShowPassword());
mShowPasswordCheckBox.setOnAction(new EventHandler()
{
@@ -281,6 +286,7 @@ public class LoginDialog extends Dialog
public void handle(ActionEvent event)
{
getTestConnectionButton().setDisable(true);
+ getTestConnectionButton().setAccessibleText(null);
getTestPassIcon().setVisible(false);
getTestFailIcon().setVisible(false);
getTestExpiredIcon().setVisible(false);
@@ -327,6 +333,8 @@ public class LoginDialog extends Dialog
getTestPassIcon().setVisible(true);
getTestFailIcon().setVisible(false);
getTestExpiredIcon().setVisible(false);
+ //Success is only shown as an icon, so report it in the button's accessible name
+ getTestConnectionButton().setAccessibleText("Test Connection: login successful");
}
else if (loginStatus == RadioReference.LoginStatus.EXPIRED_PREMIUM)
{
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceEditor.java
index 9a8fdd9a..7314125d 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/RadioReferenceEditor.java
@@ -377,6 +377,7 @@ public class RadioReferenceEditor extends BorderPane implements Consumer();
+ mSiteFrequencyTableView.setAccessibleText("Site frequencies");
mSiteFrequencyTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
mSiteFrequencyTableView.setPlaceholder(new Label("Please select a site to view frequencies"));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemEditor.java
index 89409ba2..7019493b 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemEditor.java
@@ -103,7 +103,9 @@ public class SystemEditor extends VBox
systemBox.setAlignment(Pos.CENTER_LEFT);
systemBox.setSpacing(5);
systemBox.setMaxWidth(Double.MAX_VALUE);
- systemBox.getChildren().addAll(new Label("System"), getSystemComboBox());
+ Label systemLabel = new Label("System");
+ systemLabel.setLabelFor(getSystemComboBox());
+ systemBox.getChildren().addAll(systemLabel, getSystemComboBox());
VBox.setVgrow(getTabPane(), Priority.ALWAYS);
getChildren().addAll(systemBox, getTabPane());
}
@@ -388,11 +390,14 @@ public class SystemEditor extends VBox
setGraphic(null);
mName.setText(null);
mProtocol.setText(null);
+ setAccessibleText(null);
}
else
{
mName.setText(item.getName());
mProtocol.setText(getType(item));
+ //Text is shown in nested labels, so expose it as the cell's accessible text
+ setAccessibleText(item.getName() + ", " + getType(item));
if(isSupported(item))
{
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemSiteSelectionEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemSiteSelectionEditor.java
index 8be96c12..a04f2d20 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemSiteSelectionEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemSiteSelectionEditor.java
@@ -214,6 +214,7 @@ public class SystemSiteSelectionEditor extends GridPane
if(mSiteTableView == null)
{
mSiteTableView = new TableView<>();
+ mSiteTableView.setAccessibleText("Sites");
mSiteTableView.setPlaceholder(getPlaceholderLabel());
TableColumn systemColumn = new TableColumn();
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemTalkgroupSelectionEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemTalkgroupSelectionEditor.java
index e88637b0..2529db33 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemTalkgroupSelectionEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/SystemTalkgroupSelectionEditor.java
@@ -128,6 +128,7 @@ public class SystemTalkgroupSelectionEditor extends GridPane
listBox.setSpacing(5);
listBox.setAlignment(Pos.CENTER);
Label importLabel = new Label("Import To Alias List:");
+ importLabel.setLabelFor(getAliasListNameComboBox());
listBox.getChildren().addAll(importLabel, getAliasListNameComboBox(), getNewAliasListButton());
GridPane.setConstraints(listBox, 0, row);
getChildren().add(listBox);
@@ -139,7 +140,9 @@ public class SystemTalkgroupSelectionEditor extends GridPane
HBox searchBox = new HBox();
searchBox.setSpacing(5);
searchBox.setAlignment(Pos.CENTER);
- searchBox.getChildren().addAll(new Label("Search"), getSearchField());
+ Label searchLabel = new Label("Search");
+ searchLabel.setLabelFor(getSearchField());
+ searchBox.getChildren().addAll(searchLabel, getSearchField());
GridPane.setConstraints(searchBox, 0, ++row);
getChildren().add(searchBox);
@@ -151,7 +154,9 @@ public class SystemTalkgroupSelectionEditor extends GridPane
categoryBox.setAlignment(Pos.CENTER_LEFT);
categoryBox.setSpacing(5);
HBox.setHgrow(getTalkgroupCategoryComboBox(), Priority.ALWAYS);
- categoryBox.getChildren().addAll(new Label("Category"), getTalkgroupCategoryComboBox());
+ Label categoryLabel = new Label("Category");
+ categoryLabel.setLabelFor(getTalkgroupCategoryComboBox());
+ categoryBox.getChildren().addAll(categoryLabel, getTalkgroupCategoryComboBox());
GridPane.setHgrow(categoryBox, Priority.ALWAYS);
GridPane.setConstraints(categoryBox, 0, ++row);
getChildren().add(categoryBox);
@@ -612,6 +617,7 @@ public class SystemTalkgroupSelectionEditor extends GridPane
if(mTalkgroupTableView == null)
{
mTalkgroupTableView = new TableView<>();
+ mTalkgroupTableView.setAccessibleText("Talkgroups");
mTalkgroupTableView.setMaxHeight(Double.MAX_VALUE);
TableColumn talkgroupColumn = new TableColumn("Talkgroup");
talkgroupColumn.setCellValueFactory(new PropertyValueFactory<>("talkgroup"));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/TalkgroupEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/TalkgroupEditor.java
index 5f7ec8ef..849521e7 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/radioreference/TalkgroupEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/radioreference/TalkgroupEditor.java
@@ -80,6 +80,7 @@ public class TalkgroupEditor extends GridPane
getChildren().add(radioReferenceLabel);
Label talkgroupLabel = new Label("Talkgroup");
+ talkgroupLabel.setLabelFor(getTalkgroupTextField());
GridPane.setConstraints(talkgroupLabel, 0, ++row);
GridPane.setHalignment(talkgroupLabel, HPos.RIGHT);
getChildren().add(talkgroupLabel);
@@ -89,6 +90,7 @@ public class TalkgroupEditor extends GridPane
getChildren().add(getTalkgroupTextField());
Label alphaLabel = new Label("Alpha Tag");
+ alphaLabel.setLabelFor(getAlphaTagTextField());
GridPane.setConstraints(alphaLabel, 0, ++row);
GridPane.setHalignment(alphaLabel, HPos.RIGHT);
getChildren().add(alphaLabel);
@@ -98,6 +100,7 @@ public class TalkgroupEditor extends GridPane
getChildren().add(getAlphaTagTextField());
Label descriptionLabel = new Label("Description");
+ descriptionLabel.setLabelFor(getDescriptionTextField());
GridPane.setConstraints(descriptionLabel, 0, ++row);
GridPane.setHalignment(descriptionLabel, HPos.RIGHT);
getChildren().add(descriptionLabel);
@@ -107,6 +110,7 @@ public class TalkgroupEditor extends GridPane
getChildren().add(getDescriptionTextField());
Label modeLabel = new Label("Mode");
+ modeLabel.setLabelFor(getModeTextField());
GridPane.setConstraints(modeLabel, 0, ++row);
GridPane.setHalignment(modeLabel, HPos.RIGHT);
getChildren().add(modeLabel);
@@ -222,6 +226,7 @@ public class TalkgroupEditor extends GridPane
if(mNameLabel == null)
{
mNameLabel = new Label("Name");
+ mNameLabel.setLabelFor(getAliasNameTextField());
mNameLabel.setVisible(false);
}
@@ -233,6 +238,7 @@ public class TalkgroupEditor extends GridPane
if(mGroupLabel == null)
{
mGroupLabel = new Label("Group");
+ mGroupLabel.setLabelFor(getAliasGroupTextField());
mGroupLabel.setVisible(false);
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/record/RecordConfigurationEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/record/RecordConfigurationEditor.java
index 305b2f54..efd63189 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/record/RecordConfigurationEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/record/RecordConfigurationEditor.java
@@ -22,6 +22,7 @@
package io.github.dsheirer.gui.playlist.record;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.record.RecorderType;
import io.github.dsheirer.record.config.RecordConfiguration;
@@ -117,6 +118,7 @@ public class RecordConfigurationEditor extends Editor
getChildren().add(getToggleSwitch());
Label label = new Label(mRecorderType.getDisplayString());
+ label.setLabelFor(getToggleSwitch());
GridPane.setHalignment(label, HPos.LEFT);
GridPane.setConstraints(label, 1, 0);
getChildren().add(label);
@@ -126,7 +128,7 @@ public class RecordConfigurationEditor extends Editor
{
if(mToggleSwitch == null)
{
- mToggleSwitch = new ToggleSwitch();
+ mToggleSwitch = new AccessibleToggleSwitch();
mToggleSwitch.setDisable(true);
mToggleSwitch.selectedProperty().addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java
index e3753102..cca40e14 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/source/FrequencyEditor.java
@@ -104,10 +104,12 @@ public class FrequencyEditor extends SourceConfigurationEditor addFrequencyBox());
}
@@ -553,6 +557,7 @@ public class FrequencyEditor extends SourceConfigurationEditor {
Alert confirm = new Alert(Alert.AlertType.CONFIRMATION,
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractBroadcastEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractBroadcastEditor.java
index a0d7c566..cf58a327 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractBroadcastEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractBroadcastEditor.java
@@ -22,6 +22,7 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
import io.github.dsheirer.audio.broadcast.BroadcastEvent;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.playlist.Editor;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.application.Platform;
@@ -205,7 +206,7 @@ public abstract class AbstractBroadcastEditor
{
if(mEnabledSwitch == null)
{
- mEnabledSwitch = new ToggleSwitch();
+ mEnabledSwitch = new AccessibleToggleSwitch();
mEnabledSwitch.setDisable(true);
mEnabledSwitch.selectedProperty()
.addListener((observable, oldValue, newValue) -> modifiedProperty().set(true));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractStreamEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractStreamEditor.java
index 6c0f88b4..906feabf 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractStreamEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/AbstractStreamEditor.java
@@ -25,7 +25,10 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastConfiguration;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
+import javafx.beans.binding.Bindings;
+import javafx.scene.Node;
import javafx.scene.control.CheckBox;
+import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
@@ -150,11 +153,24 @@ public abstract class AbstractStreamEditor ext
if(mShowPasswordCheckBox == null)
{
mShowPasswordCheckBox = new CheckBox("Show");
+ mShowPasswordCheckBox.setAccessibleText("Show password");
}
return mShowPasswordCheckBox;
}
+ /**
+ * Links the password label to whichever password field (masked or unmasked) is currently visible, so that
+ * screen readers announce the label for the password field that has focus.
+ * @param label for the password fields
+ */
+ protected void setPasswordLabel(Label label)
+ {
+ label.labelForProperty().bind(Bindings.when(getShowPasswordCheckBox().selectedProperty())
+ .then((Node)getUnMaskedPasswordTextField())
+ .otherwise(getMaskedPasswordTextField()));
+ }
+
protected IntegerTextField getPortTextField()
{
if(mPortTextField == null)
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyCallEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyCallEditor.java
index 24330495..7918021d 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyCallEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyCallEditor.java
@@ -22,6 +22,7 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallBroadcaster;
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyCallConfiguration;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
@@ -133,6 +134,7 @@ public class BroadcastifyCallEditor extends AbstractBroadcastEditor modifiedProperty().set(true));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java
index 01477fab..0484c97b 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/BroadcastifyStreamEditor.java
@@ -22,6 +22,7 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.broadcastify.BroadcastifyFeedConfiguration;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
@@ -100,6 +101,7 @@ public class BroadcastifyStreamEditor extends AbstractStreamEditor modifiedProperty().set(true));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/IcecastStreamEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/IcecastStreamEditor.java
index 11fc08a6..dc4f4a05 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/IcecastStreamEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/IcecastStreamEditor.java
@@ -24,6 +24,7 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.icecast.IcecastConfiguration;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
@@ -108,6 +109,7 @@ public abstract class IcecastStreamEditor extends AbstractStreamEditor modifiedProperty().set(true));
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/RdioScannerEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/RdioScannerEditor.java
index a8207c6a..485b5ffe 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/RdioScannerEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/RdioScannerEditor.java
@@ -131,6 +131,7 @@ public class RdioScannerEditor extends AbstractBroadcastEditor modifiedProperty().set(true));
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/ShoutcastV2StreamEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/ShoutcastV2StreamEditor.java
index efd81738..e7787e87 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/ShoutcastV2StreamEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/ShoutcastV2StreamEditor.java
@@ -25,6 +25,7 @@ package io.github.dsheirer.gui.playlist.streaming;
import io.github.dsheirer.audio.broadcast.BroadcastServerType;
import io.github.dsheirer.audio.broadcast.shoutcast.v2.ShoutcastV2Configuration;
+import io.github.dsheirer.gui.control.AccessibleToggleSwitch;
import io.github.dsheirer.gui.control.IntegerTextField;
import io.github.dsheirer.playlist.PlaylistManager;
import javafx.geometry.HPos;
@@ -113,6 +114,7 @@ public class ShoutcastV2StreamEditor extends AbstractStreamEditor modifiedProperty().set(true));
}
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamAliasSelectionEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamAliasSelectionEditor.java
index 54838849..4cdd477f 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamAliasSelectionEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamAliasSelectionEditor.java
@@ -39,6 +39,7 @@ import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
+import javafx.scene.control.Tooltip;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
@@ -153,6 +154,7 @@ public class StreamAliasSelectionEditor extends VBox
mSearchBox.setSpacing(5);
Label searchLabel = new Label("Search:");
+ searchLabel.setLabelFor(getSearchField());
searchLabel.setAlignment(Pos.CENTER_RIGHT);
mSearchBox.getChildren().addAll(searchLabel, getSearchField());
}
@@ -212,6 +214,7 @@ public class StreamAliasSelectionEditor extends VBox
if(mAvailableAliasTableView == null)
{
mAvailableAliasTableView = new TableView<>();
+ mAvailableAliasTableView.setAccessibleText("Available aliases");
mAvailableAliasTableView.setMaxHeight(Double.MAX_VALUE);
mAvailableAliasTableView.setDisable(true);
mAvailableAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
@@ -256,6 +259,7 @@ public class StreamAliasSelectionEditor extends VBox
if(mSelectedAliasTableView == null)
{
mSelectedAliasTableView = new TableView<>();
+ mSelectedAliasTableView.setAccessibleText("Aliases selected for this stream");
mSelectedAliasTableView.setMaxHeight(Double.MAX_VALUE);
mSelectedAliasTableView.setDisable(true);
mSelectedAliasTableView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
@@ -303,6 +307,8 @@ public class StreamAliasSelectionEditor extends VBox
mAddButton.setDisable(true);
mAddButton.setMaxWidth(Double.MAX_VALUE);
mAddButton.setGraphic(new IconNode(FontAwesome.ANGLE_RIGHT));
+ mAddButton.setAccessibleText("Add selected alias to stream");
+ mAddButton.setTooltip(new Tooltip("Add selected alias to stream"));
mAddButton.setAlignment(Pos.CENTER);
mAddButton.setOnAction(event -> {
Alias selectedAlias = getAvailableAliasTableView().getSelectionModel().getSelectedItem();
@@ -327,6 +333,8 @@ public class StreamAliasSelectionEditor extends VBox
mAddAllButton.setDisable(true);
mAddAllButton.setMaxWidth(Double.MAX_VALUE);
mAddAllButton.setGraphic(new IconNode(FontAwesome.ANGLE_DOUBLE_RIGHT));
+ mAddAllButton.setAccessibleText("Add all selected aliases to stream");
+ mAddAllButton.setTooltip(new Tooltip("Add all selected aliases to stream"));
mAddAllButton.setAlignment(Pos.CENTER);
mAddAllButton.setOnAction(new EventHandler()
{
@@ -361,6 +369,8 @@ public class StreamAliasSelectionEditor extends VBox
mRemoveButton.setDisable(true);
mRemoveButton.setMaxWidth(Double.MAX_VALUE);
mRemoveButton.setGraphic(new IconNode(FontAwesome.ANGLE_LEFT));
+ mRemoveButton.setAccessibleText("Remove selected alias from stream");
+ mRemoveButton.setTooltip(new Tooltip("Remove selected alias from stream"));
mRemoveButton.setAlignment(Pos.CENTER);
mRemoveButton.setOnAction(event -> {
Alias selectedAlias = getSelectedAliasTableView().getSelectionModel().getSelectedItem();
@@ -385,6 +395,8 @@ public class StreamAliasSelectionEditor extends VBox
mRemoveAllButton.setDisable(true);
mRemoveAllButton.setMaxWidth(Double.MAX_VALUE);
mRemoveAllButton.setGraphic(new IconNode(FontAwesome.ANGLE_DOUBLE_LEFT));
+ mRemoveAllButton.setAccessibleText("Remove all selected aliases from stream");
+ mRemoveAllButton.setTooltip(new Tooltip("Remove all selected aliases from stream"));
mRemoveAllButton.setAlignment(Pos.CENTER);
mRemoveAllButton.setOnAction(event -> {
List selectedAliases =
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamingEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamingEditor.java
index 44bedc2e..65ea5dd6 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamingEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/StreamingEditor.java
@@ -396,6 +396,7 @@ public class StreamingEditor extends SplitPane
if(mConfiguredBroadcastTableView == null)
{
mConfiguredBroadcastTableView = new TableView<>();
+ mConfiguredBroadcastTableView.setAccessibleText("Streams");
mConfiguredBroadcastTableView.setPlaceholder(new Label("Click the New button to create a new " +
"audio streaming configuration"));
mConfiguredBroadcastTableView.setItems(mPlaylistManager.getBroadcastModel().getConfiguredBroadcasts());
@@ -414,12 +415,14 @@ public class StreamingEditor extends SplitPane
if(empty || item == null || !item)
{
setGraphic(null);
+ setAccessibleText(empty ? null : "No");
}
else
{
IconNode iconNode = new IconNode(FontAwesome.CHECK);
iconNode.setFill(Color.GREEN);
setGraphic(iconNode);
+ setAccessibleText("Yes");
}
}
};
diff --git a/src/main/java/io/github/dsheirer/gui/playlist/streaming/UnknownStreamEditor.java b/src/main/java/io/github/dsheirer/gui/playlist/streaming/UnknownStreamEditor.java
index 7aa7c1e5..0618f258 100644
--- a/src/main/java/io/github/dsheirer/gui/playlist/streaming/UnknownStreamEditor.java
+++ b/src/main/java/io/github/dsheirer/gui/playlist/streaming/UnknownStreamEditor.java
@@ -72,6 +72,7 @@ public class UnknownStreamEditor extends AbstractStreamEditor