GP-4519 fixed comment and comment history dialogs for accessibility

This commit is contained in:
ghidragon 2024-04-15 14:20:07 -04:00
parent 6c60bd0313
commit 5e91d02748
4 changed files with 87 additions and 116 deletions

View File

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,119 +15,63 @@
*/
package ghidra.app.plugin.core.comments;
import ghidra.app.util.HelpTopics;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.listing.CodeUnit;
import ghidra.program.model.listing.Program;
import ghidra.util.HelpLocation;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import docking.ActionContext;
import docking.DialogComponentProvider;
import ghidra.app.util.HelpTopics;
import ghidra.program.model.listing.CodeUnit;
import ghidra.util.HelpLocation;
/**
* Dialog to show comment history; has a tab for each comment type to show
* history of changes to the comment.
*/
public class CommentHistoryDialog extends DialogComponentProvider implements ChangeListener {
private Program program;
private CodeUnit codeUnit;
public class CommentHistoryDialog extends DialogComponentProvider {
private JTabbedPane tabbedPane;
private CommentHistoryPanel eolPanel;
private CommentHistoryPanel prePanel;
private CommentHistoryPanel postPanel;
private CommentHistoryPanel platePanel;
private CommentHistoryPanel repeatablePanel;
private final static int[] COMMENT_INDEXES =
{CodeUnit.EOL_COMMENT,
CodeUnit.PRE_COMMENT,
CodeUnit.POST_COMMENT,
CodeUnit.PLATE_COMMENT,
CodeUnit.REPEATABLE_COMMENT};
/**
* Construct a new CommentHistoryDialog
* @param parent parent of this dialog
*/
CommentHistoryDialog() {
CommentHistoryDialog(CodeUnit cu, int initialCommentType) {
super("Show Comment History");
setHelpLocation(new HelpLocation(HelpTopics.COMMENTS, "Show_Comment_History"));
addWorkPanel(buildMainPanel());
setHelpLocation(new HelpLocation(HelpTopics.COMMENTS, "Show_Comment_History"));
addWorkPanel(buildMainPanel(cu, initialCommentType));
addDismissButton();
setPreferredSize(500, 300);
}
/* (non Javadoc)
* @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
*/
public void stateChanged(ChangeEvent e) {
int index = tabbedPane.getSelectedIndex();
CommentHistoryPanel panel = getHistoryPanel(COMMENT_INDEXES[index]);
panel.showCommentHistory(program, codeUnit.getMinAddress());
}
void showDialog(CodeUnit cu, int commentType, PluginTool tool, ActionContext context) {
codeUnit = cu;
program = cu.getProgram();
CommentHistoryPanel panel = getHistoryPanel(commentType);
panel.showCommentHistory(program, cu.getMinAddress());
tabbedPane.removeChangeListener(this);
for (int i=0;i<COMMENT_INDEXES.length; i++) {
if (COMMENT_INDEXES[i] == commentType) {
tabbedPane.setSelectedIndex(i);
break;
}
}
tabbedPane.addChangeListener(this);
tool.showDialog( this, context.getComponentProvider() );
}
private JPanel buildMainPanel() {
private JPanel buildMainPanel(CodeUnit cu, int initialCommentType) {
JPanel mainPanel = new JPanel(new BorderLayout());
tabbedPane = new JTabbedPane();
mainPanel.add(tabbedPane);
eolPanel = new CommentHistoryPanel(CodeUnit.EOL_COMMENT);
prePanel = new CommentHistoryPanel(CodeUnit.PRE_COMMENT);
postPanel = new CommentHistoryPanel(CodeUnit.POST_COMMENT);
platePanel = new CommentHistoryPanel(CodeUnit.PLATE_COMMENT);
repeatablePanel = new CommentHistoryPanel(CodeUnit.REPEATABLE_COMMENT);
// Note that we don't add a keylistener here as in some other tab panes. This is because
// the tab components are not focusable so there is no need to try and process a "space"
// key. Instead, the history for each comment type is also added as a tooltip on its
// corresponding tab. This will cause a screen reader to read the history for a tab
// when it is selected.
eolPanel = new CommentHistoryPanel(CodeUnit.EOL_COMMENT, cu);
prePanel = new CommentHistoryPanel(CodeUnit.PRE_COMMENT, cu);
postPanel = new CommentHistoryPanel(CodeUnit.POST_COMMENT, cu);
platePanel = new CommentHistoryPanel(CodeUnit.PLATE_COMMENT, cu);
repeatablePanel = new CommentHistoryPanel(CodeUnit.REPEATABLE_COMMENT, cu);
addTab(" EOL Comment ", eolPanel);
addTab(" Pre Comment ", prePanel);
addTab(" Post Comment ", postPanel);
addTab(" Plate Comment ", platePanel);
addTab(" Repeatable Comment ", repeatablePanel);
JScrollPane sp = new JScrollPane(eolPanel);
JViewport vp = sp.getViewport();
Dimension d = vp.getPreferredSize();
sp.getViewport().setPreferredSize(new Dimension(500, d.height*7));
tabbedPane.addTab(" EOL Comment ",sp);
tabbedPane.addTab(" Pre Comment ",new JScrollPane(prePanel));
tabbedPane.addTab(" Post Comment ",new JScrollPane(postPanel));
tabbedPane.addTab(" Plate Comment ",new JScrollPane(platePanel));
tabbedPane.addTab(" Repeatable Comment ", new JScrollPane(repeatablePanel));
tabbedPane.addChangeListener(this);
return mainPanel;
}
private CommentHistoryPanel getHistoryPanel(int commentType) {
switch(commentType) {
case CodeUnit.EOL_COMMENT:
return eolPanel;
case CodeUnit.PRE_COMMENT:
return prePanel;
case CodeUnit.POST_COMMENT:
return postPanel;
case CodeUnit.PLATE_COMMENT:
return platePanel;
case CodeUnit.REPEATABLE_COMMENT:
return repeatablePanel;
}
return null;
private void addTab(String title, CommentHistoryPanel panel) {
JScrollPane sp = new JScrollPane(panel);
tabbedPane.addTab(title, null, sp, panel.getHistory());
}
}

View File

@ -24,8 +24,7 @@ import javax.swing.text.*;
import generic.theme.*;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.CommentHistory;
import ghidra.program.model.listing.Program;
import ghidra.program.model.listing.*;
import ghidra.util.DateUtils;
import ghidra.util.Msg;
@ -47,16 +46,13 @@ class CommentHistoryPanel extends JPanel {
private int commentType;
/**
* Construct a new CommentHistoryPanel
* @param commentType comment type
*/
CommentHistoryPanel(int commentType) {
CommentHistoryPanel(int commentType, CodeUnit cu) {
super(new BorderLayout());
setUpAttributes();
this.commentType = commentType;
create();
showCommentHistory(cu.getProgram(), cu.getMinAddress());
}
/**
@ -64,7 +60,7 @@ class CommentHistoryPanel extends JPanel {
* @param program program
* @param addr address of comment history
*/
void showCommentHistory(Program program, Address addr) {
private void showCommentHistory(Program program, Address addr) {
textPane.setText("");
@ -89,6 +85,10 @@ class CommentHistoryPanel extends JPanel {
private void create() {
textPane = new JTextPane();
textPane.setEditable(false);
// Note that this panel in not focusable which means keyboard users can't navigate to
// this text pane and therefore screen readers won't read it. To compensate for this
// the history has been added as a tooltip on each tab.
textPane.setFocusable(false);
add(textPane, BorderLayout.CENTER);
doc = textPane.getStyledDocument();
}
@ -126,4 +126,14 @@ class CommentHistoryPanel extends JPanel {
StyleConstants.setTabSet(tabAttrSet, new TabSet(new TabStop[] { tabs }));
}
public String getHistory() {
try {
return doc.getText(0, doc.getLength());
}
catch (BadLocationException e) {
Msg.error(this, "This should never happen as the indexes came from the document!");
return "";
}
}
}

View File

@ -278,6 +278,12 @@ public class CommentsDialog extends ReusableDialogComponentProvider implements K
eolField = new JTextArea(5, 80);
repeatableField = new JTextArea(5, 80);
preField.getAccessibleContext().setAccessibleName("Pre Comment");
postField.getAccessibleContext().setAccessibleName("Post Comment");
plateField.getAccessibleContext().setAccessibleName("Plate Comment");
eolField.getAccessibleContext().setAccessibleName("EOL Comment");
repeatableField.getAccessibleContext().setAccessibleName("Repeatable Comment");
DocumentListener dl = new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
@ -339,8 +345,24 @@ public class CommentsDialog extends ReusableDialogComponentProvider implements K
tab.addTab(" Plate Comment ", new JScrollPane(plateField));
tab.addTab(" Repeatable Comment ", new JScrollPane(repeatableField));
tab.addChangeListener(ev -> chooseFocus());
tab.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_SPACE || keyCode == KeyEvent.VK_ENTER) {
focusSelectedTab();
e.consume();
}
}
});
tab.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1) {
focusSelectedTab();
}
}
});
ActionListener addAnnotationAction = e -> {
JTextArea currentTextArea = getSelectedTextArea();
for (AnnotationAdapterWrapper annotation : annotations) {
@ -428,7 +450,7 @@ public class CommentsDialog extends ReusableDialogComponentProvider implements K
}
}
private void chooseFocus() {
private void focusSelectedTab() {
getSelectedTextArea().requestFocus();
}

View File

@ -24,7 +24,6 @@ import ghidra.app.cmd.comments.SetCommentsCmd;
import ghidra.app.context.ListingActionContext;
import ghidra.app.context.ListingContextAction;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.framework.cmd.Command;
import ghidra.framework.options.*;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
@ -118,8 +117,8 @@ public class CommentsPlugin extends Plugin implements OptionsChangeListener {
plateComment = (plateComment.length() == 0) ? null : plateComment;
repeatableComment = (repeatableComment.length() == 0) ? null : repeatableComment;
Command cmd = new SetCommentsCmd(cu.getMinAddress(), preComment, postComment, eolComment,
plateComment, repeatableComment);
SetCommentsCmd cmd = new SetCommentsCmd(cu.getMinAddress(), preComment, postComment,
eolComment, plateComment, repeatableComment);
tool.execute(cmd, cu.getProgram());
}
@ -131,7 +130,7 @@ public class CommentsPlugin extends Plugin implements OptionsChangeListener {
*/
void deleteComments(Program program, ProgramLocation loc) {
int commentType = CommentType.getCommentType(null, loc, CodeUnit.EOL_COMMENT);
Command cmd = new SetCommentCmd(loc.getByteAddress(), commentType, null);
SetCommentCmd cmd = new SetCommentCmd(loc.getByteAddress(), commentType, null);
tool.execute(cmd, program);
}
@ -228,9 +227,9 @@ public class CommentsPlugin extends Plugin implements OptionsChangeListener {
private void showCommentHistory(ListingActionContext context) {
CodeUnit cu = context.getCodeUnit();
ProgramLocation loc = context.getLocation();
CommentHistoryDialog historyDialog = new CommentHistoryDialog();
historyDialog.showDialog(cu, CommentType.getCommentType(null, loc, CodeUnit.EOL_COMMENT),
tool, context);
int commentType = CommentType.getCommentType(null, loc, CodeUnit.EOL_COMMENT);
CommentHistoryDialog historyDialog = new CommentHistoryDialog(cu, commentType);
tool.showDialog(historyDialog, context.getComponentProvider());
}
private void updatePopupPath(DockingAction action, String actionString, ProgramLocation loc) {
@ -242,9 +241,8 @@ public class CommentsPlugin extends Plugin implements OptionsChangeListener {
if (loc instanceof FunctionRepeatableCommentFieldLocation) {
action.getPopupMenuData()
.setMenuPath(
new String[] { "Comments",
actionString + " Repeatable Comment" + endString });
.setMenuPath(new String[] { "Comments",
actionString + " Repeatable Comment" + endString });
return;
}
@ -266,9 +264,8 @@ public class CommentsPlugin extends Plugin implements OptionsChangeListener {
case CodeUnit.POST_COMMENT:
action.getPopupMenuData()
.setMenuPath(
new String[] { "Comments",
actionString + " Post-Comment" + endString });
.setMenuPath(new String[] { "Comments",
actionString + " Post-Comment" + endString });
break;
case CodeUnit.EOL_COMMENT:
@ -279,9 +276,8 @@ public class CommentsPlugin extends Plugin implements OptionsChangeListener {
case CodeUnit.REPEATABLE_COMMENT:
action.getPopupMenuData()
.setMenuPath(
new String[] { "Comments",
actionString + " Repeatable Comment" + endString });
.setMenuPath(new String[] { "Comments",
actionString + " Repeatable Comment" + endString });
break;
}
}