mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-06 03:50:02 +02:00
Merge remote-tracking branch
'origin/GP-5964-dragonmacher-file-chooser-accessibility--SQUASHED' (Closes #6310, Closes #7129, Closes #7130)
This commit is contained in:
commit
5fbb052b28
6 changed files with 272 additions and 148 deletions
|
@ -27,7 +27,7 @@ import docking.action.DockingActionIf;
|
||||||
import ghidra.docking.util.LookAndFeelUtils;
|
import ghidra.docking.util.LookAndFeelUtils;
|
||||||
import ghidra.util.StringUtilities;
|
import ghidra.util.StringUtilities;
|
||||||
|
|
||||||
class DockingToolBarUtils {
|
public class DockingToolBarUtils {
|
||||||
|
|
||||||
private static final String START_KEYBINDING_TEXT = "<BR><HR><CENTER>(";
|
private static final String START_KEYBINDING_TEXT = "<BR><HR><CENTER>(";
|
||||||
private static final String END_KEYBINDNIG_TEXT = ")</CENTER>";
|
private static final String END_KEYBINDNIG_TEXT = ")</CENTER>";
|
||||||
|
@ -37,16 +37,25 @@ class DockingToolBarUtils {
|
||||||
* @param button the button
|
* @param button the button
|
||||||
* @param action the action
|
* @param action the action
|
||||||
*/
|
*/
|
||||||
static void setToolTipText(JButton button, DockingActionIf action) {
|
public static void setToolTipText(JButton button, DockingActionIf action) {
|
||||||
|
String text = createToolTipText(button, action);
|
||||||
|
button.setToolTipText(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates tooltip text for the given action and button. This is intended to be used for
|
||||||
|
* buttons that represent the given action.
|
||||||
|
* @param button the button that is the target for the text
|
||||||
|
* @param action the action that is the source of the button
|
||||||
|
* @return the text
|
||||||
|
*/
|
||||||
|
public static String createToolTipText(JButton button, DockingActionIf action) {
|
||||||
String toolTipText = getToolTipText(action);
|
String toolTipText = getToolTipText(action);
|
||||||
String keyBindingText = getKeyBindingAcceleratorText(button, action.getKeyBinding());
|
String keyBindingText = getKeyBindingAcceleratorText(button, action.getKeyBinding());
|
||||||
if (keyBindingText != null) {
|
if (keyBindingText != null) {
|
||||||
button.setToolTipText(combingToolTipTextWithKeyBinding(toolTipText, keyBindingText));
|
return combingToolTipTextWithKeyBinding(toolTipText, keyBindingText);
|
||||||
}
|
|
||||||
else {
|
|
||||||
button.setToolTipText(toolTipText);
|
|
||||||
}
|
}
|
||||||
|
return toolTipText;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String combingToolTipTextWithKeyBinding(String toolTipText,
|
private static String combingToolTipTextWithKeyBinding(String toolTipText,
|
||||||
|
|
|
@ -130,7 +130,7 @@ public class EmptyBorderButton extends JButton {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBorder(Border border) {
|
public void setBorder(Border border) {
|
||||||
// To keep UI from installing a non-appropriate border (such as when switching themes),
|
// To keep UI from installing an incorrect border (such as when switching themes),
|
||||||
// only allow borders created by this class to be set.
|
// only allow borders created by this class to be set.
|
||||||
if (border == RAISED_BUTTON_BORDER || border == LOWERED_BUTTON_BORDER ||
|
if (border == RAISED_BUTTON_BORDER || border == LOWERED_BUTTON_BORDER ||
|
||||||
border == FOCUSED_BUTTON_BORDER || border == NO_BUTTON_BORDER) {
|
border == FOCUSED_BUTTON_BORDER || border == NO_BUTTON_BORDER) {
|
||||||
|
@ -171,6 +171,12 @@ public class EmptyBorderButton extends JButton {
|
||||||
setBorder(NO_BUTTON_BORDER);
|
setBorder(NO_BUTTON_BORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEnabled(boolean b) {
|
||||||
|
setBorder(NO_BUTTON_BORDER);
|
||||||
|
super.setEnabled(b);
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateBorderBasedOnState() {
|
protected void updateBorderBasedOnState() {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -15,36 +15,45 @@
|
||||||
*/
|
*/
|
||||||
package docking.widgets.filechooser;
|
package docking.widgets.filechooser;
|
||||||
|
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.Color;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.FocusEvent;
|
||||||
|
import java.awt.event.FocusListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.*;
|
||||||
import javax.swing.border.EmptyBorder;
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
|
||||||
|
import generic.theme.GColor;
|
||||||
import generic.theme.GThemeDefaults.Colors;
|
import generic.theme.GThemeDefaults.Colors;
|
||||||
|
|
||||||
public class FileChooserToggleButton extends JToggleButton {
|
public class FileChooserToggleButton extends JToggleButton {
|
||||||
private static final long serialVersionUID = 1L;
|
|
||||||
|
|
||||||
static final Border RAISED_BORDER = BorderFactory.createCompoundBorder(
|
//
|
||||||
BorderFactory.createRaisedBevelBorder(),
|
// All border sizes are based on trial-and-error, adjusted to prevent the UI from moving as the
|
||||||
BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
// user hovers and moves around with the keyboard.
|
||||||
|
//
|
||||||
|
private static final Border RAISED_BORDER = BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createRaisedBevelBorder(), BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
||||||
|
|
||||||
static final Border NO_BORDER = new EmptyBorder(RAISED_BORDER.getBorderInsets(new JButton()));
|
private static final Border LOWERED_BORDER = BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createLoweredBevelBorder(), BorderFactory.createEmptyBorder(2, 2, 2, 2));
|
||||||
|
|
||||||
static final Border LOWERED_BORDER = BorderFactory.createCompoundBorder(
|
// The focused border is a blue line with some padding on the outside so it is easy to see when
|
||||||
BorderFactory.createLoweredBevelBorder(),
|
// the button has focus. This is similar to other buttons in the system.
|
||||||
BorderFactory.createEmptyBorder(1, 1, 1, 1));
|
private static final Color FOCUS_COLOR = new GColor("color.border.button.focused");
|
||||||
|
private static final Border FOCUSED_BORDER = BorderFactory.createCompoundBorder(
|
||||||
|
BorderFactory.createEmptyBorder(1, 1, 1, 1), BorderFactory.createLineBorder(FOCUS_COLOR));
|
||||||
|
private static final Border UNFOCUSED_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2);
|
||||||
|
|
||||||
public FileChooserToggleButton(String text) {
|
private static final Border NO_BORDER = new EmptyBorder(4, 4, 4, 4);
|
||||||
|
|
||||||
|
private GhidraFileChooser fileChooser;
|
||||||
|
|
||||||
|
public FileChooserToggleButton(String text, GhidraFileChooser fileChooser) {
|
||||||
super(text);
|
super(text);
|
||||||
initBorder();
|
this.fileChooser = fileChooser;
|
||||||
}
|
|
||||||
|
|
||||||
public FileChooserToggleButton(Action action) {
|
|
||||||
super(action);
|
|
||||||
initBorder();
|
initBorder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,88 +68,92 @@ public class FileChooserToggleButton extends JToggleButton {
|
||||||
setContentAreaFilled(false);
|
setContentAreaFilled(false);
|
||||||
|
|
||||||
// changes the border on hover and click
|
// changes the border on hover and click
|
||||||
addMouseListener(new ButtonMouseListener());
|
addChangeListener(new ButtonStateListener());
|
||||||
|
|
||||||
// works in conjunction with the mouse listener to properly set the border
|
// works in conjunction with the mouse listener to properly set the border
|
||||||
addChangeListener(e -> {
|
addChangeListener(e -> updateBorderBasedOnState());
|
||||||
if (isSelected()) {
|
|
||||||
setBorder(LOWERED_BORDER);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setBorder(NO_BORDER);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
setFocusable(false); // this prevents the focus box from being drawn over the button
|
addFocusListener(new ButtonFocusListener());
|
||||||
|
|
||||||
|
updateBorderBasedOnState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void clearBorder() {
|
@Override
|
||||||
|
public void setBorder(Border border) {
|
||||||
|
// To keep UI from installing an incorrect border (such as when switching themes),
|
||||||
|
// only allow borders created by this class to be set.
|
||||||
|
if (border == RAISED_BORDER || border == LOWERED_BORDER || border == NO_BORDER ||
|
||||||
|
border instanceof FocusedBorder) {
|
||||||
|
super.setBorder(border);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearBorder() {
|
||||||
setBorder(NO_BORDER);
|
setBorder(NO_BORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the directory with which this button is associated. */
|
/** {@return Returns the directory with which this button is associated.} */
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ButtonMouseListener extends MouseAdapter {
|
private void updateBorderBasedOnState() {
|
||||||
private boolean inside = false;
|
if (!isEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
private Border defaultBorder;
|
ButtonModel buttonModel = getModel();
|
||||||
|
boolean pressed = buttonModel.isPressed();
|
||||||
|
boolean rollover = buttonModel.isRollover();
|
||||||
|
boolean armed = buttonModel.isArmed();
|
||||||
|
boolean selected = buttonModel.isSelected();
|
||||||
|
|
||||||
|
Border border = NO_BORDER;
|
||||||
|
|
||||||
|
if (selected) {
|
||||||
|
border = LOWERED_BORDER;
|
||||||
|
}
|
||||||
|
else if (pressed && (rollover || armed)) {
|
||||||
|
border = LOWERED_BORDER;
|
||||||
|
}
|
||||||
|
else if (rollover) {
|
||||||
|
border = RAISED_BORDER;
|
||||||
|
}
|
||||||
|
|
||||||
|
border = createFocusedBorder(border, isFocusOwner());
|
||||||
|
|
||||||
|
setBorder(border);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Border createFocusedBorder(Border outside, boolean isFocused) {
|
||||||
|
Border inside = isFocused ? FOCUSED_BORDER : UNFOCUSED_BORDER;
|
||||||
|
return new FocusedBorder(outside, inside);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ButtonStateListener implements ChangeListener {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
updateBorderBasedOnState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ButtonFocusListener implements FocusListener {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseEntered(MouseEvent me) {
|
public void focusGained(FocusEvent e) {
|
||||||
if (isSelected()) {
|
updateBorderBasedOnState();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
defaultBorder = getBorder();
|
|
||||||
setBorder(RAISED_BORDER);
|
|
||||||
inside = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseExited(MouseEvent me) {
|
public void focusLost(FocusEvent e) {
|
||||||
if (isSelected()) {
|
updateBorderBasedOnState();
|
||||||
return;
|
fileChooser.updateShortcutPanel();
|
||||||
}
|
|
||||||
|
|
||||||
inside = false;
|
|
||||||
restoreBorder();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
private class FocusedBorder extends CompoundBorder {
|
||||||
public void mousePressed(MouseEvent e) {
|
FocusedBorder(Border outsideBorder, Border insideBorder) {
|
||||||
if (isSelected()) {
|
super(outsideBorder, insideBorder);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
|
||||||
setBorder(LOWERED_BORDER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void mouseReleased(MouseEvent e) {
|
|
||||||
if (isSelected()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inside) {
|
|
||||||
setBorder(RAISED_BORDER);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
restoreBorder();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreBorder() {
|
|
||||||
if (defaultBorder != null) {
|
|
||||||
setBorder(defaultBorder);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setBorder(NO_BORDER);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import javax.swing.*;
|
||||||
|
|
||||||
import docking.ReusableDialogComponentProvider;
|
import docking.ReusableDialogComponentProvider;
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.label.GLabel;
|
|
||||||
import ghidra.framework.preferences.Preferences;
|
import ghidra.framework.preferences.Preferences;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
|
|
||||||
|
@ -47,17 +46,13 @@ class GFileChooserOptionsDialog extends ReusableDialogComponentProvider {
|
||||||
private JComponent buildComponent() {
|
private JComponent buildComponent() {
|
||||||
JPanel panel = new JPanel(new PairLayout());
|
JPanel panel = new JPanel(new PairLayout());
|
||||||
|
|
||||||
showDotFilesCheckBox = new GCheckBox();
|
showDotFilesCheckBox = new GCheckBox("Show '.' files");
|
||||||
|
showDotFilesCheckBox.setToolTipText("When toggled on the file chooser will show files " +
|
||||||
|
"with names that begin with a '.' character");
|
||||||
showDotFilesCheckBox.getAccessibleContext().setAccessibleName("Show Dot Files");
|
showDotFilesCheckBox.getAccessibleContext().setAccessibleName("Show Dot Files");
|
||||||
showDotFilesCheckBox.setSelected(true);
|
showDotFilesCheckBox.setSelected(true);
|
||||||
|
|
||||||
JLabel label = new GLabel("Show '.' files");
|
|
||||||
label.getAccessibleContext().setAccessibleName("Show Files");
|
|
||||||
label.setToolTipText("When toggled on the file chooser will show files " +
|
|
||||||
"with names that begin with a '.' character");
|
|
||||||
|
|
||||||
panel.add(showDotFilesCheckBox);
|
panel.add(showDotFilesCheckBox);
|
||||||
panel.add(label);
|
|
||||||
panel.getAccessibleContext().setAccessibleName("GFile Chooser Options");
|
panel.getAccessibleContext().setAccessibleName("GFile Chooser Options");
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@ package docking.widgets.filechooser;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.*;
|
import java.awt.event.*;
|
||||||
|
import java.beans.PropertyChangeEvent;
|
||||||
|
import java.beans.PropertyChangeListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileFilter;
|
import java.io.FileFilter;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
@ -35,7 +37,11 @@ import javax.swing.text.DefaultFormatterFactory;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
import docking.*;
|
import docking.*;
|
||||||
|
import docking.action.DockingAction;
|
||||||
|
import docking.action.DockingActionIf;
|
||||||
|
import docking.action.builder.ActionBuilder;
|
||||||
import docking.actions.KeyBindingUtils;
|
import docking.actions.KeyBindingUtils;
|
||||||
|
import docking.menu.DockingToolBarUtils;
|
||||||
import docking.widgets.*;
|
import docking.widgets.*;
|
||||||
import docking.widgets.combobox.GComboBox;
|
import docking.widgets.combobox.GComboBox;
|
||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
|
@ -166,13 +172,19 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
|
|
||||||
private Component parent;
|
private Component parent;
|
||||||
private JPanel waitPanel;
|
private JPanel waitPanel;
|
||||||
private EmptyBorderButton backButton;
|
private JButton backButton;
|
||||||
private EmptyBorderButton forwardButton;
|
private JButton forwardButton;
|
||||||
private EmptyBorderButton upLevelButton;
|
private JButton upButton;
|
||||||
private EmptyBorderButton newFolderButton;
|
private JButton newFolderButton;
|
||||||
private EmptyBorderButton refreshButton;
|
private JButton refreshButton;
|
||||||
private EmptyBorderToggleButton detailsButton;
|
private EmptyBorderToggleButton detailsButton;
|
||||||
|
|
||||||
|
private DockingAction upAction;
|
||||||
|
private DockingAction backAction;
|
||||||
|
private DockingAction forwardAction;
|
||||||
|
private KeyBindingChangeListener keyBindingChangeListener = new KeyBindingChangeListener();
|
||||||
|
|
||||||
|
private JPanel shortcutPanel;
|
||||||
private UnselectableButtonGroup shortCutButtonGroup;
|
private UnselectableButtonGroup shortCutButtonGroup;
|
||||||
private FileChooserToggleButton myComputerButton;
|
private FileChooserToggleButton myComputerButton;
|
||||||
private FileChooserToggleButton desktopButton;
|
private FileChooserToggleButton desktopButton;
|
||||||
|
@ -261,12 +273,43 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
setPreferredSize(800, 600);
|
setPreferredSize(800, 600);
|
||||||
|
|
||||||
updateDirOnly(newModel.getHomeDirectory(), true);
|
updateDirOnly(newModel.getHomeDirectory(), true);
|
||||||
|
|
||||||
|
createActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
// Setup Methods
|
// Setup Methods
|
||||||
//==================================================================================================
|
//==================================================================================================
|
||||||
|
|
||||||
|
private void createActions() {
|
||||||
|
|
||||||
|
String owner = getClass().getSimpleName();
|
||||||
|
upAction = new ActionBuilder("Up One Level", owner)
|
||||||
|
.keyBinding("Alt Up")
|
||||||
|
.onAction(c -> goUp())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
backAction = new ActionBuilder("Last Folder Visited", owner)
|
||||||
|
.keyBinding("Alt Left")
|
||||||
|
.onAction(c -> goBack())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
forwardAction = new ActionBuilder("Previous Folder Visited", owner)
|
||||||
|
.keyBinding("Alt Right")
|
||||||
|
.onAction(c -> goForward())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
upAction.addPropertyChangeListener(keyBindingChangeListener);
|
||||||
|
backAction.addPropertyChangeListener(keyBindingChangeListener);
|
||||||
|
forwardAction.addPropertyChangeListener(keyBindingChangeListener);
|
||||||
|
|
||||||
|
addAction(upAction);
|
||||||
|
addAction(backAction);
|
||||||
|
addAction(forwardAction);
|
||||||
|
|
||||||
|
updateNavigationButtonToolTips();
|
||||||
|
}
|
||||||
|
|
||||||
private JComponent buildWorkPanel() {
|
private JComponent buildWorkPanel() {
|
||||||
buildWaitPanel();
|
buildWaitPanel();
|
||||||
|
|
||||||
|
@ -302,7 +345,8 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildShortCutPanel() {
|
private JPanel buildShortCutPanel() {
|
||||||
myComputerButton = new FileChooserToggleButton("My Computer") {
|
|
||||||
|
myComputerButton = new FileChooserToggleButton("My Computer", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return MY_COMPUTER;
|
return MY_COMPUTER;
|
||||||
|
@ -314,7 +358,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
myComputerButton.addActionListener(e -> updateMyComputer());
|
myComputerButton.addActionListener(e -> updateMyComputer());
|
||||||
myComputerButton.setForeground(FOREROUND_COLOR);
|
myComputerButton.setForeground(FOREROUND_COLOR);
|
||||||
|
|
||||||
desktopButton = new FileChooserToggleButton("Desktop") {
|
desktopButton = new FileChooserToggleButton("Desktop", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return fileChooserModel.getDesktopDirectory();
|
return fileChooserModel.getDesktopDirectory();
|
||||||
|
@ -327,7 +371,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
desktopButton.setForeground(FOREROUND_COLOR);
|
desktopButton.setForeground(FOREROUND_COLOR);
|
||||||
desktopButton.setEnabled(fileChooserModel.getDesktopDirectory() != null);
|
desktopButton.setEnabled(fileChooserModel.getDesktopDirectory() != null);
|
||||||
|
|
||||||
homeButton = new FileChooserToggleButton("Home") {
|
homeButton = new FileChooserToggleButton("Home", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return fileChooserModel.getHomeDirectory();
|
return fileChooserModel.getHomeDirectory();
|
||||||
|
@ -339,7 +383,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
homeButton.addActionListener(e -> updateHome());
|
homeButton.addActionListener(e -> updateHome());
|
||||||
homeButton.setForeground(FOREROUND_COLOR);
|
homeButton.setForeground(FOREROUND_COLOR);
|
||||||
|
|
||||||
downloadsButton = new FileChooserToggleButton("Downloads") {
|
downloadsButton = new FileChooserToggleButton("Downloads", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return fileChooserModel.getDownloadsDirectory();
|
return fileChooserModel.getDownloadsDirectory();
|
||||||
|
@ -350,7 +394,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
downloadsButton.addActionListener(e -> updateDownloads());
|
downloadsButton.addActionListener(e -> updateDownloads());
|
||||||
downloadsButton.setForeground(FOREROUND_COLOR);
|
downloadsButton.setForeground(FOREROUND_COLOR);
|
||||||
|
|
||||||
recentButton = new FileChooserToggleButton("Recent") {
|
recentButton = new FileChooserToggleButton("Recent", this) {
|
||||||
@Override
|
@Override
|
||||||
File getFile() {
|
File getFile() {
|
||||||
return RECENT;
|
return RECENT;
|
||||||
|
@ -369,27 +413,30 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
shortCutButtonGroup.add(downloadsButton);
|
shortCutButtonGroup.add(downloadsButton);
|
||||||
shortCutButtonGroup.add(recentButton);
|
shortCutButtonGroup.add(recentButton);
|
||||||
|
|
||||||
JPanel shortCutPanel = new JPanel(new GridLayout(0, 1));
|
shortcutPanel = new JPanel(new GridLayout(0, 1));
|
||||||
shortCutPanel.getAccessibleContext().setAccessibleName("Short Cut");
|
shortcutPanel.getAccessibleContext().setAccessibleName("Short Cut");
|
||||||
DockingUtils.setTransparent(shortCutPanel);
|
DockingUtils.setTransparent(shortcutPanel);
|
||||||
shortCutPanel.add(myComputerButton);
|
shortcutPanel.add(myComputerButton);
|
||||||
shortCutPanel.add(desktopButton);
|
shortcutPanel.add(desktopButton);
|
||||||
shortCutPanel.add(homeButton);
|
shortcutPanel.add(homeButton);
|
||||||
shortCutPanel.add(downloadsButton);
|
shortcutPanel.add(downloadsButton);
|
||||||
shortCutPanel.add(recentButton);
|
shortcutPanel.add(recentButton);
|
||||||
|
|
||||||
JPanel panel = new JPanel(new BorderLayout());
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
panel.setBorder(BorderFactory.createLoweredBevelBorder());
|
panel.setBorder(BorderFactory.createLoweredBevelBorder());
|
||||||
panel.setBackground(SHORTCUT_BACKGROUND_COLOR);
|
panel.setBackground(SHORTCUT_BACKGROUND_COLOR);
|
||||||
panel.add(shortCutPanel, BorderLayout.NORTH);
|
panel.add(shortcutPanel, BorderLayout.NORTH);
|
||||||
panel.getAccessibleContext().setAccessibleName("Short Cut");
|
panel.getAccessibleContext().setAccessibleName("Short Cut");
|
||||||
|
|
||||||
return panel;
|
return panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private JPanel buildFileNamePanel() {
|
private JPanel buildFileNamePanel() {
|
||||||
JLabel filenameLabel = new GDLabel("File name:");
|
JLabel filenameLabel = new GDLabel("Filename:");
|
||||||
|
|
||||||
FileDropDownSelectionDataModel model = new FileDropDownSelectionDataModel(this);
|
FileDropDownSelectionDataModel model = new FileDropDownSelectionDataModel(this);
|
||||||
filenameTextField = new DropDownSelectionTextField<>(model);
|
filenameTextField = new DropDownSelectionTextField<>(model);
|
||||||
|
filenameLabel.setLabelFor(filenameTextField);
|
||||||
filenameTextField.setMatchingWindowHeight(200);
|
filenameTextField.setMatchingWindowHeight(200);
|
||||||
filenameTextField.getAccessibleContext().setAccessibleName("Filename");
|
filenameTextField.getAccessibleContext().setAccessibleName("Filename");
|
||||||
filenameTextField.addCellEditorListener(new CellEditorListener() {
|
filenameTextField.addCellEditorListener(new CellEditorListener() {
|
||||||
|
@ -427,9 +474,10 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
JLabel filterLabel = new GLabel("Type:");
|
JLabel filterLabel = new GLabel("Type:");
|
||||||
filterLabel.getAccessibleContext().setAccessibleName("Filter");
|
filterLabel.getAccessibleContext().setAccessibleName("Filter");
|
||||||
filterCombo = new GComboBox<>();
|
filterCombo = new GComboBox<>();
|
||||||
|
filterLabel.setLabelFor(filterCombo);
|
||||||
filterCombo.setRenderer(GListCellRenderer.createDefaultTextRenderer(
|
filterCombo.setRenderer(GListCellRenderer.createDefaultTextRenderer(
|
||||||
fileFilter -> fileFilter != null ? fileFilter.getDescription() : ""));
|
fileFilter -> fileFilter != null ? fileFilter.getDescription() : ""));
|
||||||
filterCombo.getAccessibleContext().setAccessibleName("Filter");
|
filterCombo.getAccessibleContext().setAccessibleName("File Type Filter");
|
||||||
filterModel = (DefaultComboBoxModel<GhidraFileFilter>) filterCombo.getModel();
|
filterModel = (DefaultComboBoxModel<GhidraFileFilter>) filterCombo.getModel();
|
||||||
addFileFilter(GhidraFileFilter.ALL);
|
addFileFilter(GhidraFileFilter.ALL);
|
||||||
filterCombo.addItemListener(e -> rescanCurrentDirectory());
|
filterCombo.addItemListener(e -> rescanCurrentDirectory());
|
||||||
|
@ -444,22 +492,12 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
return filenamePanel;
|
return filenamePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class SelectionListener<T> implements DropDownSelectionChoiceListener<File> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void selectionChanged(File file) {
|
|
||||||
// take the selection and close the dialog
|
|
||||||
worker.schedule(new SetSelectedFileAndAcceptSelection(file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private JPanel buildHeaderPanel() {
|
private JPanel buildHeaderPanel() {
|
||||||
|
|
||||||
JPanel headerPanel = new JPanel(new GridBagLayout());
|
JPanel headerPanel = new JPanel(new GridBagLayout());
|
||||||
GridBagConstraints gbc = new GridBagConstraints();
|
GridBagConstraints gbc = new GridBagConstraints();
|
||||||
|
|
||||||
gbc.gridx = 0;
|
gbc.gridx = 0;
|
||||||
// gbc.insets = new Insets(PAD, PAD, PAD, PAD);
|
|
||||||
JButton[] navButtons = buildNavigationButtons();
|
JButton[] navButtons = buildNavigationButtons();
|
||||||
for (JButton element : navButtons) {
|
for (JButton element : navButtons) {
|
||||||
headerPanel.add(element, gbc);
|
headerPanel.add(element, gbc);
|
||||||
|
@ -613,6 +651,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
}
|
}
|
||||||
|
|
||||||
private JButton[] buildNavigationButtons() {
|
private JButton[] buildNavigationButtons() {
|
||||||
|
|
||||||
backButton = new EmptyBorderButton(ICON_BACK);
|
backButton = new EmptyBorderButton(ICON_BACK);
|
||||||
backButton.setName("BACK_BUTTON");
|
backButton.setName("BACK_BUTTON");
|
||||||
backButton.setEnabled(false);
|
backButton.setEnabled(false);
|
||||||
|
@ -625,12 +664,13 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
forwardButton.setToolTipText("Go to previous folder visited");
|
forwardButton.setToolTipText("Go to previous folder visited");
|
||||||
forwardButton.addActionListener(e -> goForward());
|
forwardButton.addActionListener(e -> goForward());
|
||||||
|
|
||||||
upLevelButton = new EmptyBorderButton(ICON_UP);
|
upButton = new EmptyBorderButton(ICON_UP);
|
||||||
upLevelButton.setName(UP_BUTTON_NAME);
|
upButton.setName(UP_BUTTON_NAME);
|
||||||
upLevelButton.setToolTipText("Up one level");
|
upButton.setEnabled(false);
|
||||||
upLevelButton.addActionListener(e -> goUpOneDirectoryLevel());
|
upButton.setToolTipText("Up one level");
|
||||||
|
upButton.addActionListener(e -> goUp());
|
||||||
|
|
||||||
return new JButton[] { backButton, forwardButton, upLevelButton };
|
return new JButton[] { backButton, forwardButton, upButton };
|
||||||
}
|
}
|
||||||
|
|
||||||
private JButton[] buildNonNavigationButtons() {
|
private JButton[] buildNonNavigationButtons() {
|
||||||
|
@ -1382,15 +1422,17 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doSetSelectedFileAndUpdateDisplay(File file) {
|
private void doSetSelectedFileAndUpdateDisplay(File file) {
|
||||||
if (lastInputFocus != null) {
|
|
||||||
lastInputFocus.requestFocusInWindow();
|
Component toFocus = getRestoreFocusComponent();
|
||||||
|
if (toFocus != null) {
|
||||||
|
toFocus.requestFocusInWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// SCR 4513 - exception if we don't cancel edits before changing the display
|
// exception if we don't cancel edits before changing the display
|
||||||
cancelEdits();
|
cancelEdits();
|
||||||
selectedFiles.setFile(file);
|
selectedFiles.setFile(file);
|
||||||
updateTextFieldForFile(file);
|
updateTextFieldForFile(file);
|
||||||
|
@ -1398,6 +1440,22 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
directoryModel.setSelectedFile(file); // the list or table display
|
directoryModel.setSelectedFile(file); // the list or table display
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Component getRestoreFocusComponent() {
|
||||||
|
// ensure we transfer focus to the directory or table when the view switches
|
||||||
|
if (isTableShowing()) {
|
||||||
|
if (lastInputFocus == directoryList) {
|
||||||
|
lastInputFocus = directoryTable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (lastInputFocus == directoryTable) {
|
||||||
|
lastInputFocus = directoryList;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastInputFocus;
|
||||||
|
}
|
||||||
|
|
||||||
private void updateTextFieldForFile(File file) {
|
private void updateTextFieldForFile(File file) {
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -1452,7 +1510,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void goUpOneDirectoryLevel() {
|
private void goUp() {
|
||||||
cancelEdits();
|
cancelEdits();
|
||||||
|
|
||||||
if (currentDirectory() == null) {
|
if (currentDirectory() == null) {
|
||||||
|
@ -1521,8 +1579,16 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
updateDirAndSelectFile(currentDir, currentSelectedFile, true, false);
|
updateDirAndSelectFile(currentDir, currentSelectedFile, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateShortcutPanel() {
|
void updateShortcutPanel() {
|
||||||
// make sure that if one of the shortcut buttons is selected, the directory matches that button
|
|
||||||
|
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
|
||||||
|
Component focusOwner = kfm.getFocusOwner();
|
||||||
|
if (focusOwner != null && !SwingUtilities.isDescendingFrom(focusOwner, shortcutPanel)) {
|
||||||
|
// only synchronize the button state if the user is not interacting with the buttons
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure the selected button matches the current directory
|
||||||
File currentDirectory = currentDirectory();
|
File currentDirectory = currentDirectory();
|
||||||
checkShortCutButton(myComputerButton, currentDirectory);
|
checkShortCutButton(myComputerButton, currentDirectory);
|
||||||
checkShortCutButton(homeButton, currentDirectory);
|
checkShortCutButton(homeButton, currentDirectory);
|
||||||
|
@ -1556,13 +1622,24 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
history.clear();
|
history.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void updateNavigationButtonToolTips() {
|
||||||
|
String tip = DockingToolBarUtils.createToolTipText(backButton, backAction);
|
||||||
|
backButton.setToolTipText(tip);
|
||||||
|
|
||||||
|
tip = DockingToolBarUtils.createToolTipText(forwardButton, forwardAction);
|
||||||
|
forwardButton.setToolTipText(tip);
|
||||||
|
|
||||||
|
tip = DockingToolBarUtils.createToolTipText(upButton, upAction);
|
||||||
|
upButton.setToolTipText(tip);
|
||||||
|
}
|
||||||
|
|
||||||
private void updateNavigationButtons() {
|
private void updateNavigationButtons() {
|
||||||
backButton.setEnabled(history.hasPrevious());
|
backButton.setEnabled(history.hasPrevious());
|
||||||
forwardButton.setEnabled(history.hasNext());
|
forwardButton.setEnabled(history.hasNext());
|
||||||
|
|
||||||
File dir = currentDirectory();
|
File dir = currentDirectory();
|
||||||
boolean enable = dir != null && dir.getParentFile() != null;
|
boolean enable = dir != null && dir.getParentFile() != null;
|
||||||
upLevelButton.setEnabled(enable);
|
upButton.setEnabled(enable);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateHistoryWithSelectedFiles(HistoryEntry historyEntry) {
|
private void updateHistoryWithSelectedFiles(HistoryEntry historyEntry) {
|
||||||
|
@ -2235,7 +2312,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
public void runSwing() {
|
public void runSwing() {
|
||||||
setDirectoryList(myComputerFile, roots);
|
setDirectoryList(myComputerFile, roots);
|
||||||
setWaitPanelVisible(false);
|
setWaitPanelVisible(false);
|
||||||
Swing.runLater(() -> doSetSelectedFileAndUpdateDisplay(null));
|
setSelectedFileAndUpdateDisplay(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2254,6 +2331,7 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
setCurrentDirectoryDisplay(recentFile, addToHistory);
|
setCurrentDirectoryDisplay(recentFile, addToHistory);
|
||||||
List<File> list = CollectionUtils.asList(recentList, File.class);
|
List<File> list = CollectionUtils.asList(recentList, File.class);
|
||||||
setDirectoryList(recentFile, list);
|
setDirectoryList(recentFile, list);
|
||||||
|
setSelectedFileAndUpdateDisplay(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2430,4 +2508,26 @@ public class GhidraFileChooser extends ReusableDialogComponentProvider implement
|
||||||
return parentDir.getName() + selectedFilesText;
|
return parentDir.getName() + selectedFilesText;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SelectionListener<T> implements DropDownSelectionChoiceListener<File> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void selectionChanged(File file) {
|
||||||
|
// take the selection and close the dialog
|
||||||
|
worker.schedule(new SetSelectedFileAndAcceptSelection(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class KeyBindingChangeListener implements PropertyChangeListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void propertyChange(PropertyChangeEvent e) {
|
||||||
|
String name = e.getPropertyName();
|
||||||
|
if (name.equals(DockingActionIf.KEYBINDING_DATA_PROPERTY)) {
|
||||||
|
updateNavigationButtonToolTips();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,8 @@ public class GThemeDefaults {
|
||||||
public static final GColor YELLOW = getColor("yellow");
|
public static final GColor YELLOW = getColor("yellow");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new {@link GColor} for the given palette name.
|
* Returns a new {@link GColor} for the given palette name. The name should not include
|
||||||
|
* the prefix {@code color.palette}.
|
||||||
* <p>
|
* <p>
|
||||||
* For a list of supported palette IDs, see {@code gui.palette.theme.properties}.
|
* For a list of supported palette IDs, see {@code gui.palette.theme.properties}.
|
||||||
* <p>
|
* <p>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue