/* ### * IP: GHIDRA * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package docking; import java.awt.*; import java.awt.event.*; import javax.swing.*; import javax.swing.plaf.basic.BasicSeparatorUI; import javax.swing.table.TableCellRenderer; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.tree.DefaultTreeCellRenderer; import javax.swing.tree.TreeCellRenderer; import javax.swing.undo.UndoableEdit; import docking.widgets.button.GRadioButton; import docking.widgets.checkbox.GCheckBox; import docking.widgets.checkbox.GHtmlCheckBox; import docking.widgets.combobox.GComboBox; import docking.widgets.combobox.GhidraComboBox; import docking.widgets.label.*; import docking.widgets.list.GList; import docking.widgets.list.GListCellRenderer; import docking.widgets.table.GTableCellRenderer; import docking.widgets.tree.support.GTreeRenderer; import ghidra.docking.util.DockingWindowsLookAndFeelUtils; import ghidra.util.HTMLUtilities; import resources.ResourceManager; /** *
* Before using a native Java UI component, search for a corresponding 'G'hidra component, and * if possible choose the non-HTML version of that component (if available). *
* For instance, instead of using {@link JLabel}, use either {@link GLabel} or {@link GHtmlLabel} * (and their variants). *
* (native JLabel, JCheckbox, etc, usage is actually disallowed in the Ghidra project) *
* When using a UI component that is HTML enabled, care must be used when constructing the text * that is being rendered. *
* During string-building or concatenation, appending a non-literal string value (ie. * {@code "Hello " + getFoo();} ), the non-literal string value should be escaped using * {@link HTMLUtilities#escapeHTML(String)} (ie. {@code "Hello " + HTMLUtilities.escapeHTML(getFoo());}. *
* Of course, there are exceptions to every rule, and if the string value can be definitely be * traced to its source and there are no user-supplied origins, the HTML escaping can be skipped. *
* Note: just using a UI component that is HTML enabled does not mean that it will treat its * text as HTML text. If you need to HTML escape any values that are being fed to the component, you * need to force the HTML mode 'on' by pre-pending a "<HTML>" at the beginning of the string. * If you fail to do this, the escaped substrings will look wrong because any '<' and '>' chars * (and others) in the substring will be mangled when rendered in plain-text mode. *
* When working with plain text, try to avoid allowing a user supplied string being the first * value of text that could be fed to a UI component. This will prevent the possibly hostile * string from having a leading HTML start tag. * (ie. when displaying an error to the user about a bad file, don't put the filename * value at the start of the string, but instead put a quote or some other delimiter to prevent * html mode). *
*
*
Native Component | Recommended Component |
---|---|
{@link JLabel} | {@link GLabel} {@link GDLabel} {@link GHtmlLabel} {@link GDHtmlLabel} {@link GIconLabel} |
{@link JCheckBox} | {@link GCheckBox} {@link GHtmlCheckBox} |
{@link JComboBox} | {@link GComboBox} {@link GhidraComboBox} |
{@link JList} | {@link GList} |
{@link ListCellRenderer} {@link DefaultListCellRenderer} | {@link GListCellRenderer} |
{@link TableCellRenderer} | {@link GTableCellRenderer} |
{@link TreeCellRenderer} {@link DefaultTreeCellRenderer} | {@link GTreeRenderer}DnDTreeCellRenderer |
{@link JRadioButton} | {@link GRadioButton} |
{@link JButton} | ???tbd??? |
control
key. On Mac, it is the command
key.
*
* @param mouseEvent the event to check
* @return true if the control key is pressed
*/
public static boolean isControlModifier(MouseEvent mouseEvent) {
int modifiers = mouseEvent.getModifiersEx();
int osSpecificMask = CONTROL_KEY_MODIFIER_MASK;
return (modifiers & osSpecificMask) == osSpecificMask;
}
/**
* Checks if the mouseEvent has the "control" key down. On windows, this is actually
* the control
key. On Mac, it is the command
key.
*
* @param keyEvent the event to check
* @return true if the control key is pressed
*/
public static boolean isControlModifier(KeyEvent keyEvent) {
int modifiers = keyEvent.getModifiersEx();
int osSpecificMask = CONTROL_KEY_MODIFIER_MASK;
return (modifiers & osSpecificMask) == osSpecificMask;
}
public static UndoRedoKeeper installUndoRedo(JTextComponent textComponent) {
Document document = textComponent.getDocument();
final UndoRedoKeeper undoRedoKeeper = new UndoRedoKeeper();
document.addUndoableEditListener(e -> {
UndoableEdit edit = e.getEdit();
undoRedoKeeper.addUndo(edit);
});
// need a key listener
textComponent.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e);
if (REDO_KEYSTROKE.equals(keyStrokeForEvent)) {
undoRedoKeeper.redo();
}
else if (UNDO_KEYSTROKE.equals(keyStrokeForEvent)) {
undoRedoKeeper.undo();
}
}
});
return undoRedoKeeper;
}
/**
* A callback to operate on a component
* @param
* Notes
* Historically, to make a component transparent you would call
* {@link JComponent#setOpaque(boolean)} with a false
value. However, it turns out
* that the definition and the implementation of this method are at odds. setOpaque(false)
* is meant to signal that some part of the component is transparent, so the parent component
* needs to be painted. Most LaFs implemented this by not painting the background of the
* component, but used the parent's color instead. The Nimbus LaF actually honors the
* contract of setOpaque()
, which has the effect of painting the components
* background by default.
*
* This method allows components to achieve transparency when they used to
* rely on setOpaque(false)
.
*
* @param c the component to be made transparent
*/
public static void setTransparent(JComponent c) {
doSetTransparent(c);
if (c instanceof JScrollPane) {
doSetTransparent(((JScrollPane) c).getViewport());
}
}
private static void doSetTransparent(JComponent c) {
// transparent...
c.setOpaque(false);
// ...I really mean it!
if (!(c instanceof JViewport)) {
// ugly, I know, but you cannot do this
c.setBorder(BorderFactory.createEmptyBorder());
}
c.setBackground(new Color(0, 0, 0, 0));
}
/** Hides any open tooltip window */
public static void hideTipWindow() {
// This is a hack, since Java's manager doesn't have this method
javax.swing.ToolTipManager.sharedInstance().setEnabled(false);
javax.swing.ToolTipManager.sharedInstance().setEnabled(true);
// TODO: Ultimately, the ESCAPE key binding in the Java TTM should hide any visible tooltips. We
// need to look into why this isn't working.
}
}