package dasherJava.gui;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.text.ParseException;
import java.util.List;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTabbedPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.ScrollPaneConstants;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import dasherJava.DasherJava;
import dasherJava.core.alphabets.xml.Alphabet;
import dasherJava.core.alphabets.xml.Colors;
import dasherJava.core.input.JoystickInput;
import dasherJava.core.output.AccessibilityActionsListTarget;
import dasherJava.core.settings.Settings;
public class SettingsDialog extends JDialog {
private final Frame owner;
private volatile boolean isCurrentlyShowing = false; //field to be read by other threads
private final JLabel configurationLabel = new JLabel();
private final JSpinner frameRateSpinner = createIntSpinner(1, 10000);
private final JComboBox<String> antialiasingComboBox = new JComboBox<>(new String[] {"On", "Default", "Off"});
private final JComboBox<String> textAntialiasingComboBox = new JComboBox<>(new String[] {"On", "GASP", "Default",
"Off"});
private final JSpinner spaceBehindNodesSpinner = createIntSpinner(0, 10000);
private final JComboBox<String> windowAlwaysOnTopComboBox = new JComboBox<>(new String[] {"Always",
"When using external text output", "Never"});
private final JComboBox<String> windowUnfocusableComboBox = new JComboBox<>(new String[] {"Always",
"When using external text output", "Never"});
private final JCheckBox rememberWindowPositionCheckBox = new JCheckBox("Remember window position");
private final JCheckBox rememberWindowSizeCheckBox = new JCheckBox("Remember window size");
private final JCheckBox showStatusBarCheckBox = new JCheckBox("Show status bar");
private final JComboBox<String> confirmExitComboBox = new JComboBox<>(new String[] {"Always",
"When text has been entered", "Never"});
private final JComboBox<String> alphabetNameComboBox = new JComboBox<>();
private final JSpinner maxAlphabetHistoryLengthSpinner = createIntSpinner(1, 100);
private final JComboBox<String> colorPaletteNameOverrideComboBox = new JComboBox<>();
private final JRadioButton alphabetDefaultRadioButton = new JRadioButton("Alphabet default");
private final JRadioButton leftToRightRadioButton = new JRadioButton("Left-to-right");
private final JRadioButton rightToLeftRadioButton = new JRadioButton("Right-to-left");
private final JRadioButton topToBottomRadioButton = new JRadioButton("Top-to-bottom");
private final JRadioButton bottomToTopRadioButton = new JRadioButton("Bottom-to-top");
private final JSpinner ppmMaxOrderSpinner = createIntSpinner(0, 100);
private final JSpinner ppmAlphaSpinner = createIntSpinner(0, 100);
private final JSpinner ppmBetaSpinner = createIntSpinner(0, 100);
private final JSpinner ppmUniformSpinner = createIntSpinner(0, 1000);
private final JRadioButton internalTextOutputRadioButton = new JRadioButton("Built-in text area");
private final JRadioButton externalTextOutputRadioButton = new JRadioButton("External (AT-SPI)");
private final JRadioButton mouseInputRadioButton = new JRadioButton("Mouse input");
private final JRadioButton globalMouseInputRadioButton = new JRadioButton("Global mouse input");
private final JRadioButton joystickInputRadioButton = new JRadioButton("Joystick input");
private final JRadioButton socketInputRadioButton = new JRadioButton("Socket input");
private final JCheckBox startStopOnLeftClickCheckBox = new JCheckBox("Start/stop on left click");
private final JSpinner movementSpeedSpinner = createDoubleSpinner(0.0, 10.0, 0.001);
private final JCheckBox onlineLearningCheckBox = new JCheckBox("Learn while writing");
private final JSpinner onlineLearningDelaySpinner = createIntSpinner(0, 100000);
private final JSpinner maxNumberOfNodesSpinner = createIntSpinner(0, 100000);
private final JSpinner maxNumberOfOldRootNodesSpinner = createIntSpinner(0, 100000);
private final JSpinner minGainForNodeTradeSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner minNodeSizeForRenderingSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner minGroupSizeForRenderingSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner dropRootNodeMarginTopSpinner = createDoubleSpinner(0.0, 10000.0, 0.01);
private final JSpinner dropRootNodeMarginBottomSpinner = createDoubleSpinner(0.0, 10000.0, 0.01);
private final JSpinner dropRootNodeMarginSizeSpinner = createDoubleSpinner(0.0, 10000.0, 0.01);
private final JSpinner zoomOutMarginTopSpinner = createDoubleSpinner(0.0, 10000.0, 0.01);
private final JSpinner zoomOutMarginBottomSpinner = createDoubleSpinner(0.0, 10000.0, 0.01);
private final JSpinner zoomOutMarginSizeSpinner = createDoubleSpinner(0.0, 10000.0, 0.01);
private final JSpinner normalFontSizeSpinner = createIntSpinner(0, 10000);
private final JSpinner mediumFontSizeSpinner = createIntSpinner(0, 10000);
private final JSpinner highlightFontSizeSpinner = createIntSpinner(0, 10000);
private final JSpinner mediumFontSizeMarginBeforeSpinner = createDoubleSpinner(0.0, 10000.0, 1.0);
private final JSpinner mediumFontSizeMarginAfterSpinner = createDoubleSpinner(0.0, 10000.0, 1.0);
private final JSpinner nodeOutlineThicknessSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner inputLineThicknessSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner crosshairThicknessSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner crosshairWidthSpinner = createDoubleSpinner(0.0, 1.0, 0.01);
private final JRadioButton noneStartStopRadioButton = new JRadioButton("None");
private final JRadioButton circleStartStopRadioButton = new JRadioButton("Center circle");
private final JRadioButton twoBoxesStartStopRadioButton = new JRadioButton("Two boxes");
private final JSpinner circleSizeSpinner = createDoubleSpinner(0.0, 1.0, 0.01);
private final JSpinner circleOutlineThicknessSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner circleOutlineThicknessHoldSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner circleHoldStartTimeSpinner = createIntSpinner(0, 1000000);
private final JSpinner circleHoldStopTimeSpinner = createIntSpinner(0, 1000000);
private final JSpinner firstBoxSizeSpinner = createDoubleSpinner(0.0, 1.0, 0.01);
private final JSpinner firstBoxLeftInsetSpinner = createDoubleSpinner(0.0, 1.0, 0.01);
private final JSpinner firstBoxRightInsetSpinner = createDoubleSpinner(0.0, 1.0, 0.01);
private final JSpinner firstBoxOutlineThicknessSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner firstBoxOutlineThicknessHoldSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner firstBoxHoldTimeSpinner = createIntSpinner(0, 1000000);
private final JSpinner firstBoxResetTimeSpinner = createIntSpinner(0, 1000000);
private final JSpinner secondBoxSizeSpinner = createDoubleSpinner(0.0, 1.0, 0.01);
private final JSpinner secondBoxLeftInsetSpinner = createDoubleSpinner(0.0, 1.0, 0.01);
private final JSpinner secondBoxRightInsetSpinner = createDoubleSpinner(0.0, 1.0, 0.01);
private final JSpinner secondBoxOutlineThicknessSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner secondBoxOutlineThicknessHoldSpinner = createDoubleSpinner(0.0, 10000.0, 0.1);
private final JSpinner secondBoxHoldTimeSpinner = createIntSpinner(0, 1000000);
private final JSpinner socketOutputPortSpinner = createIntSpinner(0, 65535);
private final JTextField joystickInputNameTextField = createTextField();
private final JTextField joystickInputComponentNameXTextField = createTextField();
private final JSpinner joystickInputSensitivityXSpinner = createDoubleSpinner(-1000000000.0, 1000000000.0, 0.1);
private final JSpinner joystickInputOffsetXSpinner = createDoubleSpinner(-1000000000.0, 1000000000.0, 0.01);
private final JTextField joystickInputComponentNameYTextField = createTextField();
private final JSpinner joystickInputSensitivityYSpinner = createDoubleSpinner(-1000000000.0, 1000000000.0, 0.1);
private final JSpinner joystickInputOffsetYSpinner = createDoubleSpinner(-1000000000.0, 1000000000.0, 0.01);
private final JTextField joystickInputComponentNameStartStopTextField = createTextField();
private final JSpinner socketInputPortSpinner = createIntSpinner(0, 65535);
private final JTextField socketInputLabelXTextField = createTextField();
private final JSpinner socketInputMinXSpinner = createDoubleSpinner(-1000000000.0, 1000000000.0, 0.1);
private final JSpinner socketInputMaxXSpinner = createDoubleSpinner(-1000000000.0, 1000000000.0, 0.1);
private final JTextField socketInputLabelYTextField = createTextField();
private final JSpinner socketInputMinYSpinner = createDoubleSpinner(-1000000000.0, 1000000000.0, 0.1);
private final JSpinner socketInputMaxYSpinner = createDoubleSpinner(-1000000000.0, 1000000000.0, 0.1);
private final JTextField socketInputLabelStartStopTextField = createTextField();
//fields to remember settings that don't (yet) have GUI elements
private int windowX;
private int windowY;
private int windowWidth;
private int windowHeight;
private List<String> alphabetHistory;
private float minNodeSizeForExpansion;
private float maxNodeSizeForCollapse;
//fields for settings in margins dialog (to enable canceling)
private float dropRootNodeMarginTop;
private float dropRootNodeMarginBottom;
private float dropRootNodeMarginSize;
private float zoomOutMarginTop;
private float zoomOutMarginBottom;
private float zoomOutMarginSize;
//fields for settings in circle dialog (to enable canceling)
private float circleSize;
private float circleOutlineThickness;
private float circleOutlineThicknessHold;
private int circleHoldStartTime;
private int circleHoldStopTime;
//fields for settings in two boxes dialog (to enable canceling)
private float firstBoxSize;
private float firstBoxLeftInset;
private float firstBoxRightInset;
private float firstBoxOutlineThickness;
private float firstBoxOutlineThicknessHold;
private int firstBoxHoldTime;
private int firstBoxResetTime;
private float secondBoxSize;
private float secondBoxLeftInset;
private float secondBoxRightInset;
private float secondBoxOutlineThickness;
private float secondBoxOutlineThicknessHold;
private int secondBoxHoldTime;
//fields for settings in joystick input dialog (to enable canceling)
private String joystickInputName;
private String joystickInputComponentNameX;
private float joystickInputSensitivityX;
private float joystickInputOffsetX;
private String joystickInputComponentNameY;
private float joystickInputSensitivityY;
private float joystickInputOffsetY;
private String joystickInputComponentNameStartStop;
//fields for settings in socket input dialog (to enable canceling)
private int socketInputPort;
private String socketInputLabelX;
private float socketInputMinX;
private float socketInputMaxX;
private String socketInputLabelY;
private float socketInputMinY;
private float socketInputMaxY;
private String socketInputLabelStartStop;
public SettingsDialog(Frame owner) {
super(owner, "Settings", true);
this.owner=owner;
setLayout(new BorderLayout());
addComponentListener(new ComponentAdapter() {
@Override
public void componentHidden(ComponentEvent e) {
isCurrentlyShowing=false;
}
@Override
public void componentShown(ComponentEvent e) {
isCurrentlyShowing=true;
}
});
JPanel configurationPanel = new JPanel(null);
configurationPanel.setLayout(new BoxLayout(configurationPanel, BoxLayout.LINE_AXIS));
configurationPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
configurationPanel.add(configurationLabel);
configurationPanel.add(Box.createHorizontalGlue());
JButton changeConfigurationButton = new JButton("Change...");
changeConfigurationButton.addActionListener(e -> {
setVisible(false);
DasherJava.showConfigurationChooser();
});
configurationPanel.add(changeConfigurationButton);
add(configurationPanel, BorderLayout.PAGE_START);
//Joystick input dialog
JDialog joystickInputDialog = new JDialog(this, "Joystick Input", true);
joystickInputDialog.setLayout(new BorderLayout());
JPanel joystickInputDialogContentPanel = new JPanel(null);
joystickInputDialogContentPanel.setLayout(new BoxLayout(joystickInputDialogContentPanel, BoxLayout.PAGE_AXIS));
joystickInputDialogContentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JPanel joystickInputDialogTablePanel = new JPanel(new BorderLayout(3, 0));
joystickInputDialogTablePanel.add(createOneColumnGridPanel("Joystick name:", "X axis name:",
"X axis sensitivity:", "X axis offset:", "Y axis name:", "Y axis sensitivity:", "Y axis offset:",
"Start/stop button name:"), BorderLayout.LINE_START);
joystickInputDialogTablePanel.add(createOneColumnGridPanel(joystickInputNameTextField,
joystickInputComponentNameXTextField, joystickInputSensitivityXSpinner, joystickInputOffsetXSpinner,
joystickInputComponentNameYTextField, joystickInputSensitivityYSpinner, joystickInputOffsetYSpinner,
joystickInputComponentNameStartStopTextField), BorderLayout.LINE_END);
limitMaximumHeight(joystickInputDialogTablePanel);
joystickInputDialogContentPanel.add(joystickInputDialogTablePanel);
joystickInputDialogContentPanel.add(createUneditableTextArea("Use the button below to get a list of all "
+"connected controllers and their components. Leave fields empty if you don't want to use that "
+"axis/button. Sensitivities are multiplicative factors, while offsets are additive. A negative "
+"sensitivity thus inverts an axis. Offsets are applied after sensitivities, and the final result "
+"should be in range [-1.0, 1.0].", 7, 0));
joystickInputDialog.add(joystickInputDialogContentPanel, BorderLayout.CENTER);
JPanel joystickInputDialogButtonsPanel = new JPanel(null);
joystickInputDialogButtonsPanel.setLayout(new BoxLayout(joystickInputDialogButtonsPanel, BoxLayout.LINE_AXIS));
joystickInputDialogButtonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JDialog controllersInfoDialog = new JDialog(joystickInputDialog, "Connected Controllers", true);
controllersInfoDialog.setLayout(new BorderLayout());
JPanel controllersInfoDialogContentPanel = new JPanel(new BorderLayout());
controllersInfoDialogContentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JTextArea controllersInfoTextArea = createUneditableTextArea("", 40, 40);
JScrollPane controllersInfoScrollPane = new JScrollPane(controllersInfoTextArea,
ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
controllersInfoDialogContentPanel.add(controllersInfoScrollPane, BorderLayout.CENTER);
controllersInfoDialog.add(controllersInfoDialogContentPanel, BorderLayout.CENTER);
JPanel controllersInfoDialogButtonsPanel = new JPanel(null);
controllersInfoDialogButtonsPanel.setLayout(new BoxLayout(controllersInfoDialogButtonsPanel,
BoxLayout.LINE_AXIS));
controllersInfoDialogButtonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton controllersInfoDialogOkButton = new JButton("OK");
controllersInfoDialogOkButton.addActionListener(e -> controllersInfoDialog.setVisible(false));
controllersInfoDialogButtonsPanel.add(Box.createHorizontalGlue());
controllersInfoDialogButtonsPanel.add(controllersInfoDialogOkButton);
controllersInfoDialog.add(controllersInfoDialogButtonsPanel, BorderLayout.PAGE_END);
controllersInfoDialog.pack();
JButton listControllersButton = new JButton("List controllers...");
listControllersButton.addActionListener(e -> {
controllersInfoTextArea.setText(JoystickInput.getConnectedControllersInfo());
controllersInfoDialog.setLocationRelativeTo(joystickInputDialog);
controllersInfoDialog.setVisible(true);
});
joystickInputDialogButtonsPanel.add(listControllersButton);
joystickInputDialogButtonsPanel.add(Box.createHorizontalGlue());
JButton joystickInputDialogCancelButton = new JButton("Cancel");
joystickInputDialogCancelButton.addActionListener(e -> joystickInputDialog.setVisible(false));
joystickInputDialogButtonsPanel.add(joystickInputDialogCancelButton);
joystickInputDialogButtonsPanel.add(Box.createRigidArea(new Dimension(5, 0)));
JButton joystickInputDialogOkButton = new JButton("OK");
joystickInputDialogOkButton.addActionListener(e -> {
joystickInputName=joystickInputNameTextField.getText();
joystickInputComponentNameX=joystickInputComponentNameXTextField.getText();
joystickInputSensitivityX=readDoubleSpinner(joystickInputSensitivityXSpinner);
joystickInputOffsetX=readDoubleSpinner(joystickInputOffsetXSpinner);
joystickInputComponentNameY=joystickInputComponentNameYTextField.getText();
joystickInputSensitivityY=readDoubleSpinner(joystickInputSensitivityYSpinner);
joystickInputOffsetY=readDoubleSpinner(joystickInputOffsetYSpinner);
joystickInputComponentNameStartStop=joystickInputComponentNameStartStopTextField.getText();
joystickInputDialog.setVisible(false);
});
joystickInputDialogButtonsPanel.add(joystickInputDialogOkButton);
joystickInputDialog.add(joystickInputDialogButtonsPanel, BorderLayout.PAGE_END);
joystickInputDialog.pack();
//Socket input dialog
JDialog socketInputDialog = new JDialog(this, "Socket Input", true);
socketInputDialog.setLayout(new BorderLayout());
JPanel socketInputDialogContentPanel = new JPanel(null);
socketInputDialogContentPanel.setLayout(new BoxLayout(socketInputDialogContentPanel, BoxLayout.PAGE_AXIS));
socketInputDialogContentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JPanel socketInputDialogTablePanel = new JPanel(new BorderLayout(3, 0));
socketInputDialogTablePanel.add(createOneColumnGridPanel("TCP port:", "X label:", "X minimum:",
"X maximum:", "Y label:", "Y minimum:", "Y maximum:", "Start/stop label:"), BorderLayout.LINE_START);
socketInputDialogTablePanel.add(createOneColumnGridPanel(socketInputPortSpinner, socketInputLabelXTextField,
socketInputMinXSpinner, socketInputMaxXSpinner, socketInputLabelYTextField, socketInputMinYSpinner,
socketInputMaxYSpinner, socketInputLabelStartStopTextField), BorderLayout.LINE_END);
limitMaximumHeight(socketInputDialogTablePanel);
socketInputDialogContentPanel.add(socketInputDialogTablePanel);
socketInputDialogContentPanel.add(createUneditableTextArea("Socket input works with lines of text, each "
+"consisting of a label (one or more characters) followed by a floating point value. The minimum "
+"and maximum value for each axis can be set above. Swapping them will thus invert the input.", 6, 0));
socketInputDialog.add(socketInputDialogContentPanel, BorderLayout.CENTER);
JPanel socketInputDialogButtonsPanel = new JPanel(null);
socketInputDialogButtonsPanel.setLayout(new BoxLayout(socketInputDialogButtonsPanel, BoxLayout.LINE_AXIS));
socketInputDialogButtonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
socketInputDialogButtonsPanel.add(Box.createHorizontalGlue());
JButton socketInputDialogCancelButton = new JButton("Cancel");
socketInputDialogCancelButton.addActionListener(e -> socketInputDialog.setVisible(false));
socketInputDialogButtonsPanel.add(socketInputDialogCancelButton);
socketInputDialogButtonsPanel.add(Box.createRigidArea(new Dimension(5, 0)));
JButton socketInputDialogOkButton = new JButton("OK");
socketInputDialogOkButton.addActionListener(e -> {
socketInputPort=readIntSpinner(socketInputPortSpinner);
socketInputLabelX=socketInputLabelXTextField.getText();
socketInputMinX=readDoubleSpinner(socketInputMinXSpinner);
socketInputMaxX=readDoubleSpinner(socketInputMaxXSpinner);
socketInputLabelY=socketInputLabelYTextField.getText();
socketInputMinY=readDoubleSpinner(socketInputMinYSpinner);
socketInputMaxY=readDoubleSpinner(socketInputMaxYSpinner);
socketInputLabelStartStop=socketInputLabelStartStopTextField.getText();
socketInputDialog.setVisible(false);
});
socketInputDialogButtonsPanel.add(socketInputDialogOkButton);
socketInputDialog.add(socketInputDialogButtonsPanel, BorderLayout.PAGE_END);
socketInputDialog.pack();
//Margins dialog
JDialog marginsDialog = new JDialog(this, "Margins", true);
marginsDialog.setLayout(new BorderLayout());
JPanel marginsDialogContentPanel = new JPanel(null);
marginsDialogContentPanel.setLayout(new BoxLayout(marginsDialogContentPanel, BoxLayout.PAGE_AXIS));
marginsDialogContentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JPanel marginsDialogTablePanel = new JPanel(new BorderLayout(3, 0));
marginsDialogTablePanel.add(createOneColumnGridPanel("Drop root node margin top:",
"Drop root node margin bottom:", "Drop root node margin size:", "Zoom out margin top:",
"Zoom out margin bottom:", "Zoom out margin size:"), BorderLayout.LINE_START);
marginsDialogTablePanel.add(createOneColumnGridPanel(dropRootNodeMarginTopSpinner,
dropRootNodeMarginBottomSpinner, dropRootNodeMarginSizeSpinner, zoomOutMarginTopSpinner,
zoomOutMarginBottomSpinner, zoomOutMarginSizeSpinner), BorderLayout.LINE_END);
limitMaximumHeight(marginsDialogTablePanel);
marginsDialogContentPanel.add(marginsDialogTablePanel);
marginsDialogContentPanel.add(createUneditableTextArea("This dialog contains extra margin values that are "
+"required for numerical stability of some computations. You should normally not change (and "
+"especially not decrease) these values.", 4, 0));
marginsDialog.add(marginsDialogContentPanel, BorderLayout.CENTER);
JPanel marginsDialogButtonsPanel = new JPanel(null);
marginsDialogButtonsPanel.setLayout(new BoxLayout(marginsDialogButtonsPanel, BoxLayout.LINE_AXIS));
marginsDialogButtonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
marginsDialogButtonsPanel.add(Box.createHorizontalGlue());
JButton marginsDialogCancelButton = new JButton("Cancel");
marginsDialogCancelButton.addActionListener(e -> marginsDialog.setVisible(false));
marginsDialogButtonsPanel.add(marginsDialogCancelButton);
marginsDialogButtonsPanel.add(Box.createRigidArea(new Dimension(5, 0)));
JButton marginsDialogOkButton = new JButton("OK");
marginsDialogOkButton.addActionListener(e -> {
dropRootNodeMarginTop=readDoubleSpinner(dropRootNodeMarginTopSpinner);
dropRootNodeMarginBottom=readDoubleSpinner(dropRootNodeMarginBottomSpinner);
dropRootNodeMarginSize=readDoubleSpinner(dropRootNodeMarginSizeSpinner);
zoomOutMarginTop=readDoubleSpinner(zoomOutMarginTopSpinner);
zoomOutMarginBottom=readDoubleSpinner(zoomOutMarginBottomSpinner);
zoomOutMarginSize=readDoubleSpinner(zoomOutMarginSizeSpinner);
marginsDialog.setVisible(false);
});
marginsDialogButtonsPanel.add(marginsDialogOkButton);
marginsDialog.add(marginsDialogButtonsPanel, BorderLayout.PAGE_END);
marginsDialog.pack();
//Circle dialog
JDialog circleDialog = new JDialog(this, "Center Circle", true);
circleDialog.setLayout(new BorderLayout());
JPanel circleDialogContentPanel = new JPanel(null);
circleDialogContentPanel.setLayout(new BoxLayout(circleDialogContentPanel, BoxLayout.PAGE_AXIS));
circleDialogContentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JPanel circleDialogTablePanel = new JPanel(new BorderLayout(3, 0));
circleDialogTablePanel.add(createOneColumnGridPanel("Size:", "Outline thickness:",
"Hold outline thickness:", "Start delay (ms):", "Stop delay (ms):"), BorderLayout.LINE_START);
circleDialogTablePanel.add(createOneColumnGridPanel(circleSizeSpinner, circleOutlineThicknessSpinner,
circleOutlineThicknessHoldSpinner, circleHoldStartTimeSpinner, circleHoldStopTimeSpinner),
BorderLayout.LINE_END);
limitMaximumHeight(circleDialogTablePanel);
circleDialogContentPanel.add(circleDialogTablePanel);
circleDialog.add(circleDialogContentPanel, BorderLayout.CENTER);
JPanel circleDialogButtonsPanel = new JPanel(null);
circleDialogButtonsPanel.setLayout(new BoxLayout(circleDialogButtonsPanel, BoxLayout.LINE_AXIS));
circleDialogButtonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
circleDialogButtonsPanel.add(Box.createHorizontalGlue());
JButton circleDialogCancelButton = new JButton("Cancel");
circleDialogCancelButton.addActionListener(e -> circleDialog.setVisible(false));
circleDialogButtonsPanel.add(circleDialogCancelButton);
circleDialogButtonsPanel.add(Box.createRigidArea(new Dimension(5, 0)));
JButton circleDialogOkButton = new JButton("OK");
circleDialogOkButton.addActionListener(e -> {
circleSize=readDoubleSpinner(circleSizeSpinner);
circleOutlineThickness=readDoubleSpinner(circleOutlineThicknessSpinner);
circleOutlineThicknessHold=readDoubleSpinner(circleOutlineThicknessHoldSpinner);
circleHoldStartTime=readIntSpinner(circleHoldStartTimeSpinner);
circleHoldStopTime=readIntSpinner(circleHoldStopTimeSpinner);
circleDialog.setVisible(false);
});
circleDialogButtonsPanel.add(circleDialogOkButton);
circleDialog.add(circleDialogButtonsPanel, BorderLayout.PAGE_END);
circleDialog.pack();
//Two boxes dialog
JDialog twoBoxesDialog = new JDialog(this, "Two Boxes", true);
twoBoxesDialog.setLayout(new BorderLayout());
JPanel twoBoxesDialogContentPanel = new JPanel(null);
twoBoxesDialogContentPanel.setLayout(new BoxLayout(twoBoxesDialogContentPanel, BoxLayout.PAGE_AXIS));
twoBoxesDialogContentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JPanel twoBoxesDialogGridPanel = new JPanel(new GridLayout(1, 0));
JPanel twoBoxesDialogFirstBoxPanel = new JPanel(new BorderLayout(3, 0));
twoBoxesDialogFirstBoxPanel.setBorder(BorderFactory.createTitledBorder("First Box"));
twoBoxesDialogFirstBoxPanel.add(createOneColumnGridPanel("Height:", "Left inset:", "Right inset:",
"Outline thickness:", "Hold outline thickness:", "Second box delay (ms):", "Reset delay (ms):"),
BorderLayout.LINE_START);
twoBoxesDialogFirstBoxPanel.add(createOneColumnGridPanel(firstBoxSizeSpinner, firstBoxLeftInsetSpinner,
firstBoxRightInsetSpinner, firstBoxOutlineThicknessSpinner, firstBoxOutlineThicknessHoldSpinner,
firstBoxHoldTimeSpinner, firstBoxResetTimeSpinner), BorderLayout.LINE_END);
twoBoxesDialogGridPanel.add(twoBoxesDialogFirstBoxPanel);
JPanel twoBoxesDialogSecondBoxPanel = new JPanel(new BorderLayout(3, 0));
twoBoxesDialogSecondBoxPanel.setBorder(BorderFactory.createTitledBorder("Second Box"));
twoBoxesDialogSecondBoxPanel.add(createOneColumnGridPanel("Height:", "Left inset:", "Right inset:",
"Outline thickness:", "Hold outline thickness:", "Start delay (ms):", ""),
BorderLayout.LINE_START);
twoBoxesDialogSecondBoxPanel.add(createOneColumnGridPanel(secondBoxSizeSpinner, secondBoxLeftInsetSpinner,
secondBoxRightInsetSpinner, secondBoxOutlineThicknessSpinner, secondBoxOutlineThicknessHoldSpinner,
secondBoxHoldTimeSpinner, new JPanel()), BorderLayout.LINE_END);
twoBoxesDialogGridPanel.add(twoBoxesDialogSecondBoxPanel);
limitMaximumHeight(twoBoxesDialogGridPanel);
twoBoxesDialogContentPanel.add(twoBoxesDialogGridPanel);
twoBoxesDialog.add(twoBoxesDialogContentPanel, BorderLayout.CENTER);
JPanel twoBoxesDialogButtonsPanel = new JPanel(null);
twoBoxesDialogButtonsPanel.setLayout(new BoxLayout(twoBoxesDialogButtonsPanel, BoxLayout.LINE_AXIS));
twoBoxesDialogButtonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
twoBoxesDialogButtonsPanel.add(Box.createHorizontalGlue());
JButton twoBoxesDialogCancelButton = new JButton("Cancel");
twoBoxesDialogCancelButton.addActionListener(e -> twoBoxesDialog.setVisible(false));
twoBoxesDialogButtonsPanel.add(twoBoxesDialogCancelButton);
twoBoxesDialogButtonsPanel.add(Box.createRigidArea(new Dimension(5, 0)));
JButton twoBoxesDialogOkButton = new JButton("OK");
twoBoxesDialogOkButton.addActionListener(e -> {
firstBoxSize=readDoubleSpinner(firstBoxSizeSpinner);
firstBoxLeftInset=readDoubleSpinner(firstBoxLeftInsetSpinner);
firstBoxRightInset=readDoubleSpinner(firstBoxRightInsetSpinner);
firstBoxOutlineThickness=readDoubleSpinner(firstBoxOutlineThicknessSpinner);
firstBoxOutlineThicknessHold=readDoubleSpinner(firstBoxOutlineThicknessHoldSpinner);
firstBoxHoldTime=readIntSpinner(firstBoxHoldTimeSpinner);
firstBoxResetTime=readIntSpinner(firstBoxResetTimeSpinner);
secondBoxSize=readDoubleSpinner(secondBoxSizeSpinner);
secondBoxLeftInset=readDoubleSpinner(secondBoxLeftInsetSpinner);
secondBoxRightInset=readDoubleSpinner(secondBoxRightInsetSpinner);
secondBoxOutlineThickness=readDoubleSpinner(secondBoxOutlineThicknessSpinner);
secondBoxOutlineThicknessHold=readDoubleSpinner(secondBoxOutlineThicknessHoldSpinner);
secondBoxHoldTime=readIntSpinner(secondBoxHoldTimeSpinner);
twoBoxesDialog.setVisible(false);
});
twoBoxesDialogButtonsPanel.add(twoBoxesDialogOkButton);
twoBoxesDialog.add(twoBoxesDialogButtonsPanel, BorderLayout.PAGE_END);
twoBoxesDialog.pack();
//
JTabbedPane tabbedPane = new JTabbedPane() {
@Override
public Dimension getPreferredSize() { //Make sure that the tabbed pane is wide enough to show all tabs.
//Adapted from https://stackoverflow.com/a/42537586
Rectangle lastTabBounds = getBoundsAt(getTabCount()-1);
int tabsWidth = lastTabBounds.x+lastTabBounds.width+5; //some extra space due to margins and insets
Dimension preferred = super.getPreferredSize();
preferred.width=Math.max(preferred.width, tabsWidth);
return preferred;
}
};
tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
JPanel languagePanel = new JPanel(null);
languagePanel.setLayout(new BoxLayout(languagePanel, BoxLayout.PAGE_AXIS));
JPanel alphabetPanel = new JPanel(new BorderLayout(0, 1));
alphabetPanel.setBorder(BorderFactory.createTitledBorder("Alphabet"));
alphabetPanel.add(alphabetNameComboBox, BorderLayout.PAGE_START);
JPanel alphabetHistoryPanel = new JPanel(new BorderLayout());
alphabetHistoryPanel.add(createOneColumnGridPanel("Maximum alphabet history length:"),
BorderLayout.LINE_START);
alphabetHistoryPanel.add(createOneColumnGridPanel(maxAlphabetHistoryLengthSpinner),
BorderLayout.LINE_END);
alphabetPanel.add(alphabetHistoryPanel, BorderLayout.PAGE_END);
limitMaximumHeight(alphabetPanel);
languagePanel.add(alphabetPanel);
createButtonGroup(alphabetDefaultRadioButton, leftToRightRadioButton, rightToLeftRadioButton,
topToBottomRadioButton, bottomToTopRadioButton);
JPanel orientationPanel = createOneColumnGridPanel(alphabetDefaultRadioButton, leftToRightRadioButton,
rightToLeftRadioButton, topToBottomRadioButton, bottomToTopRadioButton);
orientationPanel.setBorder(BorderFactory.createTitledBorder("Orientation"));
limitMaximumHeight(orientationPanel);
languagePanel.add(orientationPanel);
JPanel onlineLearningPanel = new JPanel(null);
onlineLearningPanel.setLayout(new BoxLayout(onlineLearningPanel, BoxLayout.PAGE_AXIS));
onlineLearningPanel.setBorder(BorderFactory.createTitledBorder("Online Learning"));
onlineLearningPanel.add(onlineLearningCheckBox);
JPanel onlineLearningTablePanel = new JPanel(new BorderLayout(3, 0));
onlineLearningTablePanel.setAlignmentX(Component.LEFT_ALIGNMENT); //to move onlineLearningCheckBox to the left
onlineLearningTablePanel.add(createOneColumnGridPanel("Online learning delay:"), BorderLayout.LINE_START);
onlineLearningTablePanel.add(createOneColumnGridPanel(onlineLearningDelaySpinner), BorderLayout.LINE_END);
onlineLearningPanel.add(onlineLearningTablePanel);
onlineLearningPanel.setAlignmentX(Component.CENTER_ALIGNMENT); //needed because of the alignment change above
limitMaximumHeight(onlineLearningPanel);
languagePanel.add(onlineLearningPanel);
JPanel ppmLanguageModelPanel = new JPanel(new BorderLayout(3, 0));
ppmLanguageModelPanel.setBorder(BorderFactory.createTitledBorder("PPM Language Model"));
ppmLanguageModelPanel.add(createOneColumnGridPanel("Maximum order:", "Alpha:", "Beta:", "Uniform:"),
BorderLayout.LINE_START);
ppmLanguageModelPanel.add(createOneColumnGridPanel(ppmMaxOrderSpinner, ppmAlphaSpinner, ppmBetaSpinner,
ppmUniformSpinner), BorderLayout.LINE_END);
limitMaximumHeight(ppmLanguageModelPanel);
languagePanel.add(ppmLanguageModelPanel);
JPanel worldModelPanel = new JPanel(new BorderLayout(3, 0));
worldModelPanel.setBorder(BorderFactory.createTitledBorder("World Model"));
worldModelPanel.add(createOneColumnGridPanel("Maximum number of nodes:",
"Maximum number of old root nodes:", "Minimum gain for trade:"), BorderLayout.LINE_START);
worldModelPanel.add(createOneColumnGridPanel(maxNumberOfNodesSpinner, maxNumberOfOldRootNodesSpinner,
minGainForNodeTradeSpinner), BorderLayout.LINE_END);
limitMaximumHeight(worldModelPanel);
languagePanel.add(worldModelPanel);
tabbedPane.add("Language", languagePanel);
JPanel inputPanel = new JPanel(null);
inputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.PAGE_AXIS));
JPanel inputProviderPanel = new JPanel(new BorderLayout(3, 0));
inputProviderPanel.setBorder(BorderFactory.createTitledBorder("Input Provider"));
inputProviderPanel.setAlignmentX(Component.LEFT_ALIGNMENT); //to move startStopOnLeftClickCheckBox to the left
createButtonGroup(mouseInputRadioButton, globalMouseInputRadioButton, joystickInputRadioButton,
socketInputRadioButton);
inputProviderPanel.add(createOneColumnGridPanel(mouseInputRadioButton, globalMouseInputRadioButton,
joystickInputRadioButton, socketInputRadioButton), BorderLayout.LINE_START);
JButton joystickInputOptionsButton = new JButton("Options...");
joystickInputOptionsButton.addActionListener(e -> {
joystickInputNameTextField.setText(joystickInputName);
joystickInputComponentNameXTextField.setText(joystickInputComponentNameX);
joystickInputSensitivityXSpinner.setValue(joystickInputSensitivityX);
joystickInputOffsetXSpinner.setValue(joystickInputOffsetX);
joystickInputComponentNameYTextField.setText(joystickInputComponentNameY);
joystickInputSensitivityYSpinner.setValue(joystickInputSensitivityY);
joystickInputOffsetYSpinner.setValue(joystickInputOffsetY);
joystickInputComponentNameStartStopTextField.setText(joystickInputComponentNameStartStop);
joystickInputDialog.setLocationRelativeTo(this);
joystickInputDialog.setVisible(true);
});
JButton socketInputOptionsButton = new JButton("Options...");
socketInputOptionsButton.addActionListener(e -> {
socketInputPortSpinner.setValue(socketInputPort);
socketInputLabelXTextField.setText(socketInputLabelX);
socketInputMinXSpinner.setValue(socketInputMinX);
socketInputMaxXSpinner.setValue(socketInputMaxX);
socketInputLabelYTextField.setText(socketInputLabelY);
socketInputMinYSpinner.setValue(socketInputMinY);
socketInputMaxYSpinner.setValue(socketInputMaxY);
socketInputLabelStartStopTextField.setText(socketInputLabelStartStop);
socketInputDialog.setLocationRelativeTo(this);
socketInputDialog.setVisible(true);
});
inputProviderPanel.add(createOneColumnGridPanel(new JPanel(null), new JPanel(null), joystickInputOptionsButton,
socketInputOptionsButton), BorderLayout.LINE_END); //no options for mouse input
//and global mouse input
limitMaximumHeight(inputProviderPanel);
inputPanel.add(inputProviderPanel);
JPanel startStopPanel = new JPanel(new BorderLayout(3, 0));
startStopPanel.setBorder(BorderFactory.createTitledBorder("Starting/Stopping"));
startStopPanel.setAlignmentX(Component.LEFT_ALIGNMENT); //to move startStopOnLeftClickCheckBox to the left
createButtonGroup(noneStartStopRadioButton, circleStartStopRadioButton, twoBoxesStartStopRadioButton);
startStopPanel.add(createOneColumnGridPanel(noneStartStopRadioButton, circleStartStopRadioButton,
twoBoxesStartStopRadioButton), BorderLayout.LINE_START);
JButton circleStartStopOptionsButton = new JButton("Options...");
circleStartStopOptionsButton.addActionListener(e -> {
circleSizeSpinner.setValue(circleSize);
circleOutlineThicknessSpinner.setValue(circleOutlineThickness);
circleOutlineThicknessHoldSpinner.setValue(circleOutlineThicknessHold);
circleHoldStartTimeSpinner.setValue(circleHoldStartTime);
circleHoldStopTimeSpinner.setValue(circleHoldStopTime);
circleDialog.setLocationRelativeTo(this);
circleDialog.setVisible(true);
});
JButton twoBoxesStartStopOptionsButton = new JButton("Options...");
twoBoxesStartStopOptionsButton.addActionListener(e -> {
firstBoxSizeSpinner.setValue(firstBoxSize);
firstBoxLeftInsetSpinner.setValue(firstBoxLeftInset);
firstBoxRightInsetSpinner.setValue(firstBoxRightInset);
firstBoxOutlineThicknessSpinner.setValue(firstBoxOutlineThickness);
firstBoxOutlineThicknessHoldSpinner.setValue(firstBoxOutlineThicknessHold);
firstBoxHoldTimeSpinner.setValue(firstBoxHoldTime);
firstBoxResetTimeSpinner.setValue(firstBoxResetTime);
secondBoxSizeSpinner.setValue(secondBoxSize);
secondBoxLeftInsetSpinner.setValue(secondBoxLeftInset);
secondBoxRightInsetSpinner.setValue(secondBoxRightInset);
secondBoxOutlineThicknessSpinner.setValue(secondBoxOutlineThickness);
secondBoxOutlineThicknessHoldSpinner.setValue(secondBoxOutlineThicknessHold);
secondBoxHoldTimeSpinner.setValue(secondBoxHoldTime);
twoBoxesDialog.setLocationRelativeTo(this);
twoBoxesDialog.setVisible(true);
});
startStopPanel.add(createOneColumnGridPanel(new JPanel(null), //no options for "none"
circleStartStopOptionsButton, twoBoxesStartStopOptionsButton), BorderLayout.LINE_END);
limitMaximumHeight(startStopPanel);
inputPanel.add(startStopPanel);
inputPanel.add(startStopOnLeftClickCheckBox);
tabbedPane.add("Input", inputPanel);
JPanel outputPanel = new JPanel(new BorderLayout());
JPanel outputPanelContent = new JPanel(null);
outputPanelContent.setLayout(new BoxLayout(outputPanelContent, BoxLayout.PAGE_AXIS));
createButtonGroup(internalTextOutputRadioButton, externalTextOutputRadioButton);
JPanel textOutputTargetPanel = createOneColumnGridPanel(internalTextOutputRadioButton,
externalTextOutputRadioButton);
textOutputTargetPanel.setBorder(BorderFactory.createTitledBorder("Text Output Target"));
outputPanelContent.add(textOutputTargetPanel);
JPanel socketOutputPanel = new JPanel(new BorderLayout(3, 0));
socketOutputPanel.setBorder(BorderFactory.createTitledBorder("Socket Output"));
socketOutputPanel.add(new JLabel("TCP port:"), BorderLayout.LINE_START);
socketOutputPanel.add(socketOutputPortSpinner, BorderLayout.LINE_END);
socketOutputPanel.add(createUneditableTextArea("Note: The socket output is only initialized if the current "
+"alphabet contains at least one SocketOutputAction.", 0, 0), BorderLayout.PAGE_END);
outputPanelContent.add(socketOutputPanel);
JPanel accessibilityActionsPanel = new JPanel(new BorderLayout());
accessibilityActionsPanel.setBorder(BorderFactory.createTitledBorder("Accessibility Actions"));
AvailableActionsDialog availableActionsDialog = new AvailableActionsDialog(this);
JButton listAvailableActionsButton = new JButton("List available actions...");
listAvailableActionsButton.addActionListener(e -> {
DasherJava.setAccessibilityActionsListTarget(availableActionsDialog);
availableActionsDialog.setLocationRelativeTo(this);
availableActionsDialog.setVisible(true);
});
accessibilityActionsPanel.add(listAvailableActionsButton, BorderLayout.CENTER);
outputPanelContent.add(accessibilityActionsPanel);
outputPanel.add(outputPanelContent, BorderLayout.PAGE_START);
tabbedPane.add("Output", outputPanel);
JPanel viewPanel = new JPanel(null);
viewPanel.setLayout(new BoxLayout(viewPanel, BoxLayout.PAGE_AXIS));
JPanel colorPalettePanel = new JPanel(new BorderLayout());
colorPalettePanel.setBorder(BorderFactory.createTitledBorder("Color Palette (empty = alphabet default)"));
colorPalettePanel.add(colorPaletteNameOverrideComboBox, BorderLayout.PAGE_START);
limitMaximumHeight(colorPalettePanel);
viewPanel.add(colorPalettePanel);
JPanel movementPanel = new JPanel(new BorderLayout(3, 0));
movementPanel.setBorder(BorderFactory.createTitledBorder("Movement"));
movementPanel.add(createOneColumnGridPanel("FPS:", "Speed:"), BorderLayout.LINE_START);
movementPanel.add(createOneColumnGridPanel(frameRateSpinner, movementSpeedSpinner), BorderLayout.LINE_END);
JButton marginsButton = new JButton("Set margins...");
marginsButton.addActionListener(e -> {
dropRootNodeMarginTopSpinner.setValue(dropRootNodeMarginTop);
dropRootNodeMarginBottomSpinner.setValue(dropRootNodeMarginBottom);
dropRootNodeMarginSizeSpinner.setValue(dropRootNodeMarginSize);
zoomOutMarginTopSpinner.setValue(zoomOutMarginTop);
zoomOutMarginBottomSpinner.setValue(zoomOutMarginBottom);
zoomOutMarginSizeSpinner.setValue(zoomOutMarginSize);
marginsDialog.setLocationRelativeTo(this);
marginsDialog.setVisible(true);
});
movementPanel.add(marginsButton, BorderLayout.PAGE_END);
limitMaximumHeight(movementPanel);
viewPanel.add(movementPanel);
JPanel renderingPanel = new JPanel(new BorderLayout());
renderingPanel.setBorder(BorderFactory.createTitledBorder("Rendering"));
JPanel antialiasingPanel = new JPanel(new BorderLayout(3, 0));
antialiasingPanel.add(createOneColumnGridPanel("Antialiasing:", "Text antialiasing:"), BorderLayout.LINE_START);
antialiasingPanel.add(createOneColumnGridPanel(antialiasingComboBox, textAntialiasingComboBox),
BorderLayout.LINE_END);
renderingPanel.add(antialiasingPanel, BorderLayout.PAGE_START);
JPanel renderingTablePanel = new JPanel(new BorderLayout(3, 0));
renderingTablePanel.add(createOneColumnGridPanel("Space behind nodes:", "Normal font size:",
"Medium font size:", "Highlight font size:", "Medium font size margin before:",
"Medium font size margin after:", "Minimum node size for rendering:",
"Minimum group size for rendering:", "Node outline thickness:", "Input line thickness:",
"Crosshair thickness:", "Crosshair width:"), BorderLayout.LINE_START);
renderingTablePanel.add(createOneColumnGridPanel(spaceBehindNodesSpinner, normalFontSizeSpinner,
mediumFontSizeSpinner, highlightFontSizeSpinner, mediumFontSizeMarginBeforeSpinner,
mediumFontSizeMarginAfterSpinner, minNodeSizeForRenderingSpinner, minGroupSizeForRenderingSpinner,
nodeOutlineThicknessSpinner, inputLineThicknessSpinner, crosshairThicknessSpinner,
crosshairWidthSpinner), BorderLayout.LINE_END);
renderingPanel.add(renderingTablePanel, BorderLayout.PAGE_END);
limitMaximumHeight(renderingPanel);
viewPanel.add(renderingPanel);
tabbedPane.add("View", viewPanel);
JPanel applicationPanel = new JPanel(null);
applicationPanel.setLayout(new BoxLayout(applicationPanel, BoxLayout.PAGE_AXIS));
JPanel confirmExitPanel = createOneColumnGridPanel(confirmExitComboBox);
confirmExitPanel.setBorder(BorderFactory.createTitledBorder("Confirm Exit"));
confirmExitPanel.setAlignmentX(Component.LEFT_ALIGNMENT); //to move checkboxes to the left
limitMaximumHeight(confirmExitPanel);
applicationPanel.add(confirmExitPanel);
JPanel windowAlwaysOnTopPanel = createOneColumnGridPanel(windowAlwaysOnTopComboBox);
windowAlwaysOnTopPanel.setBorder(BorderFactory.createTitledBorder("Window Always On Top"));
windowAlwaysOnTopPanel.setAlignmentX(Component.LEFT_ALIGNMENT); //to move checkboxes to the left
limitMaximumHeight(windowAlwaysOnTopPanel);
applicationPanel.add(windowAlwaysOnTopPanel);
JPanel windowUnfocusablePanel = createOneColumnGridPanel(windowUnfocusableComboBox);
windowUnfocusablePanel.setBorder(BorderFactory.createTitledBorder("Window Unfocusable"));
windowUnfocusablePanel.setAlignmentX(Component.LEFT_ALIGNMENT); //to move checkboxes to the left
limitMaximumHeight(windowUnfocusablePanel);
applicationPanel.add(windowUnfocusablePanel);
applicationPanel.add(rememberWindowPositionCheckBox);
applicationPanel.add(rememberWindowSizeCheckBox);
showStatusBarCheckBox.addActionListener(e -> {
if (!showStatusBarCheckBox.isSelected()) JOptionPane.showMessageDialog(this, "When the status bar is "
+"hidden, the settings dialog can be accessed by right-clicking on the main panel",
"Warning", JOptionPane.WARNING_MESSAGE);
});
applicationPanel.add(showStatusBarCheckBox);
JTextArea statusBarHiddenTextArea = createUneditableTextArea("Note: When the status bar is hidden, the "
+"settings dialog can be accessed by right-clicking on the main panel", 0, 0);
statusBarHiddenTextArea.setAlignmentX(Component.LEFT_ALIGNMENT); //to move checkboxes to the left
applicationPanel.add(statusBarHiddenTextArea);
applicationPanel.add(Box.createVerticalGlue());
JPanel aboutPanel = new JPanel(new BorderLayout());
aboutPanel.setBorder(BorderFactory.createTitledBorder("About DasherJava"));
aboutPanel.setAlignmentX(Component.LEFT_ALIGNMENT); //to move checkboxes to the left
aboutPanel.add(createUneditableTextArea("DasherJava "+DasherJava.VERSION_STRING+" ("
+DasherJava.DATE_STRING+")\nSee "+DasherJava.WEBSITE_STRING+" for more information\n"
+DasherJava.USING_STRING, 0, 0), BorderLayout.CENTER);
limitMaximumHeight(aboutPanel);
applicationPanel.add(aboutPanel);
tabbedPane.add("Application", applicationPanel);
add(tabbedPane, BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel(null);
buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.LINE_AXIS));
buttonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
buttonsPanel.add(Box.createHorizontalGlue());
JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(e -> setVisible(false));
buttonsPanel.add(cancelButton);
buttonsPanel.add(Box.createRigidArea(new Dimension(5, 0)));
JButton okButton = new JButton("OK");
okButton.addActionListener(e -> {
DasherJava.initWithSettings(createSettingsObject(), true);
setVisible(false);
});
buttonsPanel.add(okButton);
add(buttonsPanel, BorderLayout.PAGE_END);
pack(); //Call pack now, and again later before showing the dialog, see https://stackoverflow.com/a/42537586
}
public boolean isCurrentlyShowing() {
return isCurrentlyShowing;
}
public void showSettingsDialog(Settings settings, List<Alphabet> alphabets, List<Colors> colors,
String configurationName) {
configurationLabel.setText("Current configuration: "+configurationName);
frameRateSpinner.setValue(settings.getFrameRate());
switch (settings.getAntialiasing()) {
case "default":
antialiasingComboBox.setSelectedIndex(1);
break;
case "off":
antialiasingComboBox.setSelectedIndex(2);
break;
case "on":
default: //invalid value
antialiasingComboBox.setSelectedIndex(0);
break;
}
switch (settings.getTextAntialiasing()) {
case "gasp":
textAntialiasingComboBox.setSelectedIndex(1);
break;
case "default":
textAntialiasingComboBox.setSelectedIndex(2);
break;
case "off":
textAntialiasingComboBox.setSelectedIndex(3);
break;
case "on":
default: //invalid value
textAntialiasingComboBox.setSelectedIndex(0);
break;
}
windowX=settings.getWindowX();
windowY=settings.getWindowY();
windowWidth=settings.getWindowWidth();
windowHeight=settings.getWindowHeight();
spaceBehindNodesSpinner.setValue(settings.getSpaceBehindNodes());
switch (settings.getWindowAlwaysOnTop()) {
case "always":
windowAlwaysOnTopComboBox.setSelectedIndex(0);
break;
case "never":
windowAlwaysOnTopComboBox.setSelectedIndex(2);
break;
case "whenExternalText":
default: //invalid value
windowAlwaysOnTopComboBox.setSelectedIndex(1);
break;
}
switch (settings.getWindowUnfocusable()) {
case "always":
windowUnfocusableComboBox.setSelectedIndex(0);
break;
case "never":
windowUnfocusableComboBox.setSelectedIndex(2);
break;
case "whenExternalText":
default: //invalid value
windowUnfocusableComboBox.setSelectedIndex(1);
break;
}
rememberWindowPositionCheckBox.setSelected(settings.getRememberWindowPosition());
rememberWindowSizeCheckBox.setSelected(settings.getRememberWindowSize());
showStatusBarCheckBox.setSelected(settings.getShowStatusBar());
switch (settings.getConfirmExit()) {
case "always":
confirmExitComboBox.setSelectedIndex(0);
break;
case "never":
confirmExitComboBox.setSelectedIndex(2);
break;
case "whenTextEntered":
default: //invalid value
confirmExitComboBox.setSelectedIndex(1);
break;
}
alphabetNameComboBox.removeAllItems();
for (Alphabet alphabet : alphabets) {
alphabetNameComboBox.addItem(alphabet.getName());
}
alphabetNameComboBox.setSelectedItem(settings.getAlphabetName());
alphabetHistory=settings.getAlphabetHistory();
maxAlphabetHistoryLengthSpinner.setValue(settings.getMaxAlphabetHistoryLength());
colorPaletteNameOverrideComboBox.removeAllItems();
colorPaletteNameOverrideComboBox.addItem(""); //empty = alphabet default
for (Colors colorPalette : colors) {
colorPaletteNameOverrideComboBox.addItem(colorPalette.getName());
}
colorPaletteNameOverrideComboBox.setSelectedItem(settings.getColorPaletteNameOverride());
switch (settings.getOrientationOverride()) {
case "LR":
leftToRightRadioButton.setSelected(true);
break;
case "RL":
rightToLeftRadioButton.setSelected(true);
break;
case "TB":
topToBottomRadioButton.setSelected(true);
break;
case "BT":
bottomToTopRadioButton.setSelected(true);
break;
case "":
default: //invalid value
alphabetDefaultRadioButton.setSelected(true);
break;
}
ppmMaxOrderSpinner.setValue(settings.getPPMMaxOrder());
ppmAlphaSpinner.setValue(settings.getPPMAlpha());
ppmBetaSpinner.setValue(settings.getPPMBeta());
ppmUniformSpinner.setValue(settings.getPPMUniform());
switch (settings.getTextOutputTarget()) {
case "external":
externalTextOutputRadioButton.setSelected(true);
break;
case "internal":
default: //invalid value
internalTextOutputRadioButton.setSelected(true);
break;
}
switch (settings.getInputProvider()) {
case "globalMouse":
globalMouseInputRadioButton.setSelected(true);
break;
case "joystick":
joystickInputRadioButton.setSelected(true);
break;
case "socket":
socketInputRadioButton.setSelected(true);
break;
case "mouse":
default: //invalid value
mouseInputRadioButton.setSelected(true);
break;
}
startStopOnLeftClickCheckBox.setSelected(settings.getStartStopOnLeftClick());
movementSpeedSpinner.setValue(settings.getMovementSpeed());
onlineLearningCheckBox.setSelected(settings.getOnlineLearning());
onlineLearningDelaySpinner.setValue(settings.getOnlineLearningDelay());
maxNumberOfNodesSpinner.setValue(settings.getMaxNumberOfNodes());
maxNumberOfOldRootNodesSpinner.setValue(settings.getMaxNumberOfOldRootNodes());
minGainForNodeTradeSpinner.setValue(settings.getMinGainForNodeTrade());
minNodeSizeForExpansion=settings.getMinNodeSizeForExpansion();
maxNodeSizeForCollapse=settings.getMaxNodeSizeForCollapse();
minNodeSizeForRenderingSpinner.setValue(settings.getMinNodeSizeForRendering());
minGroupSizeForRenderingSpinner.setValue(settings.getMinGroupSizeForRendering());
dropRootNodeMarginTop=settings.getDropRootNodeMarginTop();
dropRootNodeMarginBottom=settings.getDropRootNodeMarginBottom();
dropRootNodeMarginSize=settings.getDropRootNodeMarginSize();
zoomOutMarginTop=settings.getZoomOutMarginTop();
zoomOutMarginBottom=settings.getZoomOutMarginBottom();
zoomOutMarginSize=settings.getZoomOutMarginSize();
normalFontSizeSpinner.setValue(settings.getNormalFontSize());
mediumFontSizeSpinner.setValue(settings.getMediumFontSize());
highlightFontSizeSpinner.setValue(settings.getHighlightFontSize());
mediumFontSizeMarginBeforeSpinner.setValue(settings.getMediumFontSizeMarginBefore());
mediumFontSizeMarginAfterSpinner.setValue(settings.getMediumFontSizeMarginAfter());
nodeOutlineThicknessSpinner.setValue(settings.getNodeOutlineThickness());
inputLineThicknessSpinner.setValue(settings.getInputLineThickness());
crosshairThicknessSpinner.setValue(settings.getCrosshairThickness());
crosshairWidthSpinner.setValue(settings.getCrosshairWidth());
switch (settings.getStartStopHandler()) {
case "circle":
circleStartStopRadioButton.setSelected(true);
break;
case "twoBoxes":
twoBoxesStartStopRadioButton.setSelected(true);
break;
case "none":
default: //invalid value
noneStartStopRadioButton.setSelected(true);
break;
}
circleSize=settings.getCircleSize();
circleOutlineThickness=settings.getCircleOutlineThickness();
circleOutlineThicknessHold=settings.getCircleOutlineThicknessHold();
circleHoldStartTime=settings.getCircleHoldStartTime();
circleHoldStopTime=settings.getCircleHoldStopTime();
firstBoxSize=settings.getFirstBoxSize();
firstBoxLeftInset=settings.getFirstBoxLeftInset();
firstBoxRightInset=settings.getFirstBoxRightInset();
firstBoxOutlineThickness=settings.getFirstBoxOutlineThickness();
firstBoxOutlineThicknessHold=settings.getFirstBoxOutlineThicknessHold();
firstBoxHoldTime=settings.getFirstBoxHoldTime();
firstBoxResetTime=settings.getFirstBoxResetTime();
secondBoxSize=settings.getSecondBoxSize();
secondBoxLeftInset=settings.getSecondBoxLeftInset();
secondBoxRightInset=settings.getSecondBoxRightInset();
secondBoxOutlineThickness=settings.getSecondBoxOutlineThickness();
secondBoxOutlineThicknessHold=settings.getSecondBoxOutlineThicknessHold();
secondBoxHoldTime=settings.getSecondBoxHoldTime();
socketOutputPortSpinner.setValue(settings.getSocketOutputPort());
joystickInputName=settings.getJoystickInputName();
joystickInputComponentNameX=settings.getJoystickInputComponentNameX();
joystickInputSensitivityX=settings.getJoystickInputSensitivityX();
joystickInputOffsetX=settings.getJoystickInputOffsetX();
joystickInputComponentNameY=settings.getJoystickInputComponentNameY();
joystickInputSensitivityY=settings.getJoystickInputSensitivityY();
joystickInputOffsetY=settings.getJoystickInputOffsetY();
joystickInputComponentNameStartStop=settings.getJoystickInputComponentNameStartStop();
socketInputPort=settings.getSocketInputPort();
socketInputLabelX=settings.getSocketInputLabelX();
socketInputMinX=settings.getSocketInputMinX();
socketInputMaxX=settings.getSocketInputMaxX();
socketInputLabelY=settings.getSocketInputLabelY();
socketInputMinY=settings.getSocketInputMinY();
socketInputMaxY=settings.getSocketInputMaxY();
socketInputLabelStartStop=settings.getSocketInputLabelStartStop();
pack();
setLocationRelativeTo(owner);
setVisible(true);
}
private Settings createSettingsObject() {
int frameRate = readIntSpinner(frameRateSpinner);
String antialiasing = readAntialiasingString();
String textAntialiasing = readTextAntialiasingString();
int spaceBehindNodes = readIntSpinner(spaceBehindNodesSpinner);
String windowAlwaysOnTop = readWindowAlwaysOnTopString();
String windowUnfocusable = readWindowUnfocusableString();
boolean rememberWindowPosition = rememberWindowPositionCheckBox.isSelected();
boolean rememberWindowSize = rememberWindowSizeCheckBox.isSelected();
boolean showStatusBar = showStatusBarCheckBox.isSelected();
String confirmExit = readConfirmExitString();
String alphabetName = (String) alphabetNameComboBox.getSelectedItem();
int maxAlphabetHistoryLength = readIntSpinner(maxAlphabetHistoryLengthSpinner);
String colorPaletteNameOverride = (String) colorPaletteNameOverrideComboBox.getSelectedItem();
String orientationOverride = readOrientationOverrideString();
int ppmMaxOrder = readIntSpinner(ppmMaxOrderSpinner);
int ppmAlpha = readIntSpinner(ppmAlphaSpinner);
int ppmBeta = readIntSpinner(ppmBetaSpinner);
int ppmUniform = readIntSpinner(ppmUniformSpinner);
String textOutputTarget = readTextOutputTargetString();
String inputProvider = readInputProviderString();
boolean startStopOnLeftClick = startStopOnLeftClickCheckBox.isSelected();
float movementSpeed = readDoubleSpinner(movementSpeedSpinner);
boolean onlineLearning = onlineLearningCheckBox.isSelected();
int onlineLearningDelay = readIntSpinner(onlineLearningDelaySpinner);
int maxNumberOfNodes = readIntSpinner(maxNumberOfNodesSpinner);
int maxNumberOfOldRootNodes = readIntSpinner(maxNumberOfOldRootNodesSpinner);
float minGainForNodeTrade = readDoubleSpinner(minGainForNodeTradeSpinner);
float minNodeSizeForRendering = readDoubleSpinner(minNodeSizeForRenderingSpinner);
float minGroupSizeForRendering = readDoubleSpinner(minGroupSizeForRenderingSpinner);
int normalFontSize = readIntSpinner(normalFontSizeSpinner);
int mediumFontSize = readIntSpinner(mediumFontSizeSpinner);
int highlightFontSize = readIntSpinner(highlightFontSizeSpinner);
float mediumFontSizeMarginBefore = readDoubleSpinner(mediumFontSizeMarginBeforeSpinner);
float mediumFontSizeMarginAfter = readDoubleSpinner(mediumFontSizeMarginAfterSpinner);
float nodeOutlineThickness = readDoubleSpinner(nodeOutlineThicknessSpinner);
float inputLineThickness = readDoubleSpinner(inputLineThicknessSpinner);
float crosshairThickness = readDoubleSpinner(crosshairThicknessSpinner);
float crosshairWidth = readDoubleSpinner(crosshairWidthSpinner);
String startStopHandler = readStartStopHandlerString();
int socketOutputPort = readIntSpinner(socketOutputPortSpinner);
Settings settings = new Settings(frameRate, antialiasing, textAntialiasing, windowX, windowY, windowWidth,
windowHeight, spaceBehindNodes, windowAlwaysOnTop, windowUnfocusable, rememberWindowPosition,
rememberWindowSize, showStatusBar, confirmExit, alphabetHistory, maxAlphabetHistoryLength,
colorPaletteNameOverride, orientationOverride, ppmMaxOrder, ppmAlpha, ppmBeta, ppmUniform,
textOutputTarget, inputProvider, startStopOnLeftClick, movementSpeed, onlineLearning,
onlineLearningDelay, maxNumberOfNodes, maxNumberOfOldRootNodes, minGainForNodeTrade,
minNodeSizeForExpansion, maxNodeSizeForCollapse, minNodeSizeForRendering, minGroupSizeForRendering,
dropRootNodeMarginTop, dropRootNodeMarginBottom, dropRootNodeMarginSize, zoomOutMarginTop,
zoomOutMarginBottom, zoomOutMarginSize, normalFontSize, mediumFontSize, highlightFontSize,
mediumFontSizeMarginBefore, mediumFontSizeMarginAfter, nodeOutlineThickness, inputLineThickness,
crosshairThickness, crosshairWidth, startStopHandler, circleSize, circleOutlineThickness,
circleOutlineThicknessHold, circleHoldStartTime, circleHoldStopTime, firstBoxSize, firstBoxLeftInset,
firstBoxRightInset, firstBoxOutlineThickness, firstBoxOutlineThicknessHold, firstBoxHoldTime,
firstBoxResetTime, secondBoxSize, secondBoxLeftInset, secondBoxRightInset, secondBoxOutlineThickness,
secondBoxOutlineThicknessHold, secondBoxHoldTime, socketOutputPort, joystickInputName,
joystickInputComponentNameX, joystickInputSensitivityX, joystickInputOffsetX,
joystickInputComponentNameY, joystickInputSensitivityY, joystickInputOffsetY,
joystickInputComponentNameStartStop, socketInputPort, socketInputLabelX, socketInputMinX,
socketInputMaxX, socketInputLabelY, socketInputMinY, socketInputMaxY, socketInputLabelStartStop);
settings.insertIntoAlphabetHistory(alphabetName);
return settings;
}
private String readOrientationOverrideString() {
if (leftToRightRadioButton.isSelected()) return "LR";
if (rightToLeftRadioButton.isSelected()) return "RL";
if (topToBottomRadioButton.isSelected()) return "TB";
if (bottomToTopRadioButton.isSelected()) return "BT";
return ""; //alphabet default
}
private String readTextOutputTargetString() {
if (externalTextOutputRadioButton.isSelected()) return "external";
return "internal";
}
private String readInputProviderString() {
if (globalMouseInputRadioButton.isSelected()) return "globalMouse";
if (joystickInputRadioButton.isSelected()) return "joystick";
if (socketInputRadioButton.isSelected()) return "socket";
return "mouse";
}
private String readStartStopHandlerString() {
if (circleStartStopRadioButton.isSelected()) return "circle";
if (twoBoxesStartStopRadioButton.isSelected()) return "twoBoxes";
return "none";
}
private String readAntialiasingString() {
switch (antialiasingComboBox.getSelectedIndex()) {
case 1:
return "default";
case 2:
return "off";
case 0:
default:
return "on";
}
}
private String readTextAntialiasingString() {
switch (textAntialiasingComboBox.getSelectedIndex()) {
case 1:
return "gasp";
case 2:
return "default";
case 3:
return "off";
case 0:
default:
return "on";
}
}
private String readWindowAlwaysOnTopString() {
switch (windowAlwaysOnTopComboBox.getSelectedIndex()) {
case 0:
return "always";
case 2:
return "never";
case 1:
default:
return "whenExternalText";
}
}
private String readWindowUnfocusableString() {
switch (windowUnfocusableComboBox.getSelectedIndex()) {
case 0:
return "always";
case 2:
return "never";
case 1:
default:
return "whenExternalText";
}
}
private String readConfirmExitString() {
switch (confirmExitComboBox.getSelectedIndex()) {
case 0:
return "always";
case 2:
return "never";
case 1:
default:
return "whenTextEntered";
}
}
private static int readIntSpinner(JSpinner spinner) {
try {
spinner.commitEdit();
} catch (ParseException ex) {
//ignore, keep old value
}
return (int) spinner.getValue();
}
public static float readDoubleSpinner(JSpinner spinner) {
try {
spinner.commitEdit();
} catch (ParseException ex) {
//ignore, keep old value
}
//There is no SpinnerNumberModel for floats, so spinners mostly use doubles, but the
//data type may differ depending on how the value is set.
Object value = spinner.getValue();
if (value instanceof Double) return ((Double) value).floatValue();
return (float) value;
}
private static JSpinner createIntSpinner(int minimum, int maximum) {
JSpinner spinner = new JSpinner(new SpinnerNumberModel(minimum, minimum, maximum, 1));
spinner.setEditor(new JSpinner.NumberEditor(spinner, "0")); //disable digit grouping
return spinner;
}
public static JSpinner createDoubleSpinner(double minimum, double maximum, double stepSize) {
JSpinner spinner = new JSpinner(new SpinnerNumberModel(minimum, minimum, maximum, stepSize));
spinner.setEditor(new JSpinner.NumberEditor(spinner, "0.0#####")); //disable digit grouping
//and round to up to 6 decimal digits
return spinner;
}
private static JTextField createTextField() {
JTextField textField = new JTextField();
textField.setPreferredSize(new Dimension(0, textField.getPreferredSize().height)); //make sure that the
//preferred width doesn't grow based on the current text content so that the text
//field doesn't grow beyond the available space
return textField;
}
private static JTextArea createUneditableTextArea(String text, int rows, int columns) {
JTextArea textArea = new JTextArea(text, rows, columns);
textArea.setOpaque(false); //must use this method instead of setting a transparent background color,
//otherwise text is not properly redrawn when scrolling
textArea.setEditable(false);
textArea.setLineWrap(true);
textArea.setWrapStyleWord(true);
return textArea;
}
private static void limitMaximumHeight(JComponent component) { //limit to preferred height
component.setMaximumSize(new Dimension(component.getMaximumSize().width,
component.getPreferredSize().height));
}
private static void createButtonGroup(AbstractButton... buttons) {
ButtonGroup group = new ButtonGroup();
for (AbstractButton button : buttons) {
group.add(button);
}
}
private static JPanel createOneColumnGridPanel(JComponent... components) {
JPanel panel = new JPanel(new GridLayout(0, 1));
for (JComponent component : components) {
panel.add(component);
}
return panel;
}
private static JPanel createOneColumnGridPanel(String... labels) {
JPanel panel = new JPanel(new GridLayout(0, 1));
for (String label : labels) {
panel.add(new JLabel(label));
}
return panel;
}
public static class AvailableActionsDialog extends JDialog implements AccessibilityActionsListTarget {
private final JTextArea textArea;
AvailableActionsDialog(Dialog owner) {
super(owner, "Available Accessibility Actions", true);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
DasherJava.setAccessibilityActionsListTarget(null);
textArea.setText("");
}
});
setLayout(new BorderLayout());
JPanel contentPanel = new JPanel(new BorderLayout(0, 5));
contentPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
contentPanel.add(createUneditableTextArea("Keep this dialog open while focusing other applications or "
+"elements on your desktop. The text area below will update to show the list of available "
+"accessibility actions in the current situation, one per line. You can use these action "
+"identifiers in <accessibilityAction> elements when creating your own alphabet.", 4, 0),
BorderLayout.PAGE_START);
textArea=createUneditableTextArea("", 20, 40);
JScrollPane scrollPane = new JScrollPane(textArea, ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS,
ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
contentPanel.add(scrollPane, BorderLayout.CENTER);
add(contentPanel, BorderLayout.CENTER);
JPanel buttonsPanel = new JPanel(null);
buttonsPanel.setLayout(new BoxLayout(buttonsPanel, BoxLayout.LINE_AXIS));
buttonsPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JButton okButton = new JButton("OK");
okButton.addActionListener(e -> {
DasherJava.setAccessibilityActionsListTarget(null);
textArea.setText("");
setVisible(false);
});
buttonsPanel.add(Box.createHorizontalGlue());
buttonsPanel.add(okButton);
add(buttonsPanel, BorderLayout.PAGE_END);
pack();
}
@Override
public void receive(String[] actions) {
SwingUtilities.invokeLater(() -> {
StringBuilder sb = new StringBuilder("Number of actions: ");
sb.append(actions.length);
for (String action : actions) {
sb.append("\n");
sb.append(action);
}
textArea.setText(sb.toString());
});
}
}
}