Merge remote-tracking branch 'origin/patch'

Conflicts:
	Ghidra/Features/Base/src/main/java/ghidra/app/plugin/core/help/ProcessorListPlugin.java
	Ghidra/Framework/Gui/src/main/java/ghidra/util/HTMLUtilities.java
This commit is contained in:
Ryan Kurtz 2024-02-07 11:18:36 -05:00
commit 80d1031d01
50 changed files with 713 additions and 388 deletions

View file

@ -62,9 +62,12 @@ public class BuildGhidraJarScript extends GhidraScript {
builder.addExcludedFileExtension(".pdf"); builder.addExcludedFileExtension(".pdf");
File installDir = Application.getInstallationDirectory().getFile(true); File installDir = Application.getInstallationDirectory().getFile(true);
builder.buildJar(new File(installDir, "ghidra.jar"), null, monitor); File file = new File(installDir, "ghidra.jar");
builder.buildJar(file, null, monitor);
// uncomment the following line to create a src zip for debugging. // uncomment the following line to create a src zip for debugging.
// builder.buildSrcZip(new File(installDir, "GhidraSrc.zip"), monitor); // builder.buildSrcZip(new File(installDir, "GhidraSrc.zip"), monitor);
println("Finsished writing jar: " + file);
} }
} }

View file

@ -27,9 +27,7 @@ import ghidra.app.util.demangler.*;
import ghidra.app.util.importer.MessageLog; import ghidra.app.util.importer.MessageLog;
import ghidra.framework.options.*; import ghidra.framework.options.*;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.SourceType;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities; import ghidra.util.SystemUtilities;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;

View file

@ -49,7 +49,6 @@ import ghidra.program.model.listing.Function;
import ghidra.program.model.listing.Program; import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.Symbol; import ghidra.program.model.symbol.Symbol;
import ghidra.program.util.*; import ghidra.program.util.*;
import ghidra.util.HTMLUtilities;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.SwingUpdateManager; import ghidra.util.task.SwingUpdateManager;
@ -448,10 +447,10 @@ public class CallTreeProvider extends ComponentProviderAdapter {
navigateIncomingToggleAction.setSelected(isPrimary); navigateIncomingToggleAction.setSelected(isPrimary);
navigateIncomingToggleAction.setToolBarData(new ToolBarData( navigateIncomingToggleAction.setToolBarData(new ToolBarData(
Icons.NAVIGATE_ON_INCOMING_EVENT_ICON, navigationOptionsToolbarGroup, "2")); Icons.NAVIGATE_ON_INCOMING_EVENT_ICON, navigationOptionsToolbarGroup, "2"));
navigateIncomingToggleAction.setDescription(HTMLUtilities.toHTML("Incoming Navigation" + navigateIncomingToggleAction.setDescription("<html>Incoming Navigation" +
"<br><br>Toggle <b>On</b> - change the displayed " + "<br><br>Toggle <b>On</b> - change the displayed " +
"function on Listing navigation events" + "function on Listing navigation events" +
"<br>Toggled <b>Off</b> - don't change the displayed function on Listing navigation events")); "<br>Toggled <b>Off</b> - don't change the displayed function on Listing navigation events");
navigateIncomingToggleAction.setHelpLocation( navigateIncomingToggleAction.setHelpLocation(
new HelpLocation(plugin.getName(), "Call_Tree_Action_Incoming_Navigation")); new HelpLocation(plugin.getName(), "Call_Tree_Action_Incoming_Navigation"));
tool.addLocalAction(this, navigateIncomingToggleAction); tool.addLocalAction(this, navigateIncomingToggleAction);

View file

@ -124,7 +124,7 @@ public class ClearDialog extends DialogComponentProvider {
symbolsCb = new GCheckBox("Symbols"); symbolsCb = new GCheckBox("Symbols");
commentsCb = new GHtmlCheckBox( commentsCb = new GHtmlCheckBox(
"<HTML>Comments <FONT SIZE=\"2\">(does not affect automatic comments)</FONT>"); "<html>Comments <FONT SIZE=\"2\">(does not affect automatic comments)</FONT>");
commentsCb.setVerticalTextPosition(SwingConstants.TOP); commentsCb.setVerticalTextPosition(SwingConstants.TOP);
propertiesCb = new GCheckBox("Properties"); propertiesCb = new GCheckBox("Properties");
codeCb = new GCheckBox("Code"); codeCb = new GCheckBox("Code");

View file

@ -103,17 +103,17 @@ public class DataTypeListingHover extends AbstractConfigurableHover implements L
toolTipText = toolTipText.replace("Unsized", Integer.toString(dataLen)); toolTipText = toolTipText.replace("Unsized", Integer.toString(dataLen));
} }
if (dataInstance != null) { if (dataInstance != null) {
toolTipText = toolTipText.replace("</HTML>", toolTipText = toolTipText.replace("</html>",
getLocationSupplimentalToolTipText(dt, dataInstance) + "</HTML>"); getLocationSupplimentalToolTipText(dt, dataInstance) + "</html>");
} }
String warningMsg = ""; String warningMsg = "";
if (hasInvalidStorage) { if (hasInvalidStorage) {
warningMsg += "WARNING! Invalid Storage"; warningMsg += "WARNING! Invalid Storage";
} }
if (warningMsg.length() != 0) { if (warningMsg.length() != 0) {
String errorText = "<HTML><center><font color=\"" + Messages.ERROR.toHexString() + String errorText = "<html><center><font color=\"" + Messages.ERROR.toHexString() +
"\">" + warningMsg + "!</font></center><BR>"; "\">" + warningMsg + "!</font></center><BR>";
toolTipText = toolTipText.replace("<HTML>", errorText); toolTipText = toolTipText.replace("<html>", errorText);
} }
return createTooltipComponent(toolTipText); return createTooltipComponent(toolTipText);
} }

View file

@ -39,7 +39,7 @@ import ghidra.util.StringUtilities;
* The tool tip text shows relationships to key topological elements of the program relative to * The tool tip text shows relationships to key topological elements of the program relative to
* the address -- offset from image base, offset from current memory block; if the address is * the address -- offset from image base, offset from current memory block; if the address is
* within the bounds of a function, the offset from function entry point; if the address is within * within the bounds of a function, the offset from function entry point; if the address is within
* the bounds of defined data, the offset from the start of the data. * the bounds of defined data, the offset from the start of the data.
*/ */
public class ProgramAddressRelationshipListingHover extends AbstractConfigurableHover public class ProgramAddressRelationshipListingHover extends AbstractConfigurableHover
implements ListingHoverService { implements ListingHoverService {
@ -84,7 +84,7 @@ public class ProgramAddressRelationshipListingHover extends AbstractConfigurable
return null; return null;
} }
StringBuilder sb = new StringBuilder("<HTML><table>"); StringBuilder sb = new StringBuilder("<html><table>");
Address loc = programLocation.getAddress(); Address loc = programLocation.getAddress();
if (isInDefaultSpace(program, loc)) { if (isInDefaultSpace(program, loc)) {

View file

@ -130,7 +130,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
* Construct a bit field viewer/editor component. Non-edit use limits bit * Construct a bit field viewer/editor component. Non-edit use limits bit
* attribute computation to visible range only which facilitate use within * attribute computation to visible range only which facilitate use within
* scroll pane for very large structures. Edit use will determine bit attributes * scroll pane for very large structures. Edit use will determine bit attributes
* for full allocation size. * for full allocation size.
* @param composite composite data type to be viewed/modified. * @param composite composite data type to be viewed/modified.
* @param editUseEnabled if true use of editing bitfield editing/placement is * @param editUseEnabled if true use of editing bitfield editing/placement is
* supported, else viewing only. * supported, else viewing only.
@ -311,7 +311,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
* When {@link #editMode} is not {@link EditMode#NONE} the specified * When {@link #editMode} is not {@link EditMode#NONE} the specified
* bitSize and bitOffset specify the active edit bitfield. * bitSize and bitOffset specify the active edit bitfield.
* @param bitSize component bitsize * @param bitSize component bitsize
* @param bitOffset component lsb bit offset from lsb of allocation unit * @param bitOffset component lsb bit offset from lsb of allocation unit
* (allocation unit size is determine by current {@link #allocationByteSize}). * (allocation unit size is determine by current {@link #allocationByteSize}).
*/ */
void refresh(int bitSize, int bitOffset) { void refresh(int bitSize, int bitOffset) {
@ -339,7 +339,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
} }
/** /**
* Update the size and offset of the allocation unit. Since this can * Update the size and offset of the allocation unit. Since this can
* affect the size of this component, its bounds will be updated the component * affect the size of this component, its bounds will be updated the component
* repainted. * repainted.
* @param byteSize allocation byte size * @param byteSize allocation byte size
@ -413,9 +413,9 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
} }
return bitFieldAllocation.hasConflict; return bitFieldAllocation.hasConflict;
// TODO: Improve conflict detection with zero-length components. // TODO: Improve conflict detection with zero-length components.
// Zero-length components can share common offset, although // Zero-length components can share common offset, although
// zero-length components should have a lower ordinal than a // zero-length components should have a lower ordinal than a
// sized component at the same offset. // sized component at the same offset.
} }
@ -544,7 +544,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
/** /**
* Get the bit attributes object which corresponds to the specified point p within the * Get the bit attributes object which corresponds to the specified point p within the
* bounds of this component. NOTE: use of non-visible bitAttributes within the * bounds of this component. NOTE: use of non-visible bitAttributes within the
* allocation range requires edit use enablement (see {@link #editUseEnabled}). * allocation range requires edit use enablement (see {@link #editUseEnabled}).
* @param p point within the bounds of this component * @param p point within the bounds of this component
* @return bit attributes object or null * @return bit attributes object or null
@ -562,7 +562,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
} }
/** /**
* Get the bit attributes index which corresponds to the specified horizontal x position * Get the bit attributes index which corresponds to the specified horizontal x position
* within the bounds of this component. * within the bounds of this component.
* @param x horizontal x position within the bounds of this component * @param x horizontal x position within the bounds of this component
* @return bit attributes index or -1 if not found * @return bit attributes index or -1 if not found
@ -623,9 +623,9 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
conflictMsg += "<div style=\"color: red;font-style: italic\">overlaps " + conflictMsg += "<div style=\"color: red;font-style: italic\">overlaps " +
HTMLUtilities.escapeHTML(conflictTip) + "</div>"; HTMLUtilities.escapeHTML(conflictTip) + "</div>";
} }
return "<HTML><div style=\"text-align:center\">" + HTMLUtilities.escapeHTML(tip) + return "<html><div style=\"text-align:center\">" + HTMLUtilities.escapeHTML(tip) +
conflictMsg + conflictMsg +
"<div style=\"color: gray;font-style: italic\">(Shift-wheel to zoom)</div></div></HTML>"; "<div style=\"color: gray;font-style: italic\">(Shift-wheel to zoom)</div></div>";
} }
@Override @Override
@ -729,7 +729,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
int x = BYTE_SEPARATOR_THICKNESS; int x = BYTE_SEPARATOR_THICKNESS;
if (bitAttributes[0] != null && bitAttributes[0].leftEndType == EndBitType.TRUNCATED_END) { if (bitAttributes[0] != null && bitAttributes[0].leftEndType == EndBitType.TRUNCATED_END) {
// adjust left-most line to reflect truncated component // adjust left-most line to reflect truncated component
x -= BIT_SEPARATOR_THICKNESS; // backup to left line location x -= BIT_SEPARATOR_THICKNESS; // backup to left line location
drawTruncationLine(g, x, y, CELL_HEIGHT); drawTruncationLine(g, x, y, CELL_HEIGHT);
x += BIT_SEPARATOR_THICKNESS; x += BIT_SEPARATOR_THICKNESS;
@ -740,7 +740,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
BitAttributes prevAttrs = null; BitAttributes prevAttrs = null;
// Limit rendered bits to those contained within the visible view port // Limit rendered bits to those contained within the visible view port
// of this scrolled component. // of this scrolled component.
JViewport viewPort = (JViewport) getParent(); JViewport viewPort = (JViewport) getParent();
Rectangle bounds = viewPort.getViewRect(); Rectangle bounds = viewPort.getViewRect();
@ -850,9 +850,9 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
} }
/** /**
* <code>BitFieldPlacement</code> provides the ability to translate a * <code>BitFieldPlacement</code> provides the ability to translate a
* composite component to a bit-level placement within the allocation * composite component to a bit-level placement within the allocation
* range including the notion of clipped edges when one or both sides * range including the notion of clipped edges when one or both sides
* extend beyond the allocation range. * extend beyond the allocation range.
*/ */
private class BitFieldPlacement { private class BitFieldPlacement {
@ -883,7 +883,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
BitFieldDataType bitfield = (BitFieldDataType) component.getDataType(); BitFieldDataType bitfield = (BitFieldDataType) component.getDataType();
int storageSize = 8 * bitfield.getStorageSize(); int storageSize = 8 * bitfield.getStorageSize();
rightBit = leftAdj + storageSize - bitfield.getBitOffset() - 1; rightBit = leftAdj + storageSize - bitfield.getBitOffset() - 1;
// Use effective bit-size since non-packed uses are only concerned with actual // Use effective bit-size since non-packed uses are only concerned with actual
// bits stored (NOTE: this may cause a transition from declared to effective // bits stored (NOTE: this may cause a transition from declared to effective
// bit-size when editing a bitfield where the these bit-sizes differ). // bit-size when editing a bitfield where the these bit-sizes differ).
int bitSize = bitfield.getBitSize(); int bitSize = bitfield.getBitSize();
@ -913,7 +913,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
/** /**
* <code>BitFieldAllocation</code> provides the bit-level details within the * <code>BitFieldAllocation</code> provides the bit-level details within the
* allocation range including the optional overlay of an edit component * allocation range including the optional overlay of an edit component
* with confict detection. The bit-level details are defined via * with confict detection. The bit-level details are defined via
* {@link BitAttributes}. * {@link BitAttributes}.
*/ */
class BitFieldAllocation { class BitFieldAllocation {
@ -937,10 +937,10 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
private int leftChopBytes; private int leftChopBytes;
/** /**
* Bit attributes array reflects bit layout normalized to big-endian * Bit attributes array reflects bit layout normalized to big-endian
* layout where left-most allocation bit has array index of 0. In edit * layout where left-most allocation bit has array index of 0. In edit
* mode this array covers the full span of {@link #allocationByteSize}, while in * mode this array covers the full span of {@link #allocationByteSize}, while in
* non-edit mode the array size is reduced based upon visibility as * non-edit mode the array size is reduced based upon visibility as
* indicated by {@link #leftChopBytes} and {@link #rightChopBytes}. * indicated by {@link #leftChopBytes} and {@link #rightChopBytes}.
*/ */
private BitAttributes[] bitAttributes; private BitAttributes[] bitAttributes;
@ -1003,7 +1003,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
} }
/** /**
* Refresh the computed bitAttributes. When editUseEnabled is false * Refresh the computed bitAttributes. When editUseEnabled is false
* the computed bitAttributes will correspond to the visible portion * the computed bitAttributes will correspond to the visible portion
* of the component. This method does not handle changes to * of the component. This method does not handle changes to
* {@link #allocationByteSize} which require a new {@link BitFieldAllocation} * {@link #allocationByteSize} which require a new {@link BitFieldAllocation}
@ -1090,7 +1090,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
/** /**
* Allocate {@link #bitAttributes} for all structure members which reside * Allocate {@link #bitAttributes} for all structure members which reside
* within the byte range covered by {@link #allocationBytes}. * within the byte range covered by {@link #allocationBytes}.
* @param struct structure whose component bit attributes should be * @param struct structure whose component bit attributes should be
* computed. * computed.
*/ */
private void allocateStructureMembers(Structure struct) { private void allocateStructureMembers(Structure struct) {
@ -1126,7 +1126,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
/** /**
* Allocate {@link #bitAttributes} for the specified component within * Allocate {@link #bitAttributes} for the specified component within
* the byte range covered by {@link #allocationBytes}. * the byte range covered by {@link #allocationBytes}.
* @param dtc composite component * @param dtc composite component
* @param leftBit left bit index within the full {@link #allocationByteSize} * @param leftBit left bit index within the full {@link #allocationByteSize}
* where 0 is the left-most bit index. * where 0 is the left-most bit index.
@ -1200,7 +1200,7 @@ public class BitFieldPlacementComponent extends JPanel implements Scrollable {
} }
/** /**
* <code>BitAttributes</code> provide bit attributes which identify the * <code>BitAttributes</code> provide bit attributes which identify the
* associated component, a conflict component and left/right line * associated component, a conflict component and left/right line
* types to be displayed. * types to be displayed.
*/ */

View file

@ -394,10 +394,10 @@ public class CompEditorPanel extends CompositeEditorPanel {
new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Align")); new HelpLocation(provider.getHelpTopic(), provider.getHelpName() + "_" + "Align"));
} }
String alignmentToolTip = String alignmentToolTip =
"<HTML>The <B>align</B> control allows the overall minimum alignment of this<BR>" + "<html>The <B>align</B> control allows the overall minimum alignment of this<BR>" +
"data type to be specified. The actual computed alignment<BR>" + "data type to be specified. The actual computed alignment<BR>" +
"may be any multiple of this value. " + "<font color=\"" + "may be any multiple of this value. " + "<font color=\"" +
Palette.BLUE.toHexString() + "\" size=\"-2\">(&lt;F1&gt; for help)</HTML>"; Palette.BLUE.toHexString() + "\" size=\"-2\">(&lt;F1&gt; for help)</html>";
alignPanel.setToolTipText(alignmentToolTip); alignPanel.setToolTipText(alignmentToolTip);
addMinimumAlignmentComponents(); addMinimumAlignmentComponents();
@ -449,10 +449,10 @@ public class CompEditorPanel extends CompositeEditorPanel {
private void setupDefaultMinAlignButton() { private void setupDefaultMinAlignButton() {
defaultAlignButton.setName("Default Alignment"); defaultAlignButton.setName("Default Alignment");
String alignmentToolTip = "<HTML>Sets this data type to use <B>default</B> alignment.<BR>" + String alignmentToolTip = "<html>Sets this data type to use <B>default</B> alignment.<BR>" +
"If packing is disabled, the default will be 1 byte. If packing<BR>" + "If packing is disabled, the default will be 1 byte. If packing<BR>" +
"is enabled, the alignment is computed based upon the pack<BR>" + "is enabled, the alignment is computed based upon the pack<BR>" +
"setting and the alignment of each component data type.</HTML>"; "setting and the alignment of each component data type.</html>";
defaultAlignButton.addActionListener(e -> { defaultAlignButton.addActionListener(e -> {
((CompEditorModel) model).setAlignmentType(AlignmentType.DEFAULT, -1); ((CompEditorModel) model).setAlignmentType(AlignmentType.DEFAULT, -1);
@ -468,10 +468,10 @@ public class CompEditorPanel extends CompositeEditorPanel {
private void setupMachineMinAlignButton() { private void setupMachineMinAlignButton() {
machineAlignButton.setName("Machine Alignment"); machineAlignButton.setName("Machine Alignment");
String alignmentToolTip = String alignmentToolTip =
"<HTML>Sets this data type to use the <B>machine</B> alignment<BR>" + "<html>Sets this data type to use the <B>machine</B> alignment<BR>" +
"as specified by the compiler specification. If packing is<BR>" + "as specified by the compiler specification. If packing is<BR>" +
"enabled, the computed alignment of this composite should be<BR>" + "enabled, the computed alignment of this composite should be<BR>" +
"the machine alignment value.</HTML>"; "the machine alignment value.</html>";
machineAlignButton.setToolTipText(alignmentToolTip); machineAlignButton.setToolTipText(alignmentToolTip);
machineAlignButton.addActionListener(e -> { machineAlignButton.addActionListener(e -> {
@ -487,9 +487,9 @@ public class CompEditorPanel extends CompositeEditorPanel {
private void setupExplicitAlignButton() { private void setupExplicitAlignButton() {
explicitAlignButton.setName("Explicit Alignment"); explicitAlignButton.setName("Explicit Alignment");
String alignmentToolTip = String alignmentToolTip =
"<HTML>Sets this data type to use the <B>explicit</B> alignment value<BR>" + "<html>Sets this data type to use the <B>explicit</B> alignment value<BR>" +
"specified. If packing is enabled, the computed alignment of<BR>" + "specified. If packing is enabled, the computed alignment of<BR>" +
"this composite may be any multiple of this value.</HTML>"; "this composite may be any multiple of this value.</html>";
explicitAlignButton.setToolTipText(alignmentToolTip); explicitAlignButton.setToolTipText(alignmentToolTip);
explicitAlignButton.addActionListener(e -> { explicitAlignButton.addActionListener(e -> {
@ -555,10 +555,10 @@ public class CompEditorPanel extends CompositeEditorPanel {
private void setupActualAlignment() { private void setupActualAlignment() {
GridBagConstraints gridBagConstraints = new GridBagConstraints(); GridBagConstraints gridBagConstraints = new GridBagConstraints();
String actualAlignmentToolTip = String actualAlignmentToolTip =
"<HTML>The actual alignment to be used for this data type.<BR>" + "<html>The actual alignment to be used for this data type.<BR>" +
"A combination of the pack and alignment settings made to this datatype<BR>" + "A combination of the pack and alignment settings made to this datatype<BR>" +
"combined with alignments of the individual components are used to<BR>" + "combined with alignments of the individual components are used to<BR>" +
"to compute the actual alignment of this datatype.</HTML>"; "to compute the actual alignment of this datatype.</html>";
JPanel actualAlignmentPanel = new JPanel(new BorderLayout()); JPanel actualAlignmentPanel = new JPanel(new BorderLayout());
actualAlignmentLabel = new GDLabel("Alignment:"); actualAlignmentLabel = new GDLabel("Alignment:");
@ -672,10 +672,10 @@ public class CompEditorPanel extends CompositeEditorPanel {
private void setupPackingEnablementButton() { private void setupPackingEnablementButton() {
packingEnablementButton.setName("Packing Enablement"); packingEnablementButton.setName("Packing Enablement");
String packingToolTipText = String packingToolTipText =
"<HTML>Enable packing when details of all components are known (including sizing and" + "<html>Enable packing when details of all components are known (including sizing and" +
" alignment).<BR>" + "Disable packing when Reverse Engineering composite. " + " alignment).<BR>" + "Disable packing when Reverse Engineering composite. " +
"<font color=\"" + Palette.BLUE.toHexString() + "<font color=\"" + Palette.BLUE.toHexString() +
"\" size=\"-2\">(&lt;F1&gt; for help)</font></HTML>"; "\" size=\"-2\">(&lt;F1&gt; for help)</font></html>";
packingEnablementButton.addActionListener(e -> { packingEnablementButton.addActionListener(e -> {
((CompEditorModel) model).setPackingType( ((CompEditorModel) model).setPackingType(
packingEnablementButton.isSelected() ? PackingType.DEFAULT : PackingType.DISABLED, packingEnablementButton.isSelected() ? PackingType.DEFAULT : PackingType.DISABLED,
@ -692,7 +692,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
private void setupDefaultPackingButton() { private void setupDefaultPackingButton() {
defaultPackingButton.setName("Default Packing"); defaultPackingButton.setName("Default Packing");
String packingToolTipText = String packingToolTipText =
"<HTML>Indicates <B>default</B> compiler packing rules should be applied.</HTML>"; "<html>Indicates <B>default</B> compiler packing rules should be applied.</html>";
defaultPackingButton.addActionListener(e -> { defaultPackingButton.addActionListener(e -> {
((CompEditorModel) model).setPackingType(PackingType.DEFAULT, -1); ((CompEditorModel) model).setPackingType(PackingType.DEFAULT, -1);
@ -708,7 +708,7 @@ public class CompEditorPanel extends CompositeEditorPanel {
private void setupExplicitPackingButton() { private void setupExplicitPackingButton() {
explicitPackingButton.setName("Explicit Packing"); explicitPackingButton.setName("Explicit Packing");
String packingToolTipText = String packingToolTipText =
"<HTML>Indicates an explicit pack size should be applied.</HTML>"; "<html>Indicates an explicit pack size should be applied.</html>";
explicitPackingButton.addActionListener(e -> chooseByValuePacking()); explicitPackingButton.addActionListener(e -> chooseByValuePacking());
explicitPackingButton.setToolTipText(packingToolTipText); explicitPackingButton.setToolTipText(packingToolTipText);

View file

@ -94,8 +94,8 @@ public class DataTypeCellRenderer extends GTableCellRenderer {
displayName = HTMLUtilities.friendlyEncodeHTML(displayName); displayName = HTMLUtilities.friendlyEncodeHTML(displayName);
String toolTipText = ToolTipUtils.getToolTipText(dataType); String toolTipText = ToolTipUtils.getToolTipText(dataType);
String headerText = "<HTML><b>" + displayName + "</b><BR>"; String headerText = "<html><b>" + displayName + "</b><BR>";
toolTipText = toolTipText.replace("<HTML>", headerText); toolTipText = toolTipText.replace("<html>", headerText);
return toolTipText; return toolTipText;
} }
} }

View file

@ -273,8 +273,8 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
return matchingStylePanel; return matchingStylePanel;
} }
// toggles whether the structure being created is new, based upon the name field, or a current // toggles whether the structure being created is new, based upon the name field, or a current
// structure, based upon a structure in the table. This method updates the GUI to reflect the // structure, based upon a structure in the table. This method updates the GUI to reflect the
// current creation state. // current creation state.
private void setCreateStructureByName(boolean createStructureByName) { private void setCreateStructureByName(boolean createStructureByName) {
if (createStructureByName) { if (createStructureByName) {
@ -572,7 +572,7 @@ public class CreateStructureDialog extends ReusableDialogComponentProvider {
message = EXISITING_STRUCTURE_STATUS_PREFIX; message = EXISITING_STRUCTURE_STATUS_PREFIX;
} }
setStatusText("<HTML>" + message + "<BR>\"" + HTMLUtilities.escapeHTML(name) + "\""); setStatusText("<html>" + message + "<BR>\"" + HTMLUtilities.escapeHTML(name) + "\"");
} }
// this class is used instead of a cell renderer so that sorting will // this class is used instead of a cell renderer so that sorting will

View file

@ -117,7 +117,7 @@ public class DataTypeSynchronizer {
public static void commitAssumingTransactionsOpen(DataTypeManager sourceDTM, DataType refDT) { public static void commitAssumingTransactionsOpen(DataTypeManager sourceDTM, DataType refDT) {
// Must refresh associations of refDt and its dependencies to ensure that any // Must refresh associations of refDt and its dependencies to ensure that any
// non-sourced datatype is properly associated to the sourceDTM // non-sourced datatype is properly associated to the sourceDTM
DataTypeManager refDTM = refDT.getDataTypeManager(); DataTypeManager refDTM = refDT.getDataTypeManager();
SourceArchive sourceArchive = refDTM.getSourceArchive(sourceDTM.getUniversalID()); SourceArchive sourceArchive = refDTM.getSourceArchive(sourceDTM.getUniversalID());
@ -359,7 +359,7 @@ public class DataTypeSynchronizer {
// aesthetically pleasing // aesthetically pleasing
String spacerString = createHTMLSpacerString(htmlContent, otherContent); String spacerString = createHTMLSpacerString(htmlContent, otherContent);
StringBuilder buffy = new StringBuilder(); StringBuilder buffy = new StringBuilder();
buffy.append("<HTML>"); buffy.append("<html>");
// -we use CELLPADDING here to allow us to create a narrow column within the table // -we use CELLPADDING here to allow us to create a narrow column within the table
// -the CELLSPACING gives us some space around the narrow column // -the CELLSPACING gives us some space around the narrow column
@ -412,8 +412,8 @@ public class DataTypeSynchronizer {
private static String createHTMLSpacerString(String htmlContent, String otherHTMLContent) { private static String createHTMLSpacerString(String htmlContent, String otherHTMLContent) {
// unfortunately, to get the displayed widths, we have to have rendered content, which // unfortunately, to get the displayed widths, we have to have rendered content, which
// is what the JLabels below are doing for us // is what the JLabels below are doing for us
JLabel label1 = new GDHtmlLabel("<HTML>" + htmlContent); JLabel label1 = new GDHtmlLabel("<html>" + htmlContent);
JLabel label2 = new GDHtmlLabel("<HTML>" + otherHTMLContent); JLabel label2 = new GDHtmlLabel("<html>" + otherHTMLContent);
int maxPixelWidth = int maxPixelWidth =
Math.max(label1.getPreferredSize().width, label2.getPreferredSize().width); Math.max(label1.getPreferredSize().width, label2.getPreferredSize().width);

View file

@ -48,13 +48,13 @@ import ghidra.util.*;
import ghidra.util.exception.UsrException; import ghidra.util.exception.UsrException;
/** /**
* A plugin to disassemble the address at the current ProgramLocation and to * A plugin to disassemble the address at the current ProgramLocation and to
* display the Instruction. This work of this plugin is temporary in that it * display the Instruction. This work of this plugin is temporary in that it
* will not change the state of the program. * will not change the state of the program.
* *
* *
* *
* TODO Change the PseudoCodeUnit's getComment(int) method or change its * TODO Change the PseudoCodeUnit's getComment(int) method or change its
* getPreview(int) method not to call getComment(int) and then change * getPreview(int) method not to call getComment(int) and then change
* this class to not handle the UnsupportedOperationException. * this class to not handle the UnsupportedOperationException.
* TODO are the category and names correct? * TODO are the category and names correct?
@ -82,7 +82,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
new GColor("color.fg.plugin.disassembledview.address"); new GColor("color.fg.plugin.disassembledview.address");
/** /**
* The number of addresses that should be disassembled, including the * The number of addresses that should be disassembled, including the
* address of the current {@link ProgramLocation}. * address of the current {@link ProgramLocation}.
*/ */
private static final int LOOK_AHEAD_COUNT = 5; private static final int LOOK_AHEAD_COUNT = 5;
@ -94,7 +94,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
new DisassembledViewComponentProvider(); new DisassembledViewComponentProvider();
/** /**
* The last program location received from the * The last program location received from the
* {@link #locationChanged(ProgramLocation)} method. * {@link #locationChanged(ProgramLocation)} method.
*/ */
private ProgramLocation lastUpdatedLocation; private ProgramLocation lastUpdatedLocation;
@ -113,7 +113,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
public DisassembledViewPlugin(PluginTool plugintool) { public DisassembledViewPlugin(PluginTool plugintool) {
// We want to know about program activated events, location changed // We want to know about program activated events, location changed
// events and selection changed events. The first type we get from // events and selection changed events. The first type we get from
// our parent, the other two we get by passing true to our parent's // our parent, the other two we get by passing true to our parent's
// constructor // constructor
super(plugintool); super(plugintool);
} }
@ -153,8 +153,8 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
/** /**
* We want to make sure that we no longer have any contents when the * We want to make sure that we no longer have any contents when the
* program is deactivated so that we do not make any more calls to the * program is deactivated so that we do not make any more calls to the
* program or its plugins. * program or its plugins.
* *
* @param program The program being deactivated. * @param program The program being deactivated.
@ -189,7 +189,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
// we only want to update if: // we only want to update if:
// 1) we have a valid location, // 1) we have a valid location,
// 2) the location is different than the last location we processed, and // 2) the location is different than the last location we processed, and
// TODO: **Note: this step is believed to be a bug--we should only be // TODO: **Note: this step is believed to be a bug--we should only be
// getting one location change at a time, not two // getting one location change at a time, not two
// 3) the display is visible. // 3) the display is visible.
if (loc == null || loc.equals(lastUpdatedLocation) || !displayComponent.isVisible()) { if (loc == null || loc.equals(lastUpdatedLocation) || !displayComponent.isVisible()) {
@ -211,7 +211,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
/** /**
* Called when we receive program selection events. * Called when we receive program selection events.
* *
* @param selection The ProgramSelection object that is the current * @param selection The ProgramSelection object that is the current
* selection. * selection.
* @see ProgramPlugin#selectionChanged(ProgramSelection) * @see ProgramPlugin#selectionChanged(ProgramSelection)
*/ */
@ -222,19 +222,19 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
// TODO: // TODO:
// if there are multiple lines selected then we need to update the // if there are multiple lines selected then we need to update the
// display. Should we: // display. Should we:
// 1) Show each line selected with some sort of visual delimiter // 1) Show each line selected with some sort of visual delimiter
// between each value? // between each value?
// 2) Clear the display when there are multiple values selected? // 2) Clear the display when there are multiple values selected?
// 3) Do nothing and allow selections, while showing only the current // 3) Do nothing and allow selections, while showing only the current
// location as determined by the cursor? // location as determined by the cursor?
// //
// Currently solution 3) is used // Currently solution 3) is used
if (selection != null) { if (selection != null) {
if (containsMultipleSelection()) { if (containsMultipleSelection()) {
disassembleLocation(currentLocation); disassembleLocation(currentLocation);
// changed in SCR 6875 // changed in SCR 6875
// displayComponent.clearContents(); // displayComponent.clearContents();
} }
else if (selection.isEmpty()) { else if (selection.isEmpty()) {
@ -250,7 +250,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
/** /**
* Gets the pseudo disassembler used by this class. This method will lazy * Gets the pseudo disassembler used by this class. This method will lazy
* load the disassembler to prevent wasting of resources. If the * load the disassembler to prevent wasting of resources. If the
* program location changes, then the disassembler will be recreated. * program location changes, then the disassembler will be recreated.
* *
* @return the pseudo disassembler used by this class. * @return the pseudo disassembler used by this class.
@ -263,8 +263,8 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
/** /**
* Gets the {@link DisassembledAddressInfo}s for the given address. * Gets the {@link DisassembledAddressInfo}s for the given address.
* This method will disassamble {@link #LOOK_AHEAD_COUNT a few} addresses * This method will disassamble {@link #LOOK_AHEAD_COUNT a few} addresses
* after the one that is passed in. * after the one that is passed in.
* *
* @param address The address for which an info object will be obtained. * @param address The address for which an info object will be obtained.
@ -278,7 +278,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
try { try {
DisassembledAddressInfo addressInfo = new DisassembledAddressInfo(address); DisassembledAddressInfo addressInfo = new DisassembledAddressInfo(address);
// Now get some follow-on addresses to provide a small level of // Now get some follow-on addresses to provide a small level of
// context. This loop will stop if we cannot find an Address // context. This loop will stop if we cannot find an Address
// or a CodeUnit for a given address. // or a CodeUnit for a given address.
for (int i = 0; (i < LOOK_AHEAD_COUNT) && (address != null) && for (int i = 0; (i < LOOK_AHEAD_COUNT) && (address != null) &&
@ -304,7 +304,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
/** /**
* Takes the provided program location object and locates a * Takes the provided program location object and locates a
* {@link CodeUnit} for it's address that is used to display a disassembled * {@link CodeUnit} for it's address that is used to display a disassembled
* preview of the location. * preview of the location.
* *
@ -317,7 +317,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
DisassembledAddressInfo[] addressInfos = DisassembledAddressInfo[] addressInfos =
getAddressInformation(newLocation.getAddress()); getAddressInformation(newLocation.getAddress());
// add our preview content to our display (this will be empty if we // add our preview content to our display (this will be empty if we
// did not get a valid address or any valid code unit previews) // did not get a valid address or any valid code unit previews)
displayComponent.setContents(addressInfos); displayComponent.setContents(addressInfos);
} }
@ -329,14 +329,14 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
private class DisassembledViewComponentProvider extends ComponentProviderAdapter { private class DisassembledViewComponentProvider extends ComponentProviderAdapter {
/** /**
* The constant part of the tooltip text for the list cells. This * The constant part of the tooltip text for the list cells. This
* string is prepended to the currently selected address in the * string is prepended to the currently selected address in the
* program. * program.
* <p> * <p>
* Note: This value was just set on the list, but when that was done * Note: This value was just set on the list, but when that was done
* the help key triggers (Ctrl-F1) did not work correctly. * the help key triggers (Ctrl-F1) did not work correctly.
*/ */
private static final String TOOLTIP_TEXT_PREPEND = private static final String TOOLTIP_TEXT_PREPEND =
"<HTML>Currently selected<br> Code Browser program location<br>" + "address: "; "<html>Currently selected<br> Code Browser program location<br>address: ";
/** /**
* The component that will house our view. * The component that will house our view.
@ -481,10 +481,10 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
/** /**
* Notifies the provider that the user pressed the "close" button. * Notifies the provider that the user pressed the "close" button.
* The provider should take appropriate action. Usually the * The provider should take appropriate action. Usually the
* appropriate action is to hide the component or remove the * appropriate action is to hide the component or remove the
* component. If the provider does nothing in this method, * component. If the provider does nothing in this method,
* then the close button will appear broken. * then the close button will appear broken.
*/ */
@Override @Override
@ -521,7 +521,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
/** /**
* An object that provides information about the address that it wraps. The info knows how to * An object that provides information about the address that it wraps. The info knows how to
* locate an info object for the address and can generate a string preview of the address. * locate an info object for the address and can generate a string preview of the address.
*/ */
private class DisassembledAddressInfo { private class DisassembledAddressInfo {
@ -531,7 +531,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
private Address wrappedAddress; private Address wrappedAddress;
/** /**
* The code unit for the address of this info. This will be null * The code unit for the address of this info. This will be null
* after construction if not code unit exists for the address. * after construction if not code unit exists for the address.
*/ */
private CodeUnit addressCodeUnit; private CodeUnit addressCodeUnit;
@ -558,7 +558,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
/** /**
* Returns the address described by this info. * Returns the address described by this info.
* *
* @return Returns the address described by this info. * @return Returns the address described by this info.
*/ */
private Address getAddress() { private Address getAddress() {
@ -566,10 +566,10 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
/** /**
* Returns true if there is a {@link CodeUnit} for the address * Returns true if there is a {@link CodeUnit} for the address
* wrapped by this info. If not, then we do not have a valid addreess. * wrapped by this info. If not, then we do not have a valid addreess.
* *
* @return true if there is a {@link CodeUnit} for the address * @return true if there is a {@link CodeUnit} for the address
* wrapped by this info. * wrapped by this info.
*/ */
public boolean isValidAddress() { public boolean isValidAddress() {
@ -577,14 +577,14 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
/** /**
* Gets the length of the {@link CodeUnit} for the address wrapped * Gets the length of the {@link CodeUnit} for the address wrapped
* by this info. * by this info.
* <p> * <p>
* Note: If {@link #isValidAddress()} returns false, then this method * Note: If {@link #isValidAddress()} returns false, then this method
* will return <code>-1</code>. * will return <code>-1</code>.
* *
* @return the length of the code unit for the address wrapped by this * @return the length of the code unit for the address wrapped by this
* info. * info.
*/ */
public int getCodeUnitLength() { public int getCodeUnitLength() {
if (isValidAddress()) { if (isValidAddress()) {
@ -607,7 +607,7 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
Listing listing = currentProgram.getListing(); Listing listing = currentProgram.getListing();
codeUnit = listing.getCodeUnitAt(address); codeUnit = listing.getCodeUnitAt(address);
// if the CodeUnit is Data and is not defined, then we need to try to virtually // if the CodeUnit is Data and is not defined, then we need to try to virtually
// disassemble it // disassemble it
if (codeUnit instanceof Data) { if (codeUnit instanceof Data) {
if (!((Data) codeUnit).isDefined()) { if (!((Data) codeUnit).isDefined()) {
@ -624,8 +624,8 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
} }
/** /**
* Attempts to disassemble the provided address virtually * Attempts to disassemble the provided address virtually
* (without changing the state of the program) by making use of the * (without changing the state of the program) by making use of the
* {@link PseudoDisassembler}. * {@link PseudoDisassembler}.
* *
* @param address The address that will be disassembled. * @param address The address that will be disassembled.
@ -641,8 +641,8 @@ public class DisassembledViewPlugin extends ProgramPlugin implements DomainObjec
codeUnit = disassembler.disassemble(address); codeUnit = disassembler.disassemble(address);
} }
catch (UsrException ue) { catch (UsrException ue) {
// these exceptions happen if there is insufficient data from the program: // these exceptions happen if there is insufficient data from the program:
// InsufficientBytesException, UnknownInstructionException, // InsufficientBytesException, UnknownInstructionException,
// UnknownContextException // UnknownContextException
} }
} }

View file

@ -656,9 +656,9 @@ public class FunctionEditorDialog extends DialogComponentProvider implements Mod
color = getErrorForegroundColor(isSelected); color = getErrorForegroundColor(isSelected);
} }
String toolTipText = ToolTipUtils.getToolTipText(dataType); String toolTipText = ToolTipUtils.getToolTipText(dataType);
String headerText = "<HTML><b>" + String headerText = "<html><b>" +
HTMLUtilities.friendlyEncodeHTML(dataType.getPathName()) + "</b><BR>"; HTMLUtilities.friendlyEncodeHTML(dataType.getPathName()) + "</b><BR>";
toolTipText = toolTipText.replace("<HTML>", headerText); toolTipText = toolTipText.replace("<html>", headerText);
setToolTipText(toolTipText); setToolTipText(toolTipText);
} }
else { else {

View file

@ -0,0 +1,292 @@
/* ###
* 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 ghidra.app.plugin.core.help;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.Transferable;
import java.util.*;
import javax.swing.*;
import docking.ActionContext;
import docking.ReusableDialogComponentProvider;
import docking.action.DockingAction;
import docking.action.MenuData;
import docking.dnd.GClipboard;
import docking.dnd.StringTransferable;
import docking.tool.ToolConstants;
import docking.widgets.table.AbstractSortedTableModel;
import docking.widgets.table.GTable;
import ghidra.app.CorePluginPackage;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.util.HelpTopics;
import ghidra.framework.main.ApplicationLevelPlugin;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.lang.*;
import ghidra.program.util.DefaultLanguageService;
import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities;
//@formatter:off
@PluginInfo(
status = PluginStatus.RELEASED,
packageName = CorePluginPackage.NAME,
category = PluginCategoryNames.COMMON,
shortDescription = "Displays list of installed processor modules",
description = "This plugin provides a Help action that displays a list of installed processor modules"
)
//@formatter:on
public class ProcessorListPlugin extends Plugin implements ApplicationLevelPlugin {
private DockingAction processorListAction;
private ProcessorListDialogProvider dialogProvider;
public ProcessorListPlugin(PluginTool tool) {
super(tool);
}
@Override
protected void init() {
setupActions();
}
@Override
public void dispose() {
tool.removeAction(processorListAction);
processorListAction.dispose();
if (dialogProvider != null) {
dialogProvider.dispose();
}
super.dispose();
}
private void setupActions() {
processorListAction = new DockingAction("Installed Processors", this.getName()) {
@Override
public void actionPerformed(ActionContext context) {
showProcessorList();
}
};
processorListAction.setEnabled(true);
processorListAction.setMenuBarData(new MenuData(
new String[] { ToolConstants.MENU_HELP, processorListAction.getName() }, null, "AAAZ"));
processorListAction.setHelpLocation(new HelpLocation(HelpTopics.ABOUT, "ProcessorList"));
processorListAction.setDescription(getPluginDescription().getDescription());
tool.addAction(processorListAction);
}
private void dialogClosed() {
dialogProvider = null;
}
private void showProcessorList() {
if (dialogProvider == null) {
dialogProvider = new ProcessorListDialogProvider();
}
tool.showDialog(dialogProvider);
}
private void copy(boolean asHtml) {
Clipboard systemClipboard = GClipboard.getSystemClipboard();
Transferable transferable = new StringTransferable(getProcessorList(asHtml));
systemClipboard.setContents(transferable, null);
}
private Set<Processor> getProcessors() {
TreeSet<Processor> processors = new TreeSet<>();
LanguageService languageService = DefaultLanguageService.getLanguageService();
for (LanguageDescription languageDescription : languageService.getLanguageDescriptions(
true)) {
processors.add(languageDescription.getProcessor());
}
return processors;
}
private String getProcessorList(boolean asHtml) {
StringBuilder strBuilder = new StringBuilder();
if (asHtml) {
strBuilder.append("<html><BODY>\n");
strBuilder.append("<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n<tr>");
}
Set<Processor> processors = getProcessors();
int itemsPerColum = (processors.size() + 2) / 3;
int colCnt = 0;
for (Processor processor : processors) {
if (asHtml) {
if ((colCnt % itemsPerColum) == 0) {
if (colCnt != 0) {
strBuilder.append("</ul>\n</td>");
}
strBuilder.append("<td width=\"33%\">\n<ul>");
}
strBuilder.append("<li>");
}
++colCnt;
strBuilder.append(processor.toString());
if (asHtml) {
strBuilder.append("</li>");
}
strBuilder.append("\n");
}
if (asHtml) {
strBuilder.append("</ul>\n</td></tr>\n</table>");
strBuilder.append("</BODY></html>");
}
return strBuilder.toString();
}
private class ProcessorListDialogProvider extends ReusableDialogComponentProvider {
ProcessorListDialogProvider() {
super("Installed Processor Modules", false, false, true, false);
ProcessorListTableProvider tableProvider =
new ProcessorListTableProvider(tool, getName());
setRememberLocation(true);
addWorkPanel(tableProvider.getComponent());
setHelpLocation(new HelpLocation(HelpTopics.ABOUT, "ProcessorList"));
if (SystemUtilities.isInDevelopmentMode()) {
JButton copyButton = new JButton("Copy");
copyButton.addActionListener(e -> copy(false));
addButton(copyButton);
JButton copyHtmlButton = new JButton("Copy as HTML");
copyHtmlButton.addActionListener(e -> copy(true));
addButton(copyHtmlButton);
}
JButton closeButton = new JButton("Close");
closeButton.addActionListener(e -> close());
addButton(closeButton);
}
@Override
protected void dialogClosed() {
super.dialogClosed();
ProcessorListPlugin.this.dialogClosed();
}
}
public class ProcessorListTableProvider extends ComponentProviderAdapter {
GTable table;
private ProcessorListTableModel processorTableModel;
private JScrollPane scrollPane;
public ProcessorListTableProvider(PluginTool tool, String owner) {
super(tool, "Processor Table", owner);
buildTable();
}
@Override
public JComponent getComponent() {
return scrollPane;
}
private void buildTable() {
TreeSet<Processor> processors = new TreeSet<>();
LanguageService languageService = DefaultLanguageService.getLanguageService();
for (LanguageDescription languageDescription : languageService.getLanguageDescriptions(
true)) {
processors.add(languageDescription.getProcessor());
}
processorTableModel = new ProcessorListTableModel(new ArrayList<>(processors));
table = new GTable(processorTableModel);
scrollPane = new JScrollPane(table);
table.getSelectionManager().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
}
}
public class ProcessorListTableModel extends AbstractSortedTableModel<Processor> {
private static final int PROCESSOR_COL = 0;
private List<Processor> processors;
public ProcessorListTableModel(List<Processor> processors) {
this.processors = processors;
}
@Override
public Object getColumnValueForRow(Processor p, int columnIndex) {
switch (columnIndex) {
case PROCESSOR_COL:
return p.toString();
}
return null;
}
@Override
public String getName() {
return "Processors";
}
@Override
public List<Processor> getModelData() {
return processors;
}
@Override
public boolean isSortable(int columnIndex) {
return false; // maybe later when we add more columns
}
@Override
public int getColumnCount() {
return 1;
}
@Override
public int getRowCount() {
return processors.size();
}
@Override
public String getColumnName(int column) {
switch (column) {
case PROCESSOR_COL:
return "Processor";
}
return null;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case PROCESSOR_COL:
return String.class;
}
return Object.class;
}
}
}

View file

@ -93,12 +93,12 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
private TaskListener cleanupTaskSetListener = new TaskListener() { private TaskListener cleanupTaskSetListener = new TaskListener() {
@Override @Override
public void taskCompleted(Task task) { public void taskCompleted(Task task) {
runningScriptTaskSet.remove((RunScriptTask) task); runningScriptTaskSet.remove(task);
} }
@Override @Override
public void taskCancelled(Task task) { public void taskCancelled(Task task) {
runningScriptTaskSet.remove((RunScriptTask) task); runningScriptTaskSet.remove(task);
} }
}; };
@ -176,7 +176,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
}); });
scriptCategoryTree.getSelectionModel() scriptCategoryTree.getSelectionModel()
.setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); .setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
} }
private void build() { private void build() {
@ -475,11 +475,11 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
*/ */
public List<ResourceFile> getScriptDirectories() { public List<ResourceFile> getScriptDirectories() {
return bundleHost.getGhidraBundles() return bundleHost.getGhidraBundles()
.stream() .stream()
.filter(GhidraSourceBundle.class::isInstance) .filter(GhidraSourceBundle.class::isInstance)
.filter(GhidraBundle::isEnabled) .filter(GhidraBundle::isEnabled)
.map(GhidraBundle::getFile) .map(GhidraBundle::getFile)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
/** /**
@ -487,12 +487,12 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
*/ */
public List<ResourceFile> getWritableScriptDirectories() { public List<ResourceFile> getWritableScriptDirectories() {
return bundleHost.getGhidraBundles() return bundleHost.getGhidraBundles()
.stream() .stream()
.filter(GhidraSourceBundle.class::isInstance) .filter(GhidraSourceBundle.class::isInstance)
.filter(Predicate.not(GhidraBundle::isSystemBundle)) .filter(Predicate.not(GhidraBundle::isSystemBundle))
.filter(GhidraBundle::isEnabled) .filter(GhidraBundle::isEnabled)
.map(GhidraBundle::getFile) .map(GhidraBundle::getFile)
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
boolean isEditorOpen(ResourceFile script) { boolean isEditorOpen(ResourceFile script) {
@ -647,9 +647,9 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
AtomicReference<GhidraScript> ref = new AtomicReference<>(); AtomicReference<GhidraScript> ref = new AtomicReference<>();
TaskBuilder.withRunnable(monitor -> ref.set(scriptSupplier.get())) TaskBuilder.withRunnable(monitor -> ref.set(scriptSupplier.get()))
.setTitle("Compiling Script Directory") .setTitle("Compiling Script Directory")
.setLaunchDelay(1000) .setLaunchDelay(1000)
.launchModal(); .launchModal();
return ref.get(); return ref.get();
} }
@ -1012,7 +1012,7 @@ public class GhidraScriptComponentProvider extends ComponentProviderAdapter {
return list; return list;
} }
}); });
tableFilterPanel.setToolTipText("<HTML>Include scripts with <b>Names</b> or " + tableFilterPanel.setToolTipText("<html>Include scripts with <b>Names</b> or " +
"<b>Descriptions</b> containing this text."); "<b>Descriptions</b> containing this text.");
tableFilterPanel.setFocusComponent(scriptCategoryTree); tableFilterPanel.setFocusComponent(scriptCategoryTree);
} }

View file

@ -31,7 +31,7 @@ import ghidra.util.HTMLUtilities;
import ghidra.util.UserSearchUtils; import ghidra.util.UserSearchUtils;
/** /**
* A widget that allows the user to choose an existing script by typing its name or picking it * A widget that allows the user to choose an existing script by typing its name or picking it
* from a list. * from a list.
*/ */
public class ScriptSelectionEditor { public class ScriptSelectionEditor {
@ -102,7 +102,7 @@ public class ScriptSelectionEditor {
/** /**
* Adds a document listener to the text field editing component of this editor so that users * Adds a document listener to the text field editing component of this editor so that users
* can be notified when the text contents of the editor change. You may verify whether the * can be notified when the text contents of the editor change. You may verify whether the
* text changes represent a valid DataType by calling {@link #validateUserSelection()}. * text changes represent a valid DataType by calling {@link #validateUserSelection()}.
* @param listener the listener to add. * @param listener the listener to add.
* @see #validateUserSelection() * @see #validateUserSelection()
@ -176,7 +176,7 @@ public class ScriptSelectionEditor {
} }
private boolean containsValidScript() { private boolean containsValidScript() {
// look for the case where the user made a selection from the matching window, but // look for the case where the user made a selection from the matching window, but
// then changed the text field text. // then changed the text field text.
ScriptInfo selectedInfo = selectionField.getSelectedValue(); ScriptInfo selectedInfo = selectionField.getSelectedValue();
if (selectedInfo != null && if (selectedInfo != null &&
@ -211,7 +211,7 @@ public class ScriptSelectionEditor {
//================================================================================================= //=================================================================================================
// Inner Classes // Inner Classes
//================================================================================================= //=================================================================================================
private class ScriptTextFieldModel extends DefaultDropDownSelectionDataModel<ScriptInfo> { private class ScriptTextFieldModel extends DefaultDropDownSelectionDataModel<ScriptInfo> {
@ -252,7 +252,7 @@ public class ScriptSelectionEditor {
protected boolean shouldReplaceTextFieldTextWithSelectedItem(String textFieldText, protected boolean shouldReplaceTextFieldTextWithSelectedItem(String textFieldText,
ScriptInfo selectedItem) { ScriptInfo selectedItem) {
// This is called when the user presses Enter with a list item selected. By // This is called when the user presses Enter with a list item selected. By
// default, the text field will not replace the text field text if the given item // default, the text field will not replace the text field text if the given item
// does not match the text. This is to allow users to enter custom text. We do // does not match the text. This is to allow users to enter custom text. We do
// not want custom text, as the user must pick an existing script. Thus, we always // not want custom text, as the user must pick an existing script. Thus, we always
@ -265,7 +265,7 @@ public class ScriptSelectionEditor {
@Override @Override
public String getString(ScriptInfo info) { public String getString(ScriptInfo info) {
StringBuilder buffy = new StringBuilder("<HTML><P>"); StringBuilder buffy = new StringBuilder("<html><P>");
KeyStroke keyBinding = info.getKeyBinding(); KeyStroke keyBinding = info.getKeyBinding();
if (keyBinding != null) { if (keyBinding != null) {
@ -289,9 +289,9 @@ public class ScriptSelectionEditor {
private String formatDescription(String description) { private String formatDescription(String description) {
// //
// We are going to wrap lines at 50 columns so that they fit the tooltip window. We // We are going to wrap lines at 50 columns so that they fit the tooltip window. We
// will also try to keep the original structure of manually separated lines by // will also try to keep the original structure of manually separated lines by
// preserving empty lines included in the original description. Removing all newlines // preserving empty lines included in the original description. Removing all newlines
// except for the blank lines allows the line wrapping utility to create the best // except for the blank lines allows the line wrapping utility to create the best
// output. // output.
// //
@ -309,7 +309,7 @@ public class ScriptSelectionEditor {
} }
// Remove all newlines, except for consecutive newlines, which represent blank lines. // Remove all newlines, except for consecutive newlines, which represent blank lines.
// Then, for any remaining newline, add back the extra blank line. // Then, for any remaining newline, add back the extra blank line.
String trimmed = bufffy.toString(); String trimmed = bufffy.toString();
String stripped = trimmed.replaceAll("(?<!\n)\n", ""); String stripped = trimmed.replaceAll("(?<!\n)\n", "");
stripped = stripped.replaceAll("\n", "\n\n"); stripped = stripped.replaceAll("\n", "\n\n");

View file

@ -81,7 +81,7 @@ class ProgramTextWriter {
//boolean sk = false; //boolean sk = false;
if (options.isHTML()) { if (options.isHTML()) {
writer.print("<HTML><BODY BGCOLOR=#ffffe0>"); writer.print("<html><BODY BGCOLOR=#ffffe0>");
writer.println("<FONT FACE=COURIER SIZE=3><STRONG><PRE>"); writer.println("<FONT FACE=COURIER SIZE=3><STRONG><PRE>");
} }
TemplateSimplifier simplifier = new TemplateSimplifier(); TemplateSimplifier simplifier = new TemplateSimplifier();
@ -339,7 +339,7 @@ class ProgramTextWriter {
} }
if (options.isHTML()) { if (options.isHTML()) {
writer.println("</PRE></STRONG></FONT></BODY></HTML>"); writer.println("</PRE></STRONG></FONT></BODY></html>");
} }
writer.close(); writer.close();

View file

@ -59,16 +59,16 @@ import ghidra.util.task.TaskMonitor;
import utilities.util.FileUtilities; import utilities.util.FileUtilities;
/** /**
* The class used kick-off and interact with headless processing. All headless options have been * The class used kick-off and interact with headless processing. All headless options have been
* broken out into their own class: {@link HeadlessOptions}. This class is intended to be used * broken out into their own class: {@link HeadlessOptions}. This class is intended to be used
* one of two ways: * one of two ways:
* <ul> * <ul>
* <li>Used by {@link AnalyzeHeadless} to perform headless analysis based on arguments specified * <li>Used by {@link AnalyzeHeadless} to perform headless analysis based on arguments specified
* on the command line.</li> * on the command line.</li>
* <li>Used by another tool as a library to perform headless analysis.</li> * <li>Used by another tool as a library to perform headless analysis.</li>
* </ul> * </ul>
* <p> * <p>
* Note: This class is not thread safe. * Note: This class is not thread safe.
*/ */
public class HeadlessAnalyzer { public class HeadlessAnalyzer {
@ -84,16 +84,16 @@ public class HeadlessAnalyzer {
private FileSystemService fsService; private FileSystemService fsService;
/** /**
* Gets a headless analyzer, initializing the application if necessary with the specified * Gets a headless analyzer, initializing the application if necessary with the specified
* logging parameters. An {@link IllegalStateException} will be thrown if the application has * logging parameters. An {@link IllegalStateException} will be thrown if the application has
* already been initialized or a headless analyzer has already been retrieved. In these cases, * already been initialized or a headless analyzer has already been retrieved. In these cases,
* the headless analyzer should be gotten with {@link HeadlessAnalyzer#getInstance()}. * the headless analyzer should be gotten with {@link HeadlessAnalyzer#getInstance()}.
* *
* @param logFile The desired application log file. If null, the default application log file * @param logFile The desired application log file. If null, the default application log file
* will be used (see {@link Application#initializeLogging}). * will be used (see {@link Application#initializeLogging}).
* @param scriptLogFile The desired scripting log file. If null, the default scripting log file * @param scriptLogFile The desired scripting log file. If null, the default scripting log file
* will be used (see {@link Application#initializeLogging}). * will be used (see {@link Application#initializeLogging}).
* @param useLog4j true if log4j is to be used; otherwise, false. If this class is being used by * @param useLog4j true if log4j is to be used; otherwise, false. If this class is being used by
* another tool as a library, using log4j might interfere with that tool. * another tool as a library, using log4j might interfere with that tool.
* @return An instance of a new headless analyzer. * @return An instance of a new headless analyzer.
* @throws IllegalStateException if an application or headless analyzer instance has already been initialized. * @throws IllegalStateException if an application or headless analyzer instance has already been initialized.
@ -103,7 +103,7 @@ public class HeadlessAnalyzer {
boolean useLog4j) throws IllegalStateException, IOException { boolean useLog4j) throws IllegalStateException, IOException {
// Prevent more than one headless analyzer from being instantiated. Too much about it // Prevent more than one headless analyzer from being instantiated. Too much about it
// messes with global system settings, so under the current design of Ghidra, allowing // messes with global system settings, so under the current design of Ghidra, allowing
// more than one to exist could result in unpredictable behavior. // more than one to exist could result in unpredictable behavior.
if (instance != null) { if (instance != null) {
throw new IllegalStateException( throw new IllegalStateException(
@ -141,7 +141,7 @@ public class HeadlessAnalyzer {
/** /**
* Gets a headless analyzer instance, with the assumption that the application has already been * Gets a headless analyzer instance, with the assumption that the application has already been
* initialized. If this is called before the application has been initialized, it will * initialized. If this is called before the application has been initialized, it will
* initialize the application with no logging. * initialize the application with no logging.
* *
* @return An instance of a new headless analyzer. * @return An instance of a new headless analyzer.
@ -151,7 +151,7 @@ public class HeadlessAnalyzer {
public static HeadlessAnalyzer getInstance() throws IOException { public static HeadlessAnalyzer getInstance() throws IOException {
// Prevent more than one headless analyzer from being instantiated. Too much about it // Prevent more than one headless analyzer from being instantiated. Too much about it
// messes with global system settings, so under the current design of Ghidra, allowing // messes with global system settings, so under the current design of Ghidra, allowing
// more than one to exist could result in unpredictable behavior. // more than one to exist could result in unpredictable behavior.
if (instance != null) { if (instance != null) {
return instance; return instance;
@ -185,8 +185,10 @@ public class HeadlessAnalyzer {
layout = new GhidraApplicationLayout(); layout = new GhidraApplicationLayout();
} }
catch (IOException e) { catch (IOException e) {
Msg.debug(HeadlessAnalyzer.class,
"Unable to load the standard Ghidra application layout. " + e.getMessage() +
". Attempting to load the Ghidra Jar application layout.");
layout = new GhidraJarApplicationLayout(); layout = new GhidraJarApplicationLayout();
} }
return layout; return layout;
} }
@ -201,7 +203,7 @@ public class HeadlessAnalyzer {
// Ghidra URL handler registration. There's no harm in doing this more than once. // Ghidra URL handler registration. There's no harm in doing this more than once.
Handler.registerHandler(); Handler.registerHandler();
// Ensure that we are running in "headless mode", preventing Swing-based methods from // Ensure that we are running in "headless mode", preventing Swing-based methods from
// running (causing headless operation to lose focus). // running (causing headless operation to lose focus).
System.setProperty("java.awt.headless", "true"); System.setProperty("java.awt.headless", "true");
System.setProperty(SystemUtilities.HEADLESS_PROPERTY, Boolean.TRUE.toString()); System.setProperty(SystemUtilities.HEADLESS_PROPERTY, Boolean.TRUE.toString());
@ -241,12 +243,12 @@ public class HeadlessAnalyzer {
* <li>perform auto-analysis if not disabled</li> * <li>perform auto-analysis if not disabled</li>
* <li>execute ordered list of post-scripts</li> * <li>execute ordered list of post-scripts</li>
* </ol> * </ol>
* If no import files or directories have been specified the ordered list * If no import files or directories have been specified the ordered list
* of pre/post scripts will be executed once. * of pre/post scripts will be executed once.
* *
* @param ghidraURL ghidra URL for existing server repository and optional * @param ghidraURL ghidra URL for existing server repository and optional
* folder path * folder path
* @param filesToImport directories and files to be imported (null or empty * @param filesToImport directories and files to be imported (null or empty
* is acceptable if we are in -process mode) * is acceptable if we are in -process mode)
* @throws IOException if there was an IO-related problem * @throws IOException if there was an IO-related problem
* @throws MalformedURLException specified URL is invalid * @throws MalformedURLException specified URL is invalid
@ -367,16 +369,16 @@ public class HeadlessAnalyzer {
* <li>perform auto-analysis if not disabled</li> * <li>perform auto-analysis if not disabled</li>
* <li>execute ordered list of post-scripts</li> * <li>execute ordered list of post-scripts</li>
* </ol> * </ol>
* If no import files or directories have been specified the ordered list * If no import files or directories have been specified the ordered list
* of pre/post scripts will be executed once. * of pre/post scripts will be executed once.
* *
* @param projectLocation directory path of project * @param projectLocation directory path of project
* If project exists it will be opened, otherwise it will be created. * If project exists it will be opened, otherwise it will be created.
* @param projectName project name * @param projectName project name
* @param rootFolderPath root folder for imports * @param rootFolderPath root folder for imports
* @param filesToImport directories and files to be imported (null or empty is acceptable if * @param filesToImport directories and files to be imported (null or empty is acceptable if
* we are in -process mode) * we are in -process mode)
* @throws IOException if there was an IO-related problem. If caused by a failure to obtain a * @throws IOException if there was an IO-related problem. If caused by a failure to obtain a
* write-lock on the project the exception cause will a {@code LockException}. * write-lock on the project the exception cause will a {@code LockException}.
*/ */
public void processLocal(String projectLocation, String projectName, String rootFolderPath, public void processLocal(String projectLocation, String projectName, String rootFolderPath,
@ -472,7 +474,7 @@ public class HeadlessAnalyzer {
/** /**
* Checks to see if the most recent analysis timed out. * Checks to see if the most recent analysis timed out.
* *
* @return true if the most recent analysis timed out; otherwise, false. * @return true if the most recent analysis timed out; otherwise, false.
*/ */
public boolean checkAnalysisTimedOut() { public boolean checkAnalysisTimedOut() {
return analysisTimedOut; return analysisTimedOut;
@ -763,7 +765,7 @@ public class HeadlessAnalyzer {
Class<?> c = Class.forName(className, true, classLoaderForDotClassScripts); Class<?> c = Class.forName(className, true, classLoaderForDotClassScripts);
if (GhidraScript.class.isAssignableFrom(c)) { if (GhidraScript.class.isAssignableFrom(c)) {
// No issues, but return null, which signifies we don't actually have a // No issues, but return null, which signifies we don't actually have a
// ResourceFile to associate with the script name // ResourceFile to associate with the script name
return null; return null;
} }
@ -959,9 +961,9 @@ public class HeadlessAnalyzer {
* @param fileAbsolutePath Path of the file to analyze. * @param fileAbsolutePath Path of the file to analyze.
* @param program The program to analyze. * @param program The program to analyze.
* @return true if the program file should be kept. If analysis or scripts have marked * @return true if the program file should be kept. If analysis or scripts have marked
* the program as temporary changes should not be saved. Returns false in * the program as temporary changes should not be saved. Returns false in
* these cases: * these cases:
* - One of the scripts sets the Headless Continuation Option to "ABORT_AND_DELETE" or * - One of the scripts sets the Headless Continuation Option to "ABORT_AND_DELETE" or
* "CONTINUE_THEN_DELETE". * "CONTINUE_THEN_DELETE".
*/ */
private boolean analyzeProgram(String fileAbsolutePath, Program program) { private boolean analyzeProgram(String fileAbsolutePath, Program program) {
@ -1151,7 +1153,7 @@ public class HeadlessAnalyzer {
Msg.info(this, "REPORT: Processing project file: " + domFile.getPathname()); Msg.info(this, "REPORT: Processing project file: " + domFile.getPathname());
// This method already takes into account whether the user has set the "noanalysis" // This method already takes into account whether the user has set the "noanalysis"
// flag or not // flag or not
keepFile = analyzeProgram(domFile.getPathname(), program) || readOnlyFile; keepFile = analyzeProgram(domFile.getPathname(), program) || readOnlyFile;
@ -1234,7 +1236,7 @@ public class HeadlessAnalyzer {
if (!readOnlyFile) { // can't change anything if read-only file if (!readOnlyFile) { // can't change anything if read-only file
// Undo checkout of it is still checked-out and either the file is to be // Undo checkout of it is still checked-out and either the file is to be
// deleted, or we just checked it out and file changes have been committed // deleted, or we just checked it out and file changes have been committed
if (domFile.isCheckedOut()) { if (domFile.isCheckedOut()) {
if (!keepFile || if (!keepFile ||
@ -1518,14 +1520,14 @@ public class HeadlessAnalyzer {
try { try {
// Perform the load. Note that loading 1 file may result in more than 1 thing getting // Perform the load. Note that loading 1 file may result in more than 1 thing getting
// loaded. // loaded.
loadResults = loadPrograms(fsrl, folderPath); loadResults = loadPrograms(fsrl, folderPath);
Msg.info(this, "IMPORTING: Loaded " + (loadResults.size() - 1) + " additional files"); Msg.info(this, "IMPORTING: Loaded " + (loadResults.size() - 1) + " additional files");
primary = loadResults.getPrimary(); primary = loadResults.getPrimary();
Program primaryProgram = primary.getDomainObject(); Program primaryProgram = primary.getDomainObject();
// Make sure we are allowed to save ALL programs to the project. If not, save none and // Make sure we are allowed to save ALL programs to the project. If not, save none and
// fail. // fail.
if (!options.readOnly) { if (!options.readOnly) {
for (Loaded<Program> loaded : loadResults) { for (Loaded<Program> loaded : loadResults) {
@ -1546,7 +1548,7 @@ public class HeadlessAnalyzer {
// TODO: Analyze non-primary programs (GP-2965). // TODO: Analyze non-primary programs (GP-2965).
boolean doSave = analyzeProgram(fsrl.toString(), primaryProgram) && !options.readOnly; boolean doSave = analyzeProgram(fsrl.toString(), primaryProgram) && !options.readOnly;
// The act of marking the program as temporary by a script will signal // The act of marking the program as temporary by a script will signal
// us to discard any changes // us to discard any changes
if (!doSave) { if (!doSave) {
loadResults.forEach(e -> e.getDomainObject().setTemporary(true)); loadResults.forEach(e -> e.getDomainObject().setTemporary(true));
@ -1773,7 +1775,7 @@ public class HeadlessAnalyzer {
return; return;
default: default:
// Just continue // Just continue
} }
runScriptsList(options.postScripts, options.postScriptFileMap, scriptState, runScriptsList(options.postScripts, options.postScriptFileMap, scriptState,
@ -1831,7 +1833,7 @@ public class HeadlessAnalyzer {
} }
/** /**
* Ghidra project class required to gain access to specialized project constructor * Ghidra project class required to gain access to specialized project constructor
* for URL connection. * for URL connection.
*/ */
private static class HeadlessProject extends DefaultProject { private static class HeadlessProject extends DefaultProject {

View file

@ -40,9 +40,10 @@ public abstract class HTMLDataTypeRepresentation {
protected final static int MAX_CHARACTER_LENGTH = 80; protected final static int MAX_CHARACTER_LENGTH = 80;
protected final static int MAX_LINE_LENGTH = MAX_CHARACTER_LENGTH * 3; protected final static int MAX_LINE_LENGTH = MAX_CHARACTER_LENGTH * 3;
// HTML Tag constants // HTML Tag constants. Intentionally lower-case to match external checks for isHtml(), some of
protected static final String HTML_OPEN = "<HTML>"; // which check case-sensitive for "<html>"
protected static final String HTML_CLOSE = "</HTML>"; protected static final String HTML_OPEN = "<html>";
protected static final String HTML_CLOSE = "</html>";
// single HTML space // single HTML space
protected static final String HTML_SPACE = "&nbsp;"; protected static final String HTML_SPACE = "&nbsp;";
@ -300,11 +301,11 @@ public abstract class HTMLDataTypeRepresentation {
*/ */
protected HTMLDataTypeRepresentation(String htmlText) { protected HTMLDataTypeRepresentation(String htmlText) {
this.originalHTMLData = htmlText.trim(); this.originalHTMLData = htmlText.trim();
// NOTE: the text expected here should not have <HTML></HTML> tags! // NOTE: the text expected here should not have <html></html> tags!
boolean htmlStart = StringUtilities.startsWithIgnoreCase(htmlText, HTML_OPEN); boolean htmlStart = StringUtilities.startsWithIgnoreCase(htmlText, HTML_OPEN);
boolean htmlEnd = StringUtilities.startsWithIgnoreCase(htmlText, HTML_CLOSE); boolean htmlEnd = StringUtilities.startsWithIgnoreCase(htmlText, HTML_CLOSE);
if (htmlStart || htmlEnd) { if (htmlStart || htmlEnd) {
throw new AssertException("Invalid HTML format: text must not include HTML tag"); throw new AssertException("Invalid HTML format: text must not include <html> tag");
} }
} }

View file

@ -37,7 +37,7 @@ public class EolExtraCommentsPropertyEditor extends PropertyEditorSupport
private static final String ABBREVIATED_LABEL = "Use Abbreviated Comments"; private static final String ABBREVIATED_LABEL = "Use Abbreviated Comments";
private static final String REPEATABLE_TOOLTIP = private static final String REPEATABLE_TOOLTIP =
"<HTML>For repeatable comments:" + "<html>For repeatable comments:" +
"<UL>" + "<UL>" +
" <LI>ALWAYS - show even if an EOL comment exists</LI>" + " <LI>ALWAYS - show even if an EOL comment exists</LI>" +
" <LI>DEFAULT - show only when no EOL comment exists</LI>" + " <LI>DEFAULT - show only when no EOL comment exists</LI>" +
@ -45,7 +45,7 @@ public class EolExtraCommentsPropertyEditor extends PropertyEditorSupport
"</UL>"; "</UL>";
private static final String REF_REPEATABLE_TOOLTIP = private static final String REF_REPEATABLE_TOOLTIP =
"<HTML>For referenced repeatable comments:" + "<html>For referenced repeatable comments:" +
"<UL>" + "<UL>" +
" <LI>ALWAYS - show even if a higher priority comment exists</LI>" + " <LI>ALWAYS - show even if a higher priority comment exists</LI>" +
" <LI>DEFAULT - show only when no higher priority comment exists</LI>" + " <LI>DEFAULT - show only when no higher priority comment exists</LI>" +
@ -53,7 +53,7 @@ public class EolExtraCommentsPropertyEditor extends PropertyEditorSupport
"</UL>"; "</UL>";
private static final String AUTO_TOOLTIP = private static final String AUTO_TOOLTIP =
"<HTML>For automatic comments:" + "<html>For automatic comments:" +
"<UL>" + "<UL>" +
" <LI>ALWAYS - show even if a higher priority comment exists</LI>" + " <LI>ALWAYS - show even if a higher priority comment exists</LI>" +
" <LI>DEFAULT - show only when no higher priority comment exists</LI>" + " <LI>DEFAULT - show only when no higher priority comment exists</LI>" +

View file

@ -18,6 +18,7 @@ package ghidra.app.util.viewer.field;
import docking.widgets.fieldpanel.FieldDescriptionProvider; import docking.widgets.fieldpanel.FieldDescriptionProvider;
import docking.widgets.fieldpanel.field.Field; import docking.widgets.fieldpanel.field.Field;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import ghidra.program.model.address.Address;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
public class ListingFieldDescriptionProvider implements FieldDescriptionProvider { public class ListingFieldDescriptionProvider implements FieldDescriptionProvider {
@ -27,8 +28,9 @@ public class ListingFieldDescriptionProvider implements FieldDescriptionProvider
if (field instanceof ListingField listingField) { if (field instanceof ListingField listingField) {
FieldFactory fieldFactory = listingField.getFieldFactory(); FieldFactory fieldFactory = listingField.getFieldFactory();
ProgramLocation location = fieldFactory.getProgramLocation(0, 0, listingField); ProgramLocation location = fieldFactory.getProgramLocation(0, 0, listingField);
return fieldFactory.getFieldName() + " Field at Address " + location.getAddress() + Address address = location.getAddress();
" text = " + field.getText(); String addressString = address.toString(address.getAddressSpace().showSpaceName(), 1);
return fieldFactory.getFieldName() + " Field at Address " + addressString;
} }
return "Unknown Field"; return "Unknown Field";
} }

View file

@ -522,7 +522,7 @@ public class ListingCodeComparisonPanel
class ToggleOrientationAction extends ToggleDockingAction { class ToggleOrientationAction extends ToggleDockingAction {
ToggleOrientationAction() { ToggleOrientationAction() {
super("Dual Listing Toggle Orientation", owner); super("Dual Listing Toggle Orientation", owner);
setDescription("<HTML>Toggle the layout of the listings " + setDescription("<html>Toggle the layout of the listings " +
"<BR>between side-by-side and one above the other.</HTML>"); "<BR>between side-by-side and one above the other.</HTML>");
setEnabled(true); setEnabled(true);
setSelected(isSideBySide); setSelected(isSideBySide);
@ -1071,7 +1071,7 @@ public class ListingCodeComparisonPanel
saveState.remove("_BYTE_ADDR"); saveState.remove("_BYTE_ADDR");
Address desiredByteAddress = null; Address desiredByteAddress = null;
if (byteAddress != null) { if (byteAddress != null) {
// Try to get the indicated side's byte address using one of the address // Try to get the indicated side's byte address using one of the address
// correlators or by inferring it. // correlators or by inferring it.
desiredByteAddress = inferDesiredByteAddress(address, desiredAddress, byteAddress, desiredByteAddress = inferDesiredByteAddress(address, desiredAddress, byteAddress,
programLocation.getProgram(), programs[leftOrRight]); programLocation.getProgram(), programs[leftOrRight]);
@ -1852,7 +1852,7 @@ public class ListingCodeComparisonPanel
titlePanels[LEFT] = new TitledPanel(leftProgramName, listingPanels[LEFT], 5); titlePanels[LEFT] = new TitledPanel(leftProgramName, listingPanels[LEFT], 5);
titlePanels[RIGHT] = new TitledPanel(rightProgramName, listingPanels[RIGHT], 5); titlePanels[RIGHT] = new TitledPanel(rightProgramName, listingPanels[RIGHT], 5);
// Set the MINIMUM_PANEL_WIDTH for the left and right panel to prevent the split pane's // Set the MINIMUM_PANEL_WIDTH for the left and right panel to prevent the split pane's
// divider from becoming locked (can't be moved) due to extra long title names. // divider from becoming locked (can't be moved) due to extra long title names.
titlePanels[LEFT].setMinimumSize( titlePanels[LEFT].setMinimumSize(
new Dimension(MINIMUM_PANEL_WIDTH, titlePanels[LEFT].getMinimumSize().height)); new Dimension(MINIMUM_PANEL_WIDTH, titlePanels[LEFT].getMinimumSize().height));
@ -1871,7 +1871,7 @@ public class ListingCodeComparisonPanel
if (!titlePrefix.isEmpty()) { if (!titlePrefix.isEmpty()) {
titlePrefix += " "; // Add a space between prefix and title. titlePrefix += " "; // Add a space between prefix and title.
} }
String htmlPrefix = "<HTML>"; String htmlPrefix = "<html>";
if (title.startsWith(htmlPrefix)) { if (title.startsWith(htmlPrefix)) {
titlePanel.setTitleName(htmlPrefix + HTMLUtilities.friendlyEncodeHTML(titlePrefix) + titlePanel.setTitleName(htmlPrefix + HTMLUtilities.friendlyEncodeHTML(titlePrefix) +
title.substring(htmlPrefix.length())); title.substring(htmlPrefix.length()));
@ -2187,7 +2187,7 @@ public class ListingCodeComparisonPanel
* @return the matching address in the indicated program or null. * @return the matching address in the indicated program or null.
*/ */
private Address getFunctionAddress(int leftOrRight, Address otherSidesAddress) { private Address getFunctionAddress(int leftOrRight, Address otherSidesAddress) {
// Try to get the address using the correlator. // Try to get the address using the correlator.
// If the correlator couldn't determine it, then try to infer it. // If the correlator couldn't determine it, then try to infer it.
int otherSide = (leftOrRight == RIGHT) ? LEFT : RIGHT; int otherSide = (leftOrRight == RIGHT) ? LEFT : RIGHT;
// Finding desired side's address. // Finding desired side's address.
@ -2215,7 +2215,7 @@ public class ListingCodeComparisonPanel
} }
private Address getDataAddress(int leftOrRight, Address otherSidesAddress) { private Address getDataAddress(int leftOrRight, Address otherSidesAddress) {
// Correlator doesn't handle data compare, so associate beginning of data and // Correlator doesn't handle data compare, so associate beginning of data and
// infer the others based on relative position. // infer the others based on relative position.
Address leftDataAddress = getLeftDataAddress(); Address leftDataAddress = getLeftDataAddress();
Address rightDataAddress = getRightDataAddress(); Address rightDataAddress = getRightDataAddress();

View file

@ -193,7 +193,7 @@ class InfoPanel extends JPanel {
private HyperlinkComponent buildJavaVersionComponent() { private HyperlinkComponent buildJavaVersionComponent() {
String anchorName = "java_version"; String anchorName = "java_version";
final HyperlinkComponent javaVersionComponent = final HyperlinkComponent javaVersionComponent =
new HyperlinkComponent("<HTML><CENTER>Java Version " + "<A HREF=\"" + anchorName + new HyperlinkComponent("<html><CENTER>Java Version " + "<A HREF=\"" + anchorName +
"\">" + System.getProperty("java.version") + "</A></CENTER>"); "\">" + System.getProperty("java.version") + "</A></CENTER>");
javaVersionComponent.addHyperlinkListener(anchorName, e -> { javaVersionComponent.addHyperlinkListener(anchorName, e -> {
if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) { if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {

View file

@ -52,7 +52,7 @@ import ghidra.test.TestEnv;
public class ClearTest extends AbstractGhidraHeadedIntegrationTest { public class ClearTest extends AbstractGhidraHeadedIntegrationTest {
private static final String COMMENTS_CHECK_BOX_TEXT = private static final String COMMENTS_CHECK_BOX_TEXT =
"<HTML>Comments <FONT SIZE=\"2\">(does not affect automatic comments)</FONT>"; "<html>Comments <FONT SIZE=\"2\">(does not affect automatic comments)</FONT>";
private TestEnv env; private TestEnv env;
private PluginTool tool; private PluginTool tool;

View file

@ -361,8 +361,8 @@ public class HTMLDataTypeRepresentationTest extends AbstractGenericTest {
private String getParamText(String html) { private String getParamText(String html) {
// function html format: // function html format:
// <HTML>void&nbsp;myFunction(<BR>&nbsp;&nbsp;&nbsp;&nbsp;byte&nbsp;)<BR></HTML> // <html>void&nbsp;myFunction(<BR>&nbsp;&nbsp;&nbsp;&nbsp;byte&nbsp;)<BR></HTML>
Pattern p = Pattern.compile("\\((.*)\\)"); Pattern p = Pattern.compile("\\((.*)\\)");
Matcher matcher = p.matcher(html); Matcher matcher = p.matcher(html);

View file

@ -102,13 +102,22 @@ public class ByteViewerComponent extends FieldPanel implements FieldMouseListene
ByteBlockInfo info = indexMap.getBlockInfo(fieldLoc.getIndex(), fieldLoc.getFieldNum()); ByteBlockInfo info = indexMap.getBlockInfo(fieldLoc.getIndex(), fieldLoc.getFieldNum());
if (info != null) { if (info != null) {
String modelName = model.getName(); String modelName = model.getName();
return modelName + " format at " + String location = getAccessibleLocationInfo(info.getBlock(), info.getOffset());
info.getBlock().getLocationRepresentation(info.getOffset()) + ", value = " + return modelName + " format at " + location;
field.getText();
} }
return null; return null;
} }
private String getAccessibleLocationInfo(ByteBlock block, BigInteger offset) {
if (block instanceof MemoryByteBlock memBlock) {
// location represents an address, remove leading zeros to make screen reading concise
Address address = memBlock.getAddress(offset);
return address.toString(address.getAddressSpace().showSpaceName(), 1);
}
// otherwise use generic location representation
return block.getLocationRepresentation(offset);
}
@Override @Override
public void buttonPressed(FieldLocation fieldLocation, Field field, MouseEvent mouseEvent) { public void buttonPressed(FieldLocation fieldLocation, Field field, MouseEvent mouseEvent) {
if (fieldLocation == null || field == null) { if (fieldLocation == null || field == null) {

View file

@ -26,7 +26,6 @@ import docking.ComponentProvider;
import docking.action.*; import docking.action.*;
import docking.options.OptionsService; import docking.options.OptionsService;
import docking.widgets.fieldpanel.FieldPanel; import docking.widgets.fieldpanel.FieldPanel;
import docking.widgets.fieldpanel.internal.FieldPanelCoordinator;
import docking.widgets.fieldpanel.support.FieldLocation; import docking.widgets.fieldpanel.support.FieldLocation;
import docking.widgets.label.GDHtmlLabel; import docking.widgets.label.GDHtmlLabel;
import ghidra.GhidraOptions; import ghidra.GhidraOptions;
@ -40,7 +39,8 @@ import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.FunctionUtility; import ghidra.program.util.FunctionUtility;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.*; import ghidra.util.HTMLUtilities;
import ghidra.util.HelpLocation;
/** /**
* Panel that displays two decompilers for comparison * Panel that displays two decompilers for comparison
@ -133,7 +133,7 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
if (!titlePrefix.isEmpty()) { if (!titlePrefix.isEmpty()) {
titlePrefix += " "; // Add a space between prefix and title. titlePrefix += " "; // Add a space between prefix and title.
} }
String htmlPrefix = "<HTML>"; String htmlPrefix = "<html>";
if (title.startsWith(htmlPrefix)) { if (title.startsWith(htmlPrefix)) {
titlePanel.setTitleName(htmlPrefix + HTMLUtilities.friendlyEncodeHTML(titlePrefix) + titlePanel.setTitleName(htmlPrefix + HTMLUtilities.friendlyEncodeHTML(titlePrefix) +
title.substring(htmlPrefix.length())); title.substring(htmlPrefix.length()));
@ -285,7 +285,7 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
titlePanels[LEFT] = new TitledPanel(leftTitleLabel, cPanels[LEFT], 5); titlePanels[LEFT] = new TitledPanel(leftTitleLabel, cPanels[LEFT], 5);
titlePanels[RIGHT] = new TitledPanel(rightTitleLabel, cPanels[RIGHT], 5); titlePanels[RIGHT] = new TitledPanel(rightTitleLabel, cPanels[RIGHT], 5);
// Set the MINIMUM_PANEL_WIDTH for the left and right panel to prevent the split pane's // Set the MINIMUM_PANEL_WIDTH for the left and right panel to prevent the split pane's
// divider from becoming locked (can't be moved) due to extra long title names. // divider from becoming locked (can't be moved) due to extra long title names.
titlePanels[LEFT].setMinimumSize( titlePanels[LEFT].setMinimumSize(
new Dimension(MINIMUM_PANEL_WIDTH, titlePanels[LEFT].getMinimumSize().height)); new Dimension(MINIMUM_PANEL_WIDTH, titlePanels[LEFT].getMinimumSize().height));
@ -341,7 +341,7 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
} }
/** /**
* Sets the coordinator for the two decompiler panels within this code comparison panel. * Sets the coordinator for the two decompiler panels within this code comparison panel.
* It coordinates their scrolling and location synchronization. * It coordinates their scrolling and location synchronization.
* @param fieldPanelCoordinator the coordinator for the two decompiler panels * @param fieldPanelCoordinator the coordinator for the two decompiler panels
*/ */
@ -589,7 +589,7 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
/** /**
* Sets whether or not the decompilers are displayed side by side. * Sets whether or not the decompilers are displayed side by side.
* *
* @param sideBySide if true, the decompilers are side by side, otherwise one is above * @param sideBySide if true, the decompilers are side by side, otherwise one is above
* the other. * the other.
*/ */
private void showSideBySide(boolean sideBySide) { private void showSideBySide(boolean sideBySide) {
@ -648,7 +648,7 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
new MyDecompileResultsListener(leftCursorLocation, rightCursorLocation); new MyDecompileResultsListener(leftCursorLocation, rightCursorLocation);
//TEMP FIX - correct when refactoring //TEMP FIX - correct when refactoring
// Clear any previous MyDecompileResultsListener that is for a decompiler load // Clear any previous MyDecompileResultsListener that is for a decompiler load
//that hasn't finished. //that hasn't finished.
Set<MyDecompileResultsListener> toRemove = new HashSet<>(); Set<MyDecompileResultsListener> toRemove = new HashSet<>();
for (DualDecompileResultsListener l : dualDecompileResultsListenerList) { for (DualDecompileResultsListener l : dualDecompileResultsListenerList) {
@ -780,7 +780,7 @@ public abstract class DecompilerCodeComparisonPanel<T extends DualDecompilerFiel
ToggleOrientationAction() { ToggleOrientationAction() {
super("Dual Decompiler Toggle Orientation", "FunctionComparison"); super("Dual Decompiler Toggle Orientation", "FunctionComparison");
setDescription("<HTML>Toggle the layout of the decompiler " + setDescription("<html>Toggle the layout of the decompiler " +
"<BR>between side-by-side and one above the other."); "<BR>between side-by-side and one above the other.");
setEnabled(true); setEnabled(true);
setSelected(isSideBySide); setSelected(isSideBySide);

View file

@ -16,9 +16,7 @@
package ghidra.app.decompiler.component; package ghidra.app.decompiler.component;
import java.awt.*; import java.awt.*;
import java.awt.event.ComponentAdapter; import java.awt.event.*;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
@ -45,7 +43,6 @@ import ghidra.app.decompiler.component.hover.DecompilerHoverService;
import ghidra.app.decompiler.component.margin.*; import ghidra.app.decompiler.component.margin.*;
import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider; import ghidra.app.plugin.core.decompile.DecompilerClipboardProvider;
import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation; import ghidra.app.plugin.core.decompile.actions.FieldBasedSearchLocation;
import ghidra.app.util.viewer.listingpanel.MarginProvider;
import ghidra.app.util.viewer.util.ScrollpaneAlignedHorizontalLayout; import ghidra.app.util.viewer.util.ScrollpaneAlignedHorizontalLayout;
import ghidra.program.model.address.*; import ghidra.program.model.address.*;
import ghidra.program.model.listing.Function; import ghidra.program.model.listing.Function;
@ -128,7 +125,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
fieldPanel.addFieldMouseListener(this); fieldPanel.addFieldMouseListener(this);
fieldPanel.addFieldLocationListener(this); fieldPanel.addFieldLocationListener(this);
fieldPanel.addLayoutListener(this); fieldPanel.addLayoutListener(this);
fieldPanel.addComponentListener(new ComponentAdapter() { fieldPanel.addComponentListener(new ComponentAdapter() {
@Override @Override
public void componentResized(ComponentEvent e) { public void componentResized(ComponentEvent e) {
@ -1317,7 +1314,7 @@ public class DecompilerPanel extends JPanel implements FieldMouseListener, Field
if (f == null) { if (f == null) {
return null; return null;
} }
return "line " + (l.getIndex().intValue() + 1) + ", " + f.getText(); return "line " + (l.getIndex().intValue() + 1);
}); });
} }

View file

@ -52,7 +52,8 @@ import ghidra.graph.viewer.vertex.VertexClickListener;
import ghidra.program.model.address.Address; import ghidra.program.model.address.Address;
import ghidra.program.model.listing.*; import ghidra.program.model.listing.*;
import ghidra.program.util.ProgramLocation; import ghidra.program.util.ProgramLocation;
import ghidra.util.*; import ghidra.util.HelpLocation;
import ghidra.util.SystemUtilities;
import ghidra.util.exception.CancelledException; import ghidra.util.exception.CancelledException;
import ghidra.util.task.TaskMonitor; import ghidra.util.task.TaskMonitor;
import resources.Icons; import resources.Icons;
@ -172,9 +173,9 @@ public class FcgProvider
if (!graphData.hasResults()) { if (!graphData.hasResults()) {
return; return;
} }
// The graph component won't contain valid perspective information if it's not 'initialized' // The graph component won't contain valid perspective information if it's not 'initialized'
// (i.e. rendered or displayed to the user). It may happen if the graph has been kept minimized // (i.e. rendered or displayed to the user). It may happen if the graph has been kept minimized
// in the separate tab. Related method: `GraphComponent::viewerInitialized` // in the separate tab. Related method: `GraphComponent::viewerInitialized`
if (view.getGraphComponent().isUninitialized()) { if (view.getGraphComponent().isUninitialized()) {
return; return;
@ -425,10 +426,10 @@ public class FcgProvider
navigateIncomingToggleAction.setSelected(true); navigateIncomingToggleAction.setSelected(true);
navigateIncomingToggleAction.setToolBarData( navigateIncomingToggleAction.setToolBarData(
new ToolBarData(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON, TOOLBAR_GROUP_A)); new ToolBarData(Icons.NAVIGATE_ON_INCOMING_EVENT_ICON, TOOLBAR_GROUP_A));
navigateIncomingToggleAction.setDescription(HTMLUtilities.toHTML( navigateIncomingToggleAction.setDescription(
"Incoming Navigation<br><br>Toggle <b>On</b> - change the graphed " + "<html>Incoming Navigation<br><br>Toggle <b>On</b> - change the graphed " +
"function on Listing navigation events" + "function on Listing navigation events" +
"<br>Toggled <b>Off</b> - don't change the graph on Listing navigation events")); "<br>Toggled <b>Off</b> - don't change the graph on Listing navigation events");
navigateIncomingToggleAction.setHelpLocation( navigateIncomingToggleAction.setHelpLocation(
new HelpLocation(plugin.getName(), "Navigation_Incoming")); new HelpLocation(plugin.getName(), "Navigation_Incoming"));
addLocalAction(navigateIncomingToggleAction); addLocalAction(navigateIncomingToggleAction);

View file

@ -33,7 +33,7 @@ import ghidra.service.graph.*;
import ghidra.util.HTMLUtilities; import ghidra.util.HTMLUtilities;
/** /**
* Generates tool tips for an {@link AttributedVertex} or {@link AttributedEdge} in * Generates tool tips for an {@link AttributedVertex} or {@link AttributedEdge} in
* an {@link AttributedGraph} * an {@link AttributedGraph}
*/ */
public class AttributedToolTipInfo extends ToolTipInfo<Attributed> { public class AttributedToolTipInfo extends ToolTipInfo<Attributed> {
@ -79,7 +79,7 @@ public class AttributedToolTipInfo extends ToolTipInfo<Attributed> {
} }
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
buf.append("<HTML>"); buf.append("<html>");
if (graphObject instanceof AttributedVertex) { if (graphObject instanceof AttributedVertex) {
addToolTipTextForVertex(buf, (AttributedVertex) graphObject); addToolTipTextForVertex(buf, (AttributedVertex) graphObject);

View file

@ -36,8 +36,8 @@ import ghidra.program.util.ProgramMergeFilter;
import ghidra.util.HelpLocation; import ghidra.util.HelpLocation;
/** /**
* The DiffSettingsDialog is used to change the types of differences currently * The DiffSettingsDialog is used to change the types of differences currently
* highlighted. It also allows the user to change the types of differences being * highlighted. It also allows the user to change the types of differences being
* applied and whether labels and/or comments are being merged or replaced. * applied and whether labels and/or comments are being merged or replaced.
*/ */
public class DiffApplySettingsProvider extends ComponentProviderAdapter { public class DiffApplySettingsProvider extends ComponentProviderAdapter {
@ -262,7 +262,7 @@ public class DiffApplySettingsProvider extends ComponentProviderAdapter {
createChoices(); createChoices();
VariableHeightPanel panel = new VariableHeightPanel(false, 10, 3); VariableHeightPanel panel = new VariableHeightPanel(false, 10, 3);
panel.setToolTipText("<HTML>" + panel.setToolTipText("<html>" +
"For each difference type, select whether to ignore, replace, or merge." + "For each difference type, select whether to ignore, replace, or merge." +
"<BR>&nbsp&nbsp<B>Ignore</B> - don't apply this type of difference." + "<BR>&nbsp&nbsp<B>Ignore</B> - don't apply this type of difference." +
"<BR>&nbsp&nbsp<B>Replace</B> - replace the difference type with the one from program 2." + "<BR>&nbsp&nbsp<B>Replace</B> - replace the difference type with the one from program 2." +

View file

@ -44,75 +44,75 @@ import ghidra.util.layout.*;
import help.HelpService; import help.HelpService;
/** /**
* The ApplyMarkupPropertyEditor provides a custom GUI layout for the options that are used when * The ApplyMarkupPropertyEditor provides a custom GUI layout for the options that are used when
* applying version tracking markup. * applying version tracking markup.
*/ */
public class ApplyMarkupPropertyEditor implements OptionsEditor { public class ApplyMarkupPropertyEditor implements OptionsEditor {
// help tooltips // help tooltips
private static final String DATA_MATCH_DATA_TYPE_TOOLTIP = private static final String DATA_MATCH_DATA_TYPE_TOOLTIP =
"<HTML>The apply action for the <b>data type on a data match</b> when performing bulk apply operations</HTML>"; "<html>The apply action for the <b>data type on a data match</b> when performing bulk apply operations</html>";
private static final String LABELS_TOOLTIP = private static final String LABELS_TOOLTIP =
"<HTML>The apply action for <b>labels</b> when performing bulk apply operations</HTML>"; "<html>The apply action for <b>labels</b> when performing bulk apply operations</html>";
private static final String FUNCTION_NAME_TOOLTIP = private static final String FUNCTION_NAME_TOOLTIP =
"<HTML>The apply action for <b>function name</b> when performing bulk apply operations</HTML>"; "<html>The apply action for <b>function name</b> when performing bulk apply operations</html>";
private static final String FUNCTION_SIGNATURE_TOOLTIP = private static final String FUNCTION_SIGNATURE_TOOLTIP =
"<HTML>The apply action for the <b>function signature</b> " + "<html>The apply action for the <b>function signature</b> " +
"when performing bulk apply operations</HTML>"; "when performing bulk apply operations</html>";
private static final String PLATE_COMMENT_TOOLTIP = private static final String PLATE_COMMENT_TOOLTIP =
"<HTML>The apply action for <b>plate comments</b> when performing bulk apply operations</HTML>"; "<html>The apply action for <b>plate comments</b> when performing bulk apply operations</html>";
private static final String PRE_COMMENT_TOOLTIP = private static final String PRE_COMMENT_TOOLTIP =
"<HTML>The apply action for <b>pre comments</b> when performing bulk apply operations</HTML>"; "<html>The apply action for <b>pre comments</b> when performing bulk apply operations</html>";
private static final String END_OF_LINE_COMMENT_TOOLTIP = private static final String END_OF_LINE_COMMENT_TOOLTIP =
"<HTML>The apply action for <b>end of line comments</b> when performing bulk apply operations</HTML>"; "<html>The apply action for <b>end of line comments</b> when performing bulk apply operations</html>";
private static final String REPEATABLE_COMMENT_TOOLTIP = private static final String REPEATABLE_COMMENT_TOOLTIP =
"<HTML>The apply action for <b>repeatable comments</b> when performing bulk apply operations</HTML>"; "<html>The apply action for <b>repeatable comments</b> when performing bulk apply operations</html>";
private static final String POST_COMMENT_TOOLTIP = private static final String POST_COMMENT_TOOLTIP =
"<HTML>The apply action for <b>post comments</b> when performing bulk apply operations</HTML>"; "<html>The apply action for <b>post comments</b> when performing bulk apply operations</html>";
private static final String FUNCTION_RETURN_TYPE_TOOLTIP = private static final String FUNCTION_RETURN_TYPE_TOOLTIP =
"<HTML>The apply action for <b>function return type</b> when the function signature is applied</HTML>"; "<html>The apply action for <b>function return type</b> when the function signature is applied</html>";
private static final String INLINE_TOOLTIP = private static final String INLINE_TOOLTIP =
"<HTML>The apply action to use for the <b>function inline flag</b> " + "<html>The apply action to use for the <b>function inline flag</b> " +
"when applying the function signature</HTML>"; "when applying the function signature</html>";
private static final String NO_RETURN_TOOLTIP = private static final String NO_RETURN_TOOLTIP =
"<HTML>The apply action to use for the <b>function no return flag</b> " + "<html>The apply action to use for the <b>function no return flag</b> " +
"when applying the function signature</HTML>"; "when applying the function signature</html>";
private static final String CALLING_CONVENTION_TOOLTIP = private static final String CALLING_CONVENTION_TOOLTIP =
"<HTML>The apply action to use for the <b>function calling convention</b> " + "<html>The apply action to use for the <b>function calling convention</b> " +
"when applying the function signature</HTML>"; "when applying the function signature</html>";
private static final String CALL_FIXUP_TOOLTIP = private static final String CALL_FIXUP_TOOLTIP =
"<HTML>The apply action for <b>whether or not to apply call fixup</b> " + "<html>The apply action for <b>whether or not to apply call fixup</b> " +
"when applying the function signature</HTML>"; "when applying the function signature</html>";
private static final String VAR_ARGS_TOOLTIP = private static final String VAR_ARGS_TOOLTIP =
"<HTML>The apply action to use for the <b>var args flag</b> " + "<html>The apply action to use for the <b>var args flag</b> " +
"when applying the function signature</HTML>"; "when applying the function signature</html>";
private static final String PARAMETER_DATA_TYPES_TOOLTIP = private static final String PARAMETER_DATA_TYPES_TOOLTIP =
"<HTML>The apply action for <b>function parameter data types</b> when applying the function signature</HTML>"; "<html>The apply action for <b>function parameter data types</b> when applying the function signature</html>";
private static final String PARAMETER_NAMES_TOOLTIP = private static final String PARAMETER_NAMES_TOOLTIP =
"<HTML>The apply action for <b>function parameter names</b> when applying the function signature</HTML>"; "<html>The apply action for <b>function parameter names</b> when applying the function signature</html>";
private static final String PARAMETER_NAME_PRIORITY_TOOTIP = private static final String PARAMETER_NAME_PRIORITY_TOOTIP =
"<HTML>Choose whether a parameter name with a User source type or Import source type is highest " + "<html>Choose whether a parameter name with a User source type or Import source type is highest " +
"priority when determining whether to replace the name or not when using the priority.</HTML>"; "priority when determining whether to replace the name or not when using the priority.</html>";
private static final String HIGHEST_NAME_PRIORITY_TOOLTIP = private static final String HIGHEST_NAME_PRIORITY_TOOLTIP =
"<HTML>The apply action for <b>which source type is the highest priority</b> " + "<html>The apply action for <b>which source type is the highest priority</b> " +
"when applying parameter names using a priority replace</HTML>"; "when applying parameter names using a priority replace</html>";
private static final String USER_PRIORITY_TOOLTIP = private static final String USER_PRIORITY_TOOLTIP =
"<HTML>Parameter Name Source Type Priority <br>from highest to lowest:<br>" + "<html>Parameter Name Source Type Priority <br>from highest to lowest:<br>" +
"<blockquote>User Defined<br>Imported<br>Analysis<br>default (i.e. param_...)</blockquote></HTML>"; "<blockquote>User Defined<br>Imported<br>Analysis<br>default (i.e. param_...)</blockquote></html>";
private static final String IMPORT_PRIORITY_TOOLTIP = private static final String IMPORT_PRIORITY_TOOLTIP =
"<HTML>Parameter Name Source Type Priority <br>from highest to lowest:<br>" + "<html>Parameter Name Source Type Priority <br>from highest to lowest:<br>" +
"<blockquote>Imported<br>User Defined<br>Analysis<br>default (i.e. param_...)</blockquote></HTML>"; "<blockquote>Imported<br>User Defined<br>Analysis<br>default (i.e. param_...)</blockquote></html>";
private static final String PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY_TOOLTIP = private static final String PARAMETER_NAMES_REPLACE_IF_SAME_PRIORITY_TOOLTIP =
"<HTML>When function signature parameter names are being replaced based on source type priority, " + "<html>When function signature parameter names are being replaced based on source type priority, " +
"replace the destination name with the source name if their source types are the same.</HTML>"; "replace the destination name with the source name if their source types are the same.</html>";
private static final String PARAMETER_COMMENTS_TOOLTIP = private static final String PARAMETER_COMMENTS_TOOLTIP =
"<HTML>The apply action for <b>parameter comments</b> when applying the function signature</HTML>"; "<html>The apply action for <b>parameter comments</b> when applying the function signature</html>";
private static final String IGNORE_EXCLUDED_TOOLTIP = private static final String IGNORE_EXCLUDED_TOOLTIP =
"<HTML>Markup items whose \"apply option\" is set to <b>Do Not Apply</b> should be" + "<html>Markup items whose \"apply option\" is set to <b>Do Not Apply</b> should be" +
" changed to a status of <b>Ignored</b> by applying a match.</HTML>"; " changed to a status of <b>Ignored</b> by applying a match.</html>";
private static final String IGNORE_INCOMPLETE_TOOLTIP = private static final String IGNORE_INCOMPLETE_TOOLTIP =
"<HTML>Markup items that are <b>incomplete</b> (for example, no destination address is specified) " + "<html>Markup items that are <b>incomplete</b> (for example, no destination address is specified) " +
"should be changed to a status of <b>Ignored</b> by applying a match.</HTML>"; "should be changed to a status of <b>Ignored</b> by applying a match.</html>";
private JComponent editorComponent; private JComponent editorComponent;

View file

@ -190,7 +190,7 @@ public class StatusBar extends JPanel {
*/ */
public void setStatusText(String text) { public void setStatusText(String text) {
// Run this later in case we are in the midst of a Java focus transition, such as when a // Run this later in case we are in the midst of a Java focus transition, such as when a
// dialog is closing. If we don't let the focus transition finish, then we will not // dialog is closing. If we don't let the focus transition finish, then we will not
// correctly locate the active window. // correctly locate the active window.
Swing.runLater(() -> doSetStatusText(text)); Swing.runLater(() -> doSetStatusText(text));
} }
@ -287,7 +287,7 @@ public class StatusBar extends JPanel {
@Override @Override
public String getToolTipText() { public String getToolTipText() {
if (messageQueue.size() > 0) { if (messageQueue.size() > 0) {
StringBuffer buffer = new StringBuffer("<HTML>"); StringBuffer buffer = new StringBuffer("<html>");
Iterator<String> iter = messageQueue.iterator(); Iterator<String> iter = messageQueue.iterator();
for (int i = 0; iter.hasNext(); i++) { for (int i = 0; iter.hasNext(); i++) {

View file

@ -52,8 +52,8 @@ class DockingToolBarUtils {
private static String combingToolTipTextWithKeyBinding(String toolTipText, private static String combingToolTipTextWithKeyBinding(String toolTipText,
String keyBindingText) { String keyBindingText) {
StringBuilder buffy = new StringBuilder(toolTipText); StringBuilder buffy = new StringBuilder(toolTipText);
if (StringUtilities.startsWithIgnoreCase(toolTipText, "<HTML>")) { if (StringUtilities.startsWithIgnoreCase(toolTipText, "<html>")) {
String endHTMLTag = "</HTML>"; String endHTMLTag = "</html>";
int closeTagIndex = StringUtils.indexOfIgnoreCase(toolTipText, endHTMLTag); int closeTagIndex = StringUtils.indexOfIgnoreCase(toolTipText, endHTMLTag);
if (closeTagIndex < 0) { if (closeTagIndex < 0) {
// no closing tag, which is acceptable // no closing tag, which is acceptable

View file

@ -56,13 +56,13 @@ public class HorizontalRuleAction extends DockingAction {
String topHtml = HTMLUtilities.escapeHTML(topName); String topHtml = HTMLUtilities.escapeHTML(topName);
String bottomHtml = HTMLUtilities.escapeHTML(bottomName); String bottomHtml = HTMLUtilities.escapeHTML(bottomName);
menuData.setMenuItemNamePlain(String.format(""" menuData.setMenuItemNamePlain(String.format("""
<HTML><CENTER><FONT SIZE=2 COLOR="%s">%s<BR>%s</FONT></CENTER> <html><CENTER><FONT SIZE=2 COLOR="%s">%s<BR>%s</FONT></CENTER>
""", Palette.SILVER, topHtml, bottomHtml)); """, Palette.SILVER, topHtml, bottomHtml));
setMenuBarData(menuData); setMenuBarData(menuData);
// the description is meant to be used for the tooltip and is larger // the description is meant to be used for the tooltip and is larger
setDescription(String.format(""" setDescription(String.format("""
<HTML><CENTER><B>%s</B><HR><B>%s</B></CENTER> <html><CENTER><B>%s</B><HR><B>%s</B></CENTER>
""", PADDING + topHtml + PADDING, PADDING + bottomHtml + PADDING)); """, PADDING + topHtml + PADDING, PADDING + bottomHtml + PADDING));
} }

View file

@ -91,7 +91,7 @@ public class ColorEditor extends PropertyEditorSupport {
String colorString = String colorString =
WebColors.toString(ColorUtils.contrastForegroundColor(newColor), false); WebColors.toString(ColorUtils.contrastForegroundColor(newColor), false);
previewLabel.setText( previewLabel.setText(
"<HTML><CENTER><I><FONT SIZE=2 COLOR=" + colorString + ">click</FONT></I></CENTER>"); "<html><CENTER><I><FONT SIZE=2 COLOR=" + colorString + ">click</FONT></I></CENTER>");
previewLabel.setBackground(color); previewLabel.setBackground(color);
} }

View file

@ -102,7 +102,11 @@ public abstract class AbstractNumberInputDialog extends DialogComponentProvider
*/ */
protected JPanel buildMainPanel(String prompt, boolean showAsHex) { protected JPanel buildMainPanel(String prompt, boolean showAsHex) {
JPanel panel = createPanel(prompt); JPanel panel = createPanel(prompt);
numberInputField.addActionListener(e -> okCallback()); numberInputField.addActionListener(e -> {
if (okButton.isEnabled()) {
okCallback();
}
});
if (showAsHex) { if (showAsHex) {
numberInputField.setHexMode(); numberInputField.setHexMode();
@ -258,7 +262,7 @@ public abstract class AbstractNumberInputDialog extends DialogComponentProvider
//================================================================================================== //==================================================================================================
// Private Methods // Private Methods
//================================================================================================== //==================================================================================================
/** /**
* Create the main panel. * Create the main panel.

View file

@ -123,11 +123,13 @@ public class HTMLUtilities {
private static final int MAX_TOOLTIP_LENGTH = 2000; // arbitrary private static final int MAX_TOOLTIP_LENGTH = 2000; // arbitrary
private static final int TAB_SIZE = 4; private static final int TAB_SIZE = 4;
public static final String HTML = "<HTML>"; // Intentionally lower-case to match external checks for isHtml(), some of which check
public static final String HTML_CLOSE = "</HTML>"; // case-sensitive for "<html>"
public static final String BR = "<BR>"; public static final String HTML = "<html>";
public static final String PRE = "<PRE>"; public static final String HTML_CLOSE = "</html>";
public static final String PRE_CLOSE = "</PRE>"; public static final String BR = "<br>";
public static final String PRE = "<pre>";
public static final String PRE_CLOSE = "</pre>";
/** /**
* A tag to mark code that could be made into a hyperlink. This allows you to mark * A tag to mark code that could be made into a hyperlink. This allows you to mark
@ -171,35 +173,35 @@ public class HTMLUtilities {
*/ */
public static String colorString(Color color, String text) { public static String colorString(Color color, String text) {
String rgb = toHexString(color); String rgb = toHexString(color);
return "<FONT COLOR=\"" + rgb + "\">" + text + "</FONT>"; return "<font color=\"" + rgb + "\">" + text + "</font>";
} }
/** /**
* Surrounds the indicated text with HTML font coloring tags so that the * Surrounds the indicated text with HTML font coloring tags so that the
* text will display in color within HTML. * text will display in color within HTML.
* @param rgbColor (eg. "#8c0000") a string indicating the RGB hexadecimal color * @param rgbColor (e.g., "#8c0000") a string indicating the RGB hexadecimal color
* @param text the original text * @param text the original text
* @return the string for HTML colored text * @return the string for HTML colored text
*/ */
public static String colorString(String rgbColor, String text) { public static String colorString(String rgbColor, String text) {
return "<FONT COLOR=\"" + rgbColor + "\">" + text + "</FONT>"; return "<font color=\"" + rgbColor + "\">" + text + "</font>";
} }
/** /**
* Surrounds the indicated numeric value with HTML font coloring tags so that the * Surrounds the indicated numeric value with HTML font coloring tags so that the
* numeric value will display in color within HTML. * numeric value will display in color within HTML.
* @param rgbColor (eg. "#8c0000") a string indicating the RGB hexadecimal color * @param rgbColor (e.g., "#8c0000") a string indicating the RGB hexadecimal color
* @param value the numeric value to be converted to text and wrapped with color tags. * @param value the numeric value to be converted to text and wrapped with color tags.
* @return the string for the HTML colored number * @return the string for the HTML colored number
*/ */
public static String colorString(String rgbColor, int value) { public static String colorString(String rgbColor, int value) {
return "<FONT COLOR=\"" + rgbColor + "\">" + value + "</FONT>"; return "<font color=\"" + rgbColor + "\">" + value + "</font>";
} }
/** /**
* Creates a string with the indicated number of HTML space characters (<code>&#x26;nbsp;</code>). * Creates a string with the indicated number of HTML space characters (<code>&#x26;nbsp;</code>).
* @param num the number of HTML spaces * @param num the number of HTML spaces
* @return the string o HTML spaces * @return the string of HTML spaces
*/ */
public static String spaces(int num) { public static String spaces(int num) {
StringBuilder buf = new StringBuilder(HTML_SPACE.length() * num); StringBuilder buf = new StringBuilder(HTML_SPACE.length() * num);
@ -289,7 +291,7 @@ public class HTMLUtilities {
} }
/** /**
* Sets the font size of the given text by wrapping it in &lt;font&gt; tags. * Sets the font size of the given text by wrapping it in &lt;span&gt; tags.
* *
* @param text the text to size * @param text the text to size
* @param ptSize the point size of the text * @param ptSize the point size of the text
@ -303,19 +305,19 @@ public class HTMLUtilities {
} }
StringBuilder buffy = new StringBuilder(text); StringBuilder buffy = new StringBuilder(text);
buffy.insert(start, "<SPAN STYLE=\"font-size: " + ptSize + "pt\">"); buffy.insert(start, "<span style=\"font-size: " + ptSize + "pt\">");
int end = buffy.length(); int end = buffy.length();
if (StringUtilities.endsWithIgnoreCase(text, HTML_CLOSE)) { if (StringUtilities.endsWithIgnoreCase(text, HTML_CLOSE)) {
end = end - HTML_CLOSE.length(); end = end - HTML_CLOSE.length();
} }
buffy.insert(end, "</SPAN>"); buffy.insert(end, "</span>");
return buffy.toString(); return buffy.toString();
} }
/** /**
* Sets the font size and color of the given text by wrapping it in &lt;font&gt; tags. * Sets the font size and color of the given text by wrapping it in &lt;span&gt; tags.
* *
* @param text the text to size * @param text the text to size
* @param color the color of the text * @param color the color of the text
@ -330,14 +332,14 @@ public class HTMLUtilities {
} }
StringBuilder buffy = new StringBuilder(text); StringBuilder buffy = new StringBuilder(text);
buffy.insert(start, "<SPAN STYLE=\"font-size: " + ptSize + "pt; color: " + rgb + "\">"); buffy.insert(start, "<span style=\"font-size: " + ptSize + "pt; color: " + rgb + "\">");
int end = buffy.length(); int end = buffy.length();
if (StringUtilities.endsWithIgnoreCase(text, HTML_CLOSE)) { if (StringUtilities.endsWithIgnoreCase(text, HTML_CLOSE)) {
end = end - HTML_CLOSE.length(); end = end - HTML_CLOSE.length();
} }
buffy.insert(end, "</SPAN>"); buffy.insert(end, "</span>");
return buffy.toString(); return buffy.toString();
} }
@ -363,8 +365,8 @@ public class HTMLUtilities {
/** /**
* Takes HTML text wrapped by {@link #wrapWithLinkPlaceholder(String, String)} and replaces * Takes HTML text wrapped by {@link #wrapWithLinkPlaceholder(String, String)} and replaces
* the custom link comment tags with HTML anchor (<code>A</code>) tags, where the <code>HREF</code> * the custom link comment tags with HTML anchor (<code>A</code>) tags, where the
* value is the value that was in the <code>CONTENT</code> attribute. * <code>HREF</code> value is the value that was in the <code>CONTENT</code> attribute.
* *
* @param text the text for which to replace the markup * @param text the text for which to replace the markup
* @return the updated text * @return the updated text
@ -378,14 +380,14 @@ public class HTMLUtilities {
while (matcher.find()) { while (matcher.find()) {
String content = matcher.group(1); String content = matcher.group(1);
String escaped = content.replace("$", "\\$"); String escaped = content.replace("$", "\\$");
String updated = "<A HREF=\"" + escaped + "\">"; String updated = "<a href=\"" + escaped + "\">";
matcher.appendReplacement(buffy, updated); matcher.appendReplacement(buffy, updated);
} }
matcher.appendTail(buffy); matcher.appendTail(buffy);
String pass1 = buffy.toString(); String pass1 = buffy.toString();
String pass2 = pass1.replaceAll(LINK_PLACEHOLDER_CLOSE, "</A>"); String pass2 = pass1.replaceAll(LINK_PLACEHOLDER_CLOSE, "</a>");
return pass2; return pass2;
} }
@ -398,8 +400,7 @@ public class HTMLUtilities {
*/ */
public static String toHTML(String text) { public static String toHTML(String text) {
int noMax = 0; int noMax = 0;
String html = toWrappedHTML(text, noMax); return toWrappedHTML(text, noMax);
return html;
} }
/** /**
@ -593,7 +594,7 @@ public class HTMLUtilities {
* <p> * <p>
* See also <code>StringEscapeUtils#escapeHtml3(String)</code> if you need quote-safe html encoding. * See also <code>StringEscapeUtils#escapeHtml3(String)</code> if you need quote-safe html encoding.
* <p> * <p>
* *
* @param text plain-text that might have some characters that should NOT be interpreted as HTML * @param text plain-text that might have some characters that should NOT be interpreted as HTML
* @param makeSpacesNonBreaking true to convert spaces into {@value #HTML_SPACE} * @param makeSpacesNonBreaking true to convert spaces into {@value #HTML_SPACE}
* @return string with any html characters replaced with equivalents * @return string with any html characters replaced with equivalents
@ -643,8 +644,8 @@ public class HTMLUtilities {
} }
/** /**
* Tests a unicode code point (i.e., 32 bit character) to see if it needs to be escaped before * Tests a unicode code point (i.e., 32 bit character) to see if it needs to be escaped before
* being added to a HTML document because it is non-printable or a non-standard control * being added to a HTML document because it is non-printable or a non-standard control
* character * character
* *
* @param codePoint character to test * @param codePoint character to test
@ -802,8 +803,8 @@ public class HTMLUtilities {
// formatting tags (like <B>, <FONT>, etc). So, just normalize the text, not // formatting tags (like <B>, <FONT>, etc). So, just normalize the text, not
// preserving any of the line breaks. // preserving any of the line breaks.
// //
// Note: Calling this method here causes unwanted removal of newlines. If the original // Note: Calling this method here causes unwanted removal of newlines. If the original
// need for this call is found, this can be revisited. // need for this call is found, this can be revisited.
// (see history for condense() code) // (see history for condense() code)
// String condensed = condense(updated); // String condensed = condense(updated);
return updated; return updated;

View file

@ -107,7 +107,7 @@ public class HTMLUtilitiesTest {
@Test @Test
public void testToLiteralHTML_AlreadyStartingWithHTML() { public void testToLiteralHTML_AlreadyStartingWithHTML() {
String s = "<HTML>Wrap<BR>here"; String s = "<html>Wrap<BR>here";
String html = HTMLUtilities.toLiteralHTML(s, 4); String html = HTMLUtilities.toLiteralHTML(s, 4);
assertEquals(HTML + "&lt;HTM<BR>\nL&gt;Wr<BR>\nap&lt;B<BR>\nR&gt;he<BR>\nre", html); assertEquals(HTML + "&lt;HTM<BR>\nL&gt;Wr<BR>\nap&lt;B<BR>\nR&gt;he<BR>\nre", html);
} }
@ -121,7 +121,7 @@ public class HTMLUtilitiesTest {
@Test @Test
public void testFromHTML() { public void testFromHTML() {
String s = "<HTML><b>Bold</b>, <i>italics</i>, <font size='3'>sized font!</font>"; String s = "<html><b>Bold</b>, <i>italics</i>, <font size='3'>sized font!</font>";
String text = Swing.runNow(() -> HTMLUtilities.fromHTML(s)); String text = Swing.runNow(() -> HTMLUtilities.fromHTML(s));
assertEquals("Bold, italics, sized font!", text); assertEquals("Bold, italics, sized font!", text);
} }

View file

@ -15,14 +15,6 @@
*/ */
package help.screenshot; package help.screenshot;
import help.GHelpBuilder;
import help.HelpBuildUtils;
import help.validator.UnusedHelpImageFileFinder;
import help.validator.location.DirectoryHelpModuleLocation;
import help.validator.location.HelpModuleLocation;
import help.validator.model.HelpTopic;
import help.validator.model.IMG;
import java.awt.image.BufferedImage; import java.awt.image.BufferedImage;
import java.io.*; import java.io.*;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -36,6 +28,14 @@ import java.util.Map.Entry;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import help.GHelpBuilder;
import help.HelpBuildUtils;
import help.validator.UnusedHelpImageFileFinder;
import help.validator.location.DirectoryHelpModuleLocation;
import help.validator.location.HelpModuleLocation;
import help.validator.model.HelpTopic;
import help.validator.model.IMG;
public class HelpMissingScreenShotReportGenerator { public class HelpMissingScreenShotReportGenerator {
private static boolean debugEnabled; private static boolean debugEnabled;
@ -68,17 +68,17 @@ public class HelpMissingScreenShotReportGenerator {
generator.generateReport(); generator.generateReport();
} }
private Set<HelpModuleLocation> helpDirectories = new HashSet<HelpModuleLocation>(); private Set<HelpModuleLocation> helpDirectories = new HashSet<>();
private Map<String, HelpTopic> topicNameToTopic = new HashMap<String, HelpTopic>(); private Map<String, HelpTopic> topicNameToTopic = new HashMap<>();
private Set<HelpTestCase> testCases = new HashSet<HelpTestCase>(); private Set<HelpTestCase> testCases = new HashSet<>();
private Map<String, HelpTestCase> imageNameToTestCase = new HashMap<String, HelpTestCase>(); private Map<String, HelpTestCase> imageNameToTestCase = new HashMap<>();
private SortedSet<String> badlyNamedTestFiles = new TreeSet<String>(); private SortedSet<String> badlyNamedTestFiles = new TreeSet<>();
private SortedSet<HelpTestCase> badlyNamedTestCases = new TreeSet<HelpTestCase>(); private SortedSet<HelpTestCase> badlyNamedTestCases = new TreeSet<>();
// private Map<HelpDirectory, IMG> untestedImages = new TreeMap<HelpDirectory, IMG>(); // private Map<HelpDirectory, IMG> untestedImages = new TreeMap<HelpDirectory, IMG>();
private Map<HelpTopic, Set<IMG>> untestedImages = new TreeMap<HelpTopic, Set<IMG>>(); private Map<HelpTopic, Set<IMG>> untestedImages = new TreeMap<>();
private Set<Path> examinedImageFiles = new HashSet<Path>(); private Set<Path> examinedImageFiles = new HashSet<>();
private File outputFile; private File outputFile;
@ -125,8 +125,8 @@ public class HelpMissingScreenShotReportGenerator {
writer.write("<P>\n"); writer.write("<P>\n");
// //
// Total Image File Count // Total Image File Count
// //
int untestedCount = 0; int untestedCount = 0;
Collection<Set<IMG>> values = untestedImages.values(); Collection<Set<IMG>> values = untestedImages.values();
for (Set<IMG> set : values) { for (Set<IMG> set : values) {
@ -141,7 +141,7 @@ public class HelpMissingScreenShotReportGenerator {
// //
// All Tested Images // All Tested Images
// //
writer.write("<H3>\n"); writer.write("<H3>\n");
writer.write("Total Tested Images: " + imageNameToTestCase.size() + "\n"); writer.write("Total Tested Images: " + imageNameToTestCase.size() + "\n");
writer.write("</H3>\n"); writer.write("</H3>\n");
@ -432,7 +432,7 @@ public class HelpMissingScreenShotReportGenerator {
Set<IMG> set = untestedImages.get(topic); Set<IMG> set = untestedImages.get(topic);
if (set == null) { if (set == null) {
set = new TreeSet<IMG>(); set = new TreeSet<>();
untestedImages.put(topic, set); untestedImages.put(topic, set);
} }
@ -619,7 +619,7 @@ public class HelpMissingScreenShotReportGenerator {
} }
private void writeHeader(BufferedWriter writer) throws IOException { private void writeHeader(BufferedWriter writer) throws IOException {
writer.write("<HTML>\n"); writer.write("<html>\n");
writer.write("<HEAD>\n"); writer.write("<HEAD>\n");
createStyleSheet(writer); createStyleSheet(writer);
writer.write("</HEAD>\n"); writer.write("</HEAD>\n");
@ -635,7 +635,7 @@ public class HelpMissingScreenShotReportGenerator {
writer.write("<BR>\n"); writer.write("<BR>\n");
writer.write("</BODY>\n"); writer.write("</BODY>\n");
writer.write("</HTML>\n"); writer.write("</html>\n");
} }
private void createStyleSheet(BufferedWriter writer) throws IOException { private void createStyleSheet(BufferedWriter writer) throws IOException {
@ -643,11 +643,16 @@ public class HelpMissingScreenShotReportGenerator {
writer.write("<!--\n"); writer.write("<!--\n");
writer.write("body { font-family:arial; font-size:22pt }\n"); writer.write("body { font-family:arial; font-size:22pt }\n");
writer.write("h1 { color:#000080; font-family:times new roman; font-size:28pt; font-weight:bold; text-align:center; }\n"); writer.write(
writer.write("h2 { color:#984c4c; font-family:times new roman; font-size:28pt; font-weight:bold; }\n"); "h1 { color:#000080; font-family:times new roman; font-size:28pt; font-weight:bold; text-align:center; }\n");
writer.write("h2.title { color:#000080; font-family:times new roman; font-size:14pt; font-weight:bold; text-align:center;}\n"); writer.write(
writer.write("h3 { color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; margin-left:.5in }\n"); "h2 { color:#984c4c; font-family:times new roman; font-size:28pt; font-weight:bold; }\n");
writer.write("table { margin-left:1in; margin-right:1in; min-width:20em; width:90%; background-color:#EEEEFF }\n"); writer.write(
"h2.title { color:#000080; font-family:times new roman; font-size:14pt; font-weight:bold; text-align:center;}\n");
writer.write(
"h3 { color:#0000ff; font-family:times new roman; font-size:14pt; font-weight:bold; margin-left:.5in }\n");
writer.write(
"table { margin-left:1in; margin-right:1in; min-width:20em; width:90%; background-color:#EEEEFF }\n");
writer.write("th { text-align:center; }\n"); writer.write("th { text-align:center; }\n");
writer.write("td { text-align:left; padding: 20px }\n"); writer.write("td { text-align:left; padding: 20px }\n");
@ -665,7 +670,8 @@ public class HelpMissingScreenShotReportGenerator {
private Path filePath; private Path filePath;
private HelpTopic helpTopic; private HelpTopic helpTopic;
HelpTestFile(HelpModuleLocation helpDir, HelpTopic helpTopic, Path filePath, String filename) { HelpTestFile(HelpModuleLocation helpDir, HelpTopic helpTopic, Path filePath,
String filename) {
this.helpDir = helpDir; this.helpDir = helpDir;
this.helpTopic = helpTopic; this.helpTopic = helpTopic;
this.filePath = filePath; this.filePath = filePath;

View file

@ -146,7 +146,7 @@ public class HelpScreenShotReportGenerator {
} }
private void writeHeader(BufferedWriter writer) throws IOException { private void writeHeader(BufferedWriter writer) throws IOException {
writer.write("<HTML>\n"); writer.write("<html>\n");
writer.write("<HEAD>\n"); writer.write("<HEAD>\n");
createStyleSheet(writer); createStyleSheet(writer);
writer.write("</HEAD>\n"); writer.write("</HEAD>\n");
@ -178,7 +178,7 @@ public class HelpScreenShotReportGenerator {
writer.write("</P>\n"); writer.write("</P>\n");
writer.write("</BODY>\n"); writer.write("</BODY>\n");
writer.write("</HTML>\n"); writer.write("</html>\n");
} }
private void createStyleSheet(BufferedWriter writer) throws IOException { private void createStyleSheet(BufferedWriter writer) throws IOException {

View file

@ -94,8 +94,8 @@ public abstract class AbstractHelpTest extends AbstractGenericTest {
Path file = Files.createFile(fullTOCPath); Path file = Files.createFile(fullTOCPath);
//@formatter:off //@formatter:off
String TOCXML = "<?xml version='1.0' encoding='ISO-8859-1' ?>\n" + String TOCXML = "<?xml version='1.0' encoding='ISO-8859-1' ?>\n" +
"<!-- Auto-generated on Fri Apr 03 09:37:08 EDT 2015 -->\n\n" + "<!-- Auto-generated on Fri Apr 03 09:37:08 EDT 2015 -->\n\n" +
"<tocroot>\n" + "<tocroot>\n" +
"</tocroot>\n"; "</tocroot>\n";
@ -114,14 +114,14 @@ public abstract class AbstractHelpTest extends AbstractGenericTest {
} }
//@formatter:off //@formatter:off
String HTML = String HTML =
"<HTML>\n" + "<html>\n" +
"<HEAD>\n" + "<HEAD>\n" +
"<TITLE>Configure Tool</TITLE>\n" + "<TITLE>Configure Tool</TITLE>\n" +
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" + "<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" +
"</HEAD>\n" + "</HEAD>\n" +
"<BODY>\n" + "<BODY>\n" +
" <H1><A name=\""+anchor+"\"></A>Configure Tool</H1>\n" + " <H1><A name=\""+anchor+"\"></A>Configure Tool</H1>\n" +
" Some text with reference to shared image <IMG src=\"help/shared/note.png\">\n" + " Some text with reference to shared image <IMG src=\"help/shared/note.png\">\n" +
" \n" + " \n" +
@ -140,19 +140,19 @@ public abstract class AbstractHelpTest extends AbstractGenericTest {
assertNotNull("Must specify the A tag HREF attribute", HREF); assertNotNull("Must specify the A tag HREF attribute", HREF);
//@formatter:off //@formatter:off
String HTML = String HTML =
"<HTML>\n" + "<html>\n" +
"<HEAD>\n" + "<HEAD>\n" +
"<TITLE>Configure Tool</TITLE>\n" + "<TITLE>Configure Tool</TITLE>\n" +
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" + "<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" +
"</HEAD>\n" + "</HEAD>\n" +
"<BODY>\n" + "<BODY>\n" +
" <H1><A name=\"Fake_Anchor\"></A>Configure Tool</H1>\n" + " <H1><A name=\"Fake_Anchor\"></A>Configure Tool</H1>\n" +
" And this is a link <A HREF=\""+HREF+"\">Click Me</A>" + " And this is a link <A HREF=\""+HREF+"\">Click Me</A>" +
" \n" + " \n" +
"</BODY>\n" + "</BODY>\n" +
"</HTML>\n"; "</html>\n";
//@formatter:on //@formatter:on
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE); Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
@ -167,19 +167,19 @@ public abstract class AbstractHelpTest extends AbstractGenericTest {
assertNotNull("Must specify the A tag SRC attribute", SRC); assertNotNull("Must specify the A tag SRC attribute", SRC);
//@formatter:off //@formatter:off
String HTML = String HTML =
"<HTML>\n" + "<html>\n" +
"<HEAD>\n" + "<HEAD>\n" +
"<TITLE>Configure Tool</TITLE>\n" + "<TITLE>Configure Tool</TITLE>\n" +
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" + "<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" +
"</HEAD>\n" + "</HEAD>\n" +
"<BODY>\n" + "<BODY>\n" +
" <H1><A name=\"Fake_Anchor\"></A>Configure Tool</H1>\n" + " <H1><A name=\"Fake_Anchor\"></A>Configure Tool</H1>\n" +
" Some text with reference to shared image <IMG src=\""+SRC+"\">\n" + " Some text with reference to shared image <IMG src=\""+SRC+"\">\n" +
" \n" + " \n" +
"</BODY>\n" + "</BODY>\n" +
"</HTML>\n"; "</html>\n";
//@formatter:on //@formatter:on
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE); Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);

View file

@ -150,7 +150,7 @@ public class HelpFileTest extends AbstractHelpTest {
assertTrue(hrefs.isEmpty()); assertTrue(hrefs.isEmpty());
} }
// @Test // @Test
// for debugging a real help file // for debugging a real help file
public void test() throws Exception { public void test() throws Exception {
@ -183,18 +183,18 @@ public class HelpFileTest extends AbstractHelpTest {
String badAttr = "bob=1"; String badAttr = "bob=1";
//@formatter:off //@formatter:off
String HTML = String HTML =
"<HTML>\n" + "<html>\n" +
"<HEAD>\n" + "<HEAD>\n" +
"<TITLE>Configure Tool</TITLE>\n" + "<TITLE>Configure Tool</TITLE>\n" +
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" + "<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" +
"</HEAD>\n" + "</HEAD>\n" +
"<BODY>\n" + "<BODY>\n" +
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" + "<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
"Some text with reference to shared image <a "+badAttr+">Click me</a>\n" + "Some text with reference to shared image <a "+badAttr+">Click me</a>\n" +
"\n" + "\n" +
"</BODY>\n" + "</BODY>\n" +
"</HTML>\n"; "</html>\n";
//@formatter:on //@formatter:on
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE); Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
@ -208,18 +208,18 @@ public class HelpFileTest extends AbstractHelpTest {
String badAttr = "bob=1"; String badAttr = "bob=1";
//@formatter:off //@formatter:off
String HTML = String HTML =
"<HTML>\n" + "<html>\n" +
"<HEAD>\n" + "<HEAD>\n" +
"<TITLE>Configure Tool</TITLE>\n" + "<TITLE>Configure Tool</TITLE>\n" +
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" + "<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" +
"</HEAD>\n" + "</HEAD>\n" +
"<BODY>\n" + "<BODY>\n" +
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" + "<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
"Some text with reference to shared image <IMG "+badAttr+"s>\n" + "Some text with reference to shared image <IMG "+badAttr+"s>\n" +
"\n" + "\n" +
"</BODY>\n" + "</BODY>\n" +
"</HTML>\n"; "</html>\n";
//@formatter:on //@formatter:on
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE); Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
@ -233,18 +233,18 @@ public class HelpFileTest extends AbstractHelpTest {
String badURI = ":baduri"; // no scheme name on this URI String badURI = ":baduri"; // no scheme name on this URI
//@formatter:off //@formatter:off
String HTML = String HTML =
"<HTML>\n" + "<html>\n" +
"<HEAD>\n" + "<HEAD>\n" +
"<TITLE>Configure Tool</TITLE>\n" + "<TITLE>Configure Tool</TITLE>\n" +
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" + "<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" +
"</HEAD>\n" + "</HEAD>\n" +
"<BODY>\n" + "<BODY>\n" +
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" + "<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
"Some text with reference to shared image <a href=\""+badURI+"\">Click me</a>\n" + "Some text with reference to shared image <a href=\""+badURI+"\">Click me</a>\n" +
"\n" + "\n" +
"</BODY>\n" + "</BODY>\n" +
"</HTML>\n"; "</html>\n";
//@formatter:on //@formatter:on
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE); Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
@ -258,18 +258,18 @@ public class HelpFileTest extends AbstractHelpTest {
String badName = "bad_name"; String badName = "bad_name";
//@formatter:off //@formatter:off
String HTML = String HTML =
"<HTML>\n" + "<html>\n" +
"<HEAD>\n" + "<HEAD>\n" +
"<TITLE>Configure Tool</TITLE>\n" + "<TITLE>Configure Tool</TITLE>\n" +
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/"+badName+".css\">\n" + "<LINK rel=\"stylesheet\" type=\"text/css\" href=\"../../shared/"+badName+".css\">\n" +
"</HEAD>\n" + "</HEAD>\n" +
"<BODY>\n" + "<BODY>\n" +
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" + "<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
"Some text with reference to shared image <IMG src=\"help/shared/DefaultStyle.css\">\n" + "Some text with reference to shared image <IMG src=\"help/shared/DefaultStyle.css\">\n" +
"\n" + "\n" +
"</BODY>\n" + "</BODY>\n" +
"</HTML>\n"; "</html>\n";
//@formatter:on //@formatter:on
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE); Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);
@ -284,20 +284,20 @@ public class HelpFileTest extends AbstractHelpTest {
String badURI = ":baduri"; // no scheme name on this URI String badURI = ":baduri"; // no scheme name on this URI
//@formatter:off //@formatter:off
String HTML = String HTML =
"<HTML>\n" + "<html>\n" +
"<HEAD>\n" + "<HEAD>\n" +
"<TITLE>Configure Tool</TITLE>\n" + "<TITLE>Configure Tool</TITLE>\n" +
"<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" + "<LINK rel=\"stylesheet\" type=\"text/css\" href=\"help/shared/DefaultStyle.css\">\n" +
"</HEAD>\n" + "</HEAD>\n" +
"<BODY>\n" + "<BODY>\n" +
"<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" + "<H1><A name=\"ManagePluginsDialog\"></A>Configure Tool</H1>\n" +
" <!--" + " <!--" +
" Some text with reference to shared image <a href=\""+badURI+"\">Click me</a>\n" + " Some text with reference to shared image <a href=\""+badURI+"\">Click me</a>\n" +
" -->" + " -->" +
"\n" + "\n" +
"</BODY>\n" + "</BODY>\n" +
"</HTML>\n"; "</html>\n";
//@formatter:on //@formatter:on
Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE); Files.write(file, HTML.getBytes(), StandardOpenOption.CREATE);

View file

@ -54,9 +54,9 @@ public class ProjectDataTablePanel extends JPanel {
public Set<DomainFile> filesPendingSelection; public Set<DomainFile> filesPendingSelection;
private GHtmlLabel capacityExceededText = private GHtmlLabel capacityExceededText =
new GHtmlLabel("<HTML><CENTER><I>Table view disabled for very large projects, or<BR>" + new GHtmlLabel("<html><CENTER><I>Table view disabled for very large projects, or<BR>" +
"if an older project/repository filesystem is in use.<BR>" + "if an older project/repository filesystem is in use.<BR>" +
"View will remain disabled until project is closed.</I></CENTER></HTML>"); "View will remain disabled until project is closed.</I></CENTER></html>");
private GGlassPanePainter painter = new TableGlassPanePainter(); private GGlassPanePainter painter = new TableGlassPanePainter();
@ -95,8 +95,9 @@ public class ProjectDataTablePanel extends JPanel {
checkOpen(e); checkOpen(e);
} }
}); });
gTable.getSelectionModel().addListSelectionListener( gTable.getSelectionModel()
e -> plugin.getTool().contextChanged(null)); .addListSelectionListener(
e -> plugin.getTool().contextChanged(null));
gTable.setDefaultRenderer(Date.class, new DateCellRenderer()); gTable.setDefaultRenderer(Date.class, new DateCellRenderer());
gTable.setDefaultRenderer(DomainFileType.class, new TypeCellRenderer()); gTable.setDefaultRenderer(DomainFileType.class, new TypeCellRenderer());
@ -275,7 +276,7 @@ public class ProjectDataTablePanel extends JPanel {
//================================================================================================== //==================================================================================================
// Inner Classes // Inner Classes
//================================================================================================== //==================================================================================================
private class ProjectDataTableDomainFolderChangeListener implements DomainFolderChangeListener { private class ProjectDataTableDomainFolderChangeListener implements DomainFolderChangeListener {

View file

@ -89,17 +89,17 @@ public class ProjectDataDeleteAction extends FrontendProjectTreeAction {
if (fileCount == 1) { if (fileCount == 1) {
if (!selectedFiles.isEmpty()) { if (!selectedFiles.isEmpty()) {
DomainFile file = CollectionUtils.any(selectedFiles); DomainFile file = CollectionUtils.any(selectedFiles);
return "<HTML>Are you sure you want to <B><U>permanently</U></B> delete \"" + return "<html>Are you sure you want to <B><U>permanently</U></B> delete \"" +
HTMLUtilities.escapeHTML(file.getName()) + "\"?"; HTMLUtilities.escapeHTML(file.getName()) + "\"?";
} }
// only folders are selected, but they contain files // only folders are selected, but they contain files
return "<HTML>Are you sure you want to <B><U>permanently</U></B> delete the " + return "<html>Are you sure you want to <B><U>permanently</U></B> delete the " +
" selected files and folders?"; " selected files and folders?";
} }
// multiple files selected // multiple files selected
return "<HTML>Are you sure you want to <B><U>permanently</U></B> delete the " + fileCount + return "<html>Are you sure you want to <B><U>permanently</U></B> delete the " + fileCount +
" selected files?"; " selected files?";
} }

View file

@ -68,7 +68,7 @@ class PluginDetailsPanel extends AbstractDetailsPanel {
List<PluginDescription> dependencies = model.getDependencies(descriptor); List<PluginDescription> dependencies = model.getDependencies(descriptor);
Collections.sort(dependencies, (pd1, pd2) -> pd1.getName().compareTo(pd2.getName())); Collections.sort(dependencies, (pd1, pd2) -> pd1.getName().compareTo(pd2.getName()));
StringBuilder buffer = new StringBuilder("<HTML>"); StringBuilder buffer = new StringBuilder("<html>");
buffer.append("<TABLE cellpadding=2>"); buffer.append("<TABLE cellpadding=2>");

View file

@ -57,7 +57,7 @@ class ExtensionDetailsPanel extends AbstractDetailsPanel {
createFieldAttributes(); createFieldAttributes();
createMainPanel(); createMainPanel();
// Any time the table is reloaded or a new selection is made, we want to reload this // Any time the table is reloaded or a new selection is made, we want to reload this
// panel. This ensures we are always viewing data for the currently-selected item. // panel. This ensures we are always viewing data for the currently-selected item.
tablePanel.getTableModel().addThreadedTableModelListener(new ThreadedTableModelListener() { tablePanel.getTableModel().addThreadedTableModelListener(new ThreadedTableModelListener() {
@ -94,7 +94,7 @@ class ExtensionDetailsPanel extends AbstractDetailsPanel {
return; return;
} }
StringBuilder buffer = new StringBuilder("<HTML>"); StringBuilder buffer = new StringBuilder("<html>");
buffer.append("<TABLE cellpadding=2>"); buffer.append("<TABLE cellpadding=2>");
insertRowTitle(buffer, "Name"); insertRowTitle(buffer, "Name");

View file

@ -244,7 +244,7 @@ public class ProgramCompilerSpec extends BasicCompilerSpec {
return; return;
} }
StringBuilder buffer = new StringBuilder(); StringBuilder buffer = new StringBuilder();
buffer.append("<HTML>User-defined extensions failed to parse: "); buffer.append("<html>User-defined extensions failed to parse: ");
buffer.append("<ul>"); buffer.append("<ul>");
for (String line : errorList) { for (String line : errorList) {
buffer.append("<li>").append(line).append("</li>"); buffer.append("<li>").append(line).append("</li>");

View file

@ -57,9 +57,9 @@ public class DataTypeArchiveTransformerPanel extends JPanel {
private void setupDescription() { private void setupDescription() {
JLabel label = new GHtmlLabel( JLabel label = new GHtmlLabel(
"<HTML>Specify the files for converting a new data type archive (.gdt)<BR>" + "<html>Specify the files for converting a new data type archive (.gdt)<BR>" +
"to match the IDs of data types in an old data type archive.<BR>" + "to match the IDs of data types in an old data type archive.<BR>" +
"The result will be saved to the destination archive.</HTML>"); "The result will be saved to the destination archive.</html>");
label.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0)); label.setBorder(BorderFactory.createEmptyBorder(0, 0, 8, 0));
label.setHorizontalAlignment(SwingConstants.CENTER); label.setHorizontalAlignment(SwingConstants.CENTER);
add(label, BorderLayout.NORTH); add(label, BorderLayout.NORTH);

View file

@ -23,11 +23,11 @@ import java.util.*;
import generic.jar.ResourceFile; import generic.jar.ResourceFile;
import ghidra.framework.ApplicationProperties; import ghidra.framework.ApplicationProperties;
import ghidra.framework.GModule; import ghidra.framework.GModule;
import utility.application.ApplicationLayout; import ghidra.util.Msg;
import utility.module.ModuleUtilities; import utility.module.ModuleUtilities;
/** /**
* The Ghidra jar application layout defines the customizable elements of the Ghidra application's * The Ghidra jar application layout defines the customizable elements of the Ghidra application's
* directory structure when running in "single jar mode." * directory structure when running in "single jar mode."
*/ */
public class GhidraJarApplicationLayout extends GhidraApplicationLayout { public class GhidraJarApplicationLayout extends GhidraApplicationLayout {
@ -51,7 +51,11 @@ public class GhidraJarApplicationLayout extends GhidraApplicationLayout {
protected Collection<ResourceFile> findGhidraApplicationRootDirs() { protected Collection<ResourceFile> findGhidraApplicationRootDirs() {
List<ResourceFile> dirs = new ArrayList<>(); List<ResourceFile> dirs = new ArrayList<>();
String appPropPath = "/_Root/Ghidra/" + ApplicationProperties.PROPERTY_FILE; String appPropPath = "/_Root/Ghidra/" + ApplicationProperties.PROPERTY_FILE;
URL appPropUrl = ApplicationLayout.class.getResource(appPropPath); URL appPropUrl = getClass().getResource(appPropPath);
if (appPropUrl == null) {
throw new IllegalStateException(
"The Ghidra Jar must have an application.properties file at " + appPropPath);
}
ResourceFile rootDir = fromUrl(appPropUrl).getParentFile(); ResourceFile rootDir = fromUrl(appPropUrl).getParentFile();
dirs.add(rootDir); dirs.add(rootDir);
return dirs; return dirs;
@ -79,7 +83,12 @@ public class GhidraJarApplicationLayout extends GhidraApplicationLayout {
@Override @Override
protected List<ResourceFile> findExtensionInstallationDirectories() { protected List<ResourceFile> findExtensionInstallationDirectories() {
URL extensionInstallUrl = ApplicationLayout.class.getResource("/_Root/Ghidra/Extensions"); String path = "/_Root/Ghidra/Extensions";
URL extensionInstallUrl = getClass().getResource(path);
if (extensionInstallUrl == null) {
Msg.debug(this, "No Extensions dir found at " + path);
return List.of();
}
ResourceFile extensionInstallDir = fromUrl(extensionInstallUrl); ResourceFile extensionInstallDir = fromUrl(extensionInstallUrl);
return Collections.singletonList(extensionInstallDir); return Collections.singletonList(extensionInstallDir);
} }
@ -94,7 +103,7 @@ public class GhidraJarApplicationLayout extends GhidraApplicationLayout {
String urlString = url.toExternalForm(); String urlString = url.toExternalForm();
try { try {
// Decode the URL to replace things like %20 with real spaces. // Decode the URL to replace things like %20 with real spaces.
// Note: can't use URLDecoder.decode(String, Charset) because Utility must be // Note: can't use URLDecoder.decode(String, Charset) because Utility must be
// Java 1.8 compatible. // Java 1.8 compatible.
urlString = URLDecoder.decode(urlString, "UTF-8"); urlString = URLDecoder.decode(urlString, "UTF-8");
} }