mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2024-11-21 19:42:14 +00:00
Merge remote-tracking branch 'origin/GP-5049-dragonmacher-hyperlink-comp-activation--SQUASHED'
This commit is contained in:
commit
3f485449e8
@ -1,285 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* 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.features.bsim.gui.search.results;
|
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.event.HyperlinkEvent;
|
|
||||||
|
|
||||||
import docking.widgets.HyperlinkComponent;
|
|
||||||
import docking.widgets.table.DiscoverableTableUtils;
|
|
||||||
import docking.widgets.table.TableColumnDescriptor;
|
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
|
||||||
import ghidra.app.plugin.core.table.TableComponentProvider;
|
|
||||||
import ghidra.app.util.query.TableService;
|
|
||||||
import ghidra.docking.settings.Settings;
|
|
||||||
import ghidra.features.bsim.query.facade.SFQueryResult;
|
|
||||||
import ghidra.features.bsim.query.protocol.SimilarityResult;
|
|
||||||
import ghidra.framework.plugintool.ServiceProvider;
|
|
||||||
import ghidra.program.database.symbol.FunctionSymbol;
|
|
||||||
import ghidra.program.model.address.Address;
|
|
||||||
import ghidra.program.model.listing.Function;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.util.ProgramLocation;
|
|
||||||
import ghidra.util.Msg;
|
|
||||||
import ghidra.util.datastruct.Accumulator;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.table.AddressBasedTableModel;
|
|
||||||
import ghidra.util.table.field.*;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Panel that displays the list of functions to be included in a BSim query.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
class DisplayFunctionsPanel extends JPanel {
|
|
||||||
|
|
||||||
private static final String SHOW_TABLE_HREF_NAME = "ShowTable";
|
|
||||||
|
|
||||||
private static final Color MARKER_COLOR = Palette.getColor("lightskyblue");
|
|
||||||
|
|
||||||
private HyperlinkComponent functionsHTMLComponent;
|
|
||||||
|
|
||||||
private TableComponentProvider<?> tableProvider;
|
|
||||||
private final ServiceProvider serviceProvider;
|
|
||||||
private Set<FunctionSymbol> selectedFunctions;
|
|
||||||
|
|
||||||
// Maps input functions to the number of matches associated with it. This is
|
|
||||||
// here to provide quick access for the MatchCountTableColumn.
|
|
||||||
private Map<String, Integer> functionMatchMap = new HashMap<>();
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
DisplayFunctionsPanel(ServiceProvider serviceProvider, String desc) {
|
|
||||||
super(new BorderLayout());
|
|
||||||
this.serviceProvider = serviceProvider;
|
|
||||||
this.description = desc;
|
|
||||||
|
|
||||||
functionsHTMLComponent = new HyperlinkComponent(getNoFunctionsSelectedMessage());
|
|
||||||
functionsHTMLComponent.addHyperlinkListener(SHOW_TABLE_HREF_NAME, e -> {
|
|
||||||
if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
|
|
||||||
// not a mouse click
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showAllSelectedFunctionsTable();
|
|
||||||
});
|
|
||||||
|
|
||||||
add(functionsHTMLComponent, BorderLayout.CENTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes a new set of query results and parses the function counts.
|
|
||||||
*
|
|
||||||
* @param queryResult the object to hold the load results
|
|
||||||
*/
|
|
||||||
public void loadQueryResults(SFQueryResult queryResult) {
|
|
||||||
parseFunctionMatchCounts(queryResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getNoFunctionsSelectedMessage() {
|
|
||||||
StringBuilder buffy = new StringBuilder();
|
|
||||||
buffy.append(
|
|
||||||
"<html><font color=\"" + Palette.GRAY + "\"><i>No functions selected</i></font>");
|
|
||||||
return buffy.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void close() {
|
|
||||||
if (tableProvider != null) {
|
|
||||||
tableProvider.removeFromTool();
|
|
||||||
tableProvider = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedFunctions = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSelectedFunctions(Set<FunctionSymbol> functions) {
|
|
||||||
|
|
||||||
this.selectedFunctions = functions;
|
|
||||||
if (tableProvider != null && tableProvider.isInTool()) {
|
|
||||||
tableProvider.removeFromTool();
|
|
||||||
tableProvider = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (functions.isEmpty()) {
|
|
||||||
functionsHTMLComponent.setText(getNoFunctionsSelectedMessage());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String text = createTextForSelectedFunctions(functions);
|
|
||||||
functionsHTMLComponent.setText(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String createTextForSelectedFunctions(Set<FunctionSymbol> functions) {
|
|
||||||
|
|
||||||
if (functions.isEmpty()) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
StringBuilder buffy = new StringBuilder();
|
|
||||||
int count = functions.size();
|
|
||||||
Function firstFunc = (Function) functions.iterator().next().getObject();
|
|
||||||
String programName = firstFunc.getProgram().getDomainFile().getPathname();
|
|
||||||
buffy.append(description).append(" ");
|
|
||||||
buffy.append(firstFunc.getName());
|
|
||||||
if (count > 1) {
|
|
||||||
buffy.append(" and ");
|
|
||||||
buffy.append(count - 1);
|
|
||||||
buffy.append(" other function");
|
|
||||||
}
|
|
||||||
if (count > 2) {
|
|
||||||
buffy.append("s");
|
|
||||||
}
|
|
||||||
buffy.append(" from ");
|
|
||||||
buffy.append(programName);
|
|
||||||
buffy.append(" <a href=\"").append(SHOW_TABLE_HREF_NAME).append("\">"); // open anchor
|
|
||||||
buffy.append("<font color=\"" + Palette.BLUE + "\">");
|
|
||||||
buffy.append(" (show table) ");
|
|
||||||
buffy.append("</font>");
|
|
||||||
buffy.append("</a>"); // close anchor
|
|
||||||
return buffy.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showAllSelectedFunctionsTable() {
|
|
||||||
if (tableProvider != null) {
|
|
||||||
if (tableProvider.isInTool()) {
|
|
||||||
tableProvider.setVisible(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// it has been closed--cleanup
|
|
||||||
tableProvider = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
TableService tableService = serviceProvider.getService(TableService.class);
|
|
||||||
if (tableService == null) {
|
|
||||||
Msg.showWarn(getClass(), this, "No Table Service Found",
|
|
||||||
"Unable to locate the Table Service. Make sure the plugin is installed.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionSymbol arbitraryFunction = selectedFunctions.iterator().next();
|
|
||||||
Program program = arbitraryFunction.getProgram();
|
|
||||||
SelectedFunctionsModel model =
|
|
||||||
new SelectedFunctionsModel(program, serviceProvider, selectedFunctions);
|
|
||||||
tableProvider = tableService.showTableWithMarkers("Selected Query Functions",
|
|
||||||
"QueryDialogTable", model, MARKER_COLOR, null /*icon*/, "Selected Query Functions",
|
|
||||||
null /*navigatable - use default*/);
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// Inner Classes
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
private class SelectedFunctionsModel extends AddressBasedTableModel<Function> {
|
|
||||||
private final List<FunctionSymbol> functions;
|
|
||||||
|
|
||||||
SelectedFunctionsModel(Program program, ServiceProvider serviceProvider,
|
|
||||||
Set<FunctionSymbol> functions) {
|
|
||||||
super("Selected Query Functions", serviceProvider, program, null);
|
|
||||||
this.functions = new ArrayList<>(functions);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected TableColumnDescriptor<Function> createTableColumnDescriptor() {
|
|
||||||
TableColumnDescriptor<Function> descriptor = new TableColumnDescriptor<>();
|
|
||||||
|
|
||||||
descriptor.addVisibleColumn(
|
|
||||||
DiscoverableTableUtils.adaptColumForModel(this, new LabelTableColumn()));
|
|
||||||
descriptor.addVisibleColumn(
|
|
||||||
DiscoverableTableUtils.adaptColumForModel(this, new AddressTableColumn()), 1, true);
|
|
||||||
descriptor.addVisibleColumn(DiscoverableTableUtils.adaptColumForModel(this,
|
|
||||||
new FunctionSignatureTableColumn()));
|
|
||||||
descriptor.addVisibleColumn(new MatchCountTableColumn());
|
|
||||||
|
|
||||||
return descriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Address getAddress(int row) {
|
|
||||||
Function function = getRowObject(row);
|
|
||||||
return function.getEntryPoint();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doLoad(Accumulator<Function> accumulator, TaskMonitor monitor)
|
|
||||||
throws CancelledException {
|
|
||||||
for (FunctionSymbol sym : functions) {
|
|
||||||
Object obj = sym.getObject();
|
|
||||||
if (obj != null) {
|
|
||||||
accumulator.add((Function) obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ProgramLocation getProgramLocation(int row, int column) {
|
|
||||||
// TODO: I cannot reconcile how to show the user a table of functions and let them
|
|
||||||
// navigate in a way that is understandable (if they navigate, that changes the selected
|
|
||||||
// functions, which would then clear the table)
|
|
||||||
//
|
|
||||||
// Sad Face :(
|
|
||||||
//
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates how many matches are associated with each base function in
|
|
||||||
* the given result set and stores them in the {@link DisplayFunctionsPanel#functionMatchMap}.
|
|
||||||
*
|
|
||||||
* @param queryResult the object that holds the function matches
|
|
||||||
*/
|
|
||||||
private void parseFunctionMatchCounts(SFQueryResult queryResult) {
|
|
||||||
|
|
||||||
List<SimilarityResult> results = queryResult.getSimilarityResults();
|
|
||||||
|
|
||||||
for (SimilarityResult result : results) {
|
|
||||||
String funcName = result.getBase().getFunctionName();
|
|
||||||
functionMatchMap.put(funcName, result.getTotalCount());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Column for showing the number of matches each base function has.
|
|
||||||
*
|
|
||||||
* To make this as fast as possible, the counts for each function are NOT determined
|
|
||||||
* here; they're calculated when a new result set is received and stored in the
|
|
||||||
* {@link DisplayFunctionsPanel#functionMatchMap}. This class has only to
|
|
||||||
* go to that map and extract the correct value.
|
|
||||||
*
|
|
||||||
* @see DisplayFunctionsPanel#parseFunctionMatchCounts(SFQueryResult)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
private class MatchCountTableColumn
|
|
||||||
extends AbstractProgramBasedDynamicTableColumn<Function, Integer> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getColumnName() {
|
|
||||||
return "Matches";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Integer getValue(Function rowObject, Settings settings, Program program,
|
|
||||||
ServiceProvider serviceProvider) throws IllegalArgumentException {
|
|
||||||
|
|
||||||
if (functionMatchMap == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return functionMatchMap.get(rowObject.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -125,5 +125,6 @@ class VersionInfoPanel extends JPanel {
|
|||||||
private void addJavaInfo(List<String> lines, String def) {
|
private void addJavaInfo(List<String> lines, String def) {
|
||||||
lines.add("Java Vendor: " + System.getProperty("java.vendor", def));
|
lines.add("Java Vendor: " + System.getProperty("java.vendor", def));
|
||||||
lines.add("Java Version: " + System.getProperty("java.version", def));
|
lines.add("Java Version: " + System.getProperty("java.version", def));
|
||||||
|
lines.add("Java Home: " + System.getProperty("java.home"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -21,13 +21,11 @@ import java.util.List;
|
|||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.border.Border;
|
import javax.swing.border.Border;
|
||||||
import javax.swing.event.HyperlinkEvent;
|
|
||||||
import javax.swing.event.HyperlinkListener;
|
|
||||||
|
|
||||||
import docking.DockingWindowManager;
|
import docking.DockingWindowManager;
|
||||||
import docking.ReusableDialogComponentProvider;
|
import docking.ReusableDialogComponentProvider;
|
||||||
import docking.tool.ToolConstants;
|
import docking.tool.ToolConstants;
|
||||||
import docking.widgets.HyperlinkComponent;
|
import docking.widgets.GHyperlinkComponent;
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.combobox.GhidraComboBox;
|
import docking.widgets.combobox.GhidraComboBox;
|
||||||
import ghidra.GhidraOptions;
|
import ghidra.GhidraOptions;
|
||||||
@ -77,7 +75,7 @@ public class GoToAddressLabelDialog extends ReusableDialogComponentProvider
|
|||||||
|
|
||||||
private Navigatable navigatable;
|
private Navigatable navigatable;
|
||||||
|
|
||||||
private HyperlinkComponent hyperlink;
|
private GHyperlinkComponent hyperlink;
|
||||||
|
|
||||||
private JCheckBox includeDynamicBox;
|
private JCheckBox includeDynamicBox;
|
||||||
|
|
||||||
@ -183,23 +181,26 @@ public class GoToAddressLabelDialog extends ReusableDialogComponentProvider
|
|||||||
gbc.weightx = 1;
|
gbc.weightx = 1;
|
||||||
gbc.gridwidth = 2;
|
gbc.gridwidth = 2;
|
||||||
gbc.insets = new Insets(5, 5, 5, 5);
|
gbc.insets = new Insets(5, 5, 5, 5);
|
||||||
hyperlink = new HyperlinkComponent("<html>Enter an address, label, <a href=\"" +
|
|
||||||
EXPRESSION_ANCHOR_NAME + "\">expression</a>, or " + "<a href=\"" +
|
hyperlink = new GHyperlinkComponent();
|
||||||
FILE_OFFSET_ANCHOR_NAME + "\">file offset</a>:");
|
hyperlink.addText("Enter an address, label, ");
|
||||||
HyperlinkListener hyperlinkListener = evt -> {
|
hyperlink.addLink("expression,", () -> {
|
||||||
if (evt.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
|
HelpLocation loc = new HelpLocation(HelpTopics.NAVIGATION, EXPRESSION_ANCHOR_NAME);
|
||||||
HelpLocation loc = new HelpLocation(HelpTopics.NAVIGATION, evt.getDescription());
|
DockingWindowManager.getHelpService().showHelp(loc);
|
||||||
DockingWindowManager.getHelpService().showHelp(loc);
|
});
|
||||||
}
|
|
||||||
};
|
hyperlink.addText(" or ");
|
||||||
hyperlink.addHyperlinkListener(EXPRESSION_ANCHOR_NAME, hyperlinkListener);
|
hyperlink.addLink("file offset:", () -> {
|
||||||
hyperlink.addHyperlinkListener(FILE_OFFSET_ANCHOR_NAME, hyperlinkListener);
|
HelpLocation loc = new HelpLocation(HelpTopics.NAVIGATION, FILE_OFFSET_ANCHOR_NAME);
|
||||||
|
DockingWindowManager.getHelpService().showHelp(loc);
|
||||||
|
});
|
||||||
|
|
||||||
inner.add(hyperlink, gbc);
|
inner.add(hyperlink, gbc);
|
||||||
|
|
||||||
comboBox = new GhidraComboBox<>();
|
comboBox = new GhidraComboBox<>();
|
||||||
comboBox.setEditable(true);
|
comboBox.setEditable(true);
|
||||||
comboBox.addActionListener(evt -> okCallback());
|
comboBox.addActionListener(evt -> okCallback());
|
||||||
String comboName = "Go To Address or Lable Text Field / Combobox";
|
String comboName = "Go To Address or Label Text Field / Combobox";
|
||||||
comboBox.setName(comboName);
|
comboBox.setName(comboName);
|
||||||
comboBox.getAccessibleContext().setAccessibleName(comboName);
|
comboBox.getAccessibleContext().setAccessibleName(comboName);
|
||||||
|
|
||||||
@ -218,7 +219,7 @@ public class GoToAddressLabelDialog extends ReusableDialogComponentProvider
|
|||||||
inner.add(caseSensitiveBox, gbc);
|
inner.add(caseSensitiveBox, gbc);
|
||||||
|
|
||||||
includeDynamicBox = new GCheckBox("Dynamic labels", true);
|
includeDynamicBox = new GCheckBox("Dynamic labels", true);
|
||||||
includeDynamicBox.setToolTipText("Include dynamic lables in the search (slower)");
|
includeDynamicBox.setToolTipText("Include dynamic labels in the search (slower)");
|
||||||
gbc.gridx = 1;
|
gbc.gridx = 1;
|
||||||
inner.add(includeDynamicBox, gbc);
|
inner.add(includeDynamicBox, gbc);
|
||||||
String dynamicCheckBoxName = "Dynamic Checkbox";
|
String dynamicCheckBoxName = "Dynamic Checkbox";
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -16,21 +16,17 @@
|
|||||||
package ghidra.framework.main;
|
package ghidra.framework.main;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.HyperlinkEvent;
|
|
||||||
import javax.swing.text.View;
|
import javax.swing.text.View;
|
||||||
|
|
||||||
import docking.DockingUtils;
|
import docking.widgets.MultiLineLabel;
|
||||||
import docking.widgets.*;
|
|
||||||
import docking.widgets.label.*;
|
import docking.widgets.label.*;
|
||||||
import generic.theme.GColor;
|
import generic.theme.GColor;
|
||||||
import generic.theme.GThemeDefaults.Colors.Palette;
|
import generic.theme.GThemeDefaults.Colors.Palette;
|
||||||
import generic.theme.Gui;
|
import generic.theme.Gui;
|
||||||
import generic.util.WindowUtilities;
|
|
||||||
import ghidra.framework.Application;
|
import ghidra.framework.Application;
|
||||||
import ghidra.framework.ApplicationProperties;
|
import ghidra.framework.ApplicationProperties;
|
||||||
import ghidra.util.*;
|
import ghidra.util.*;
|
||||||
@ -140,7 +136,6 @@ class InfoPanel extends JPanel {
|
|||||||
JPanel vPanel = new JPanel(new BorderLayout());
|
JPanel vPanel = new JPanel(new BorderLayout());
|
||||||
vPanel.setBackground(bgColor);
|
vPanel.setBackground(bgColor);
|
||||||
vPanel.add(buildVersionLabel(), BorderLayout.CENTER);
|
vPanel.add(buildVersionLabel(), BorderLayout.CENTER);
|
||||||
vPanel.add(buildJavaVersionComponent(), BorderLayout.SOUTH);
|
|
||||||
return vPanel;
|
return vPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,41 +187,6 @@ class InfoPanel extends JPanel {
|
|||||||
return imagePanel;
|
return imagePanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
private HyperlinkComponent buildJavaVersionComponent() {
|
|
||||||
String anchorName = "java_version";
|
|
||||||
final HyperlinkComponent javaVersionComponent =
|
|
||||||
new HyperlinkComponent("<html><CENTER>Java Version " + "<A HREF=\"" + anchorName +
|
|
||||||
"\">" + System.getProperty("java.version") + "</A></CENTER>");
|
|
||||||
javaVersionComponent.addHyperlinkListener(anchorName, e -> {
|
|
||||||
if (e.getEventType() != HyperlinkEvent.EventType.ACTIVATED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
showJavaHomeInfo(javaVersionComponent);
|
|
||||||
});
|
|
||||||
|
|
||||||
DockingUtils.setTransparent(javaVersionComponent);
|
|
||||||
return javaVersionComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showJavaHomeInfo(final HyperlinkComponent javaVersionComponent) {
|
|
||||||
JToolTip tooltip = new JToolTip();
|
|
||||||
tooltip.setTipText(System.getProperty("java.home"));
|
|
||||||
|
|
||||||
Point location = MouseInfo.getPointerInfo().getLocation();
|
|
||||||
Window window = WindowUtilities.windowForComponent(javaVersionComponent);
|
|
||||||
tooltip.setLocation(location);
|
|
||||||
|
|
||||||
PopupWindow popupWindow = new PopupWindow(window, tooltip);
|
|
||||||
|
|
||||||
SwingUtilities.convertPointFromScreen(location, javaVersionComponent);
|
|
||||||
MouseEvent dummyEvent =
|
|
||||||
new MouseEvent(javaVersionComponent, (int) System.currentTimeMillis(),
|
|
||||||
System.currentTimeMillis(), 0, location.x, location.y, 1, false);
|
|
||||||
popupWindow.setCloseWindowDelay(1);
|
|
||||||
popupWindow.showPopup(dummyEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read the version information from the the resource file.
|
* Read the version information from the the resource file.
|
||||||
*/
|
*/
|
||||||
@ -241,7 +201,8 @@ class InfoPanel extends JPanel {
|
|||||||
// set some default values in case we don't have the resource file.
|
// set some default values in case we don't have the resource file.
|
||||||
version = "Version " + Application.getApplicationVersion() +
|
version = "Version " + Application.getApplicationVersion() +
|
||||||
(SystemUtilities.isInDevelopmentMode() ? " - DEVELOPMENT" : "") + buildInfo + "\n" +
|
(SystemUtilities.isInDevelopmentMode() ? " - DEVELOPMENT" : "") + buildInfo + "\n" +
|
||||||
Application.getBuildDate();
|
Application.getBuildDate() + "\n" +
|
||||||
|
"Java Version " + System.getProperty("java.version");
|
||||||
|
|
||||||
marking =
|
marking =
|
||||||
Application.getApplicationProperty(ApplicationProperties.RELEASE_MARKING_PROPERTY);
|
Application.getApplicationProperty(ApplicationProperties.RELEASE_MARKING_PROPERTY);
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -46,6 +46,7 @@ import ghidra.util.filechooser.GhidraFileFilter;
|
|||||||
import ghidra.util.xml.GenericXMLOutputter;
|
import ghidra.util.xml.GenericXMLOutputter;
|
||||||
import ghidra.util.xml.XmlUtilities;
|
import ghidra.util.xml.XmlUtilities;
|
||||||
import util.CollectionUtils;
|
import util.CollectionUtils;
|
||||||
|
import utilities.util.reflection.ReflectionUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class to provide utilities for system key bindings, such as importing and
|
* A class to provide utilities for system key bindings, such as importing and
|
||||||
@ -340,6 +341,12 @@ public class KeyBindingUtils {
|
|||||||
if (keyText == null) {
|
if (keyText == null) {
|
||||||
// no binding--just pick a name
|
// no binding--just pick a name
|
||||||
keyText = action.getValue(Action.NAME);
|
keyText = action.getValue(Action.NAME);
|
||||||
|
if (keyText == null) {
|
||||||
|
Msg.error(KeyBindingUtils.class, "Action must have a name to be registered",
|
||||||
|
ReflectionUtilities.createJavaFilteredThrowable());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
im.put(keyStroke, keyText);
|
im.put(keyStroke, keyText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,224 @@
|
|||||||
|
/* ###
|
||||||
|
* IP: GHIDRA
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package docking.widgets;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.*;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.Border;
|
||||||
|
import javax.swing.event.HyperlinkEvent;
|
||||||
|
import javax.swing.event.HyperlinkEvent.EventType;
|
||||||
|
import javax.swing.event.HyperlinkListener;
|
||||||
|
import javax.swing.text.DefaultCaret;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
import docking.DockingUtils;
|
||||||
|
import docking.actions.KeyBindingUtils;
|
||||||
|
import generic.theme.GColor;
|
||||||
|
import ghidra.docking.util.LookAndFeelUtils;
|
||||||
|
import utility.function.Callback;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A component that acts like a label, but adds the ability to render HTML links with a client
|
||||||
|
* callback for when the link is activated. Links can be activated by mouse clicking or or by
|
||||||
|
* focusing the link and then pressing Enter or Space.
|
||||||
|
* <p>
|
||||||
|
* Users can make one simple text link by calling {@link #addLink(String, Callback)}.
|
||||||
|
* Alternatively, users can mix plain text and links by using both {@link #addText(String)} and
|
||||||
|
* {@link #addLink(String, Callback)}.
|
||||||
|
*/
|
||||||
|
public class GHyperlinkComponent extends JPanel {
|
||||||
|
|
||||||
|
public GHyperlinkComponent() {
|
||||||
|
setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds text to this widget that will be displayed as plain text.
|
||||||
|
* @param text the text
|
||||||
|
*/
|
||||||
|
public void addText(String text) {
|
||||||
|
JTextPane textPane = new FixedSizeTextPane(text, Callback.dummy());
|
||||||
|
textPane.setFocusable(false);
|
||||||
|
add(textPane);
|
||||||
|
String updated = text;
|
||||||
|
|
||||||
|
// the text pane will trim leading spaces; keep the client spaces
|
||||||
|
int leadingSpaces = text.indexOf(text.trim());
|
||||||
|
if (leadingSpaces != 0) {
|
||||||
|
updated = StringUtils.repeat(" ", leadingSpaces) + text.substring(leadingSpaces);
|
||||||
|
}
|
||||||
|
|
||||||
|
setText(textPane, updated);
|
||||||
|
|
||||||
|
// clear the description to avoid excess text reading
|
||||||
|
textPane.getAccessibleContext().setAccessibleDescription("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the given text to create a link the user can click.
|
||||||
|
* @param text the text
|
||||||
|
* @param linkActivatedCallback the callback that will be called when the link is activated
|
||||||
|
*/
|
||||||
|
public void addLink(String text, Callback linkActivatedCallback) {
|
||||||
|
JTextPane textPane = new FixedSizeTextPane(text, linkActivatedCallback);
|
||||||
|
add(textPane);
|
||||||
|
setText(textPane, "<a href=\"stub\">" + text + "</a>");
|
||||||
|
textPane.getAccessibleContext().setAccessibleDescription("Clickable link");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setText(JTextPane textPane, String text) {
|
||||||
|
|
||||||
|
String html = "<html><nobr>" + text;
|
||||||
|
textPane.setText(html);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Hack Alert!: We've run into scenarios where changing the text of this component
|
||||||
|
// causes the entire component to paint no text. For some reason, the
|
||||||
|
// component will work correctly if it has a non-zero size border installed.
|
||||||
|
// Also, if we call getPreferredSize(), then it will work.
|
||||||
|
//
|
||||||
|
textPane.getPreferredSize();
|
||||||
|
getPreferredSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==================================================================================================
|
||||||
|
// Inner Classes
|
||||||
|
//==================================================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A text pane that can render links.
|
||||||
|
*/
|
||||||
|
private class FixedSizeTextPane extends JTextPane {
|
||||||
|
|
||||||
|
private String rawText;
|
||||||
|
|
||||||
|
FixedSizeTextPane(String rawText, Callback linkActivatedCallback) {
|
||||||
|
this.rawText = rawText;
|
||||||
|
|
||||||
|
setContentType("text/html");
|
||||||
|
setEditable(false);
|
||||||
|
setCaret(new NonScrollingCaret());
|
||||||
|
DockingUtils.setTransparent(this);
|
||||||
|
|
||||||
|
// Create a border the same dimensions as the one we will later switch to when focused.
|
||||||
|
// This keeps the UI from moving.
|
||||||
|
Border defaultBorder = BorderFactory.createEmptyBorder(1, 1, 1, 1);
|
||||||
|
setBorder(defaultBorder);
|
||||||
|
|
||||||
|
// change the border of the link so users can see when it is focused
|
||||||
|
Color FOCUS_COLOR = new GColor("color.border.button.focused");
|
||||||
|
Border FOCUSED_BORDER = BorderFactory.createLineBorder(FOCUS_COLOR);
|
||||||
|
addFocusListener(new FocusListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost(FocusEvent e) {
|
||||||
|
setBorder(defaultBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusGained(FocusEvent e) {
|
||||||
|
setBorder(FOCUSED_BORDER);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addHyperlinkListener(new HyperlinkListener() {
|
||||||
|
@Override
|
||||||
|
public void hyperlinkUpdate(HyperlinkEvent e) {
|
||||||
|
EventType type = e.getEventType();
|
||||||
|
if (type == EventType.ACTIVATED) {
|
||||||
|
linkActivatedCallback.call();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
addActivationKeyBinding(linkActivatedCallback);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addActivationKeyBinding(Callback linkActivatedCallback) {
|
||||||
|
// Space and Enter typically activate buttons
|
||||||
|
KeyStroke enterKs = KeyBindingUtils.parseKeyStroke("Enter");
|
||||||
|
KeyStroke spaceKs = KeyBindingUtils.parseKeyStroke("Space");
|
||||||
|
Action action = new AbstractAction("Activate Link") {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
linkActivatedCallback.call();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
KeyBindingUtils.registerAction(this, enterKs, action, JComponent.WHEN_FOCUSED);
|
||||||
|
KeyBindingUtils.registerAction(this, spaceKs, action, JComponent.WHEN_FOCUSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMaximumSize() {
|
||||||
|
return getBestSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getMinimumSize() {
|
||||||
|
return getBestSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize() {
|
||||||
|
return getBestSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dimension getBestSize() {
|
||||||
|
int width = getBestWidth();
|
||||||
|
Dimension d = super.getPreferredSize();
|
||||||
|
d.width = Math.min(width, d.width);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getBestWidth() {
|
||||||
|
Font font = getFont();
|
||||||
|
FontMetrics fm = getFontMetrics(font);
|
||||||
|
int stringWidth = fm.stringWidth(rawText);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Make a width that is at least as big as the width of the text. Use the preferred
|
||||||
|
// width, as that is more accurate. Do not do this for the FlatLaf UIs because their
|
||||||
|
// preferred width includes a minimum width which will be too large for a small number
|
||||||
|
// of characters.
|
||||||
|
//
|
||||||
|
Dimension preferred = super.getPreferredSize();
|
||||||
|
int width = stringWidth;
|
||||||
|
if (!LookAndFeelUtils.isUsingFlatUI()) {
|
||||||
|
width = Math.max(stringWidth, preferred.width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a fudge factor to compensate for text calculation rounding, based on a 12pt font size
|
||||||
|
Insets insets = getInsets();
|
||||||
|
width += insets.left + insets.right;
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class NonScrollingCaret extends DefaultCaret {
|
||||||
|
|
||||||
|
private NonScrollingCaret() {
|
||||||
|
setVisible(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void adjustVisibility(Rectangle nloc) {
|
||||||
|
// we don't want to adjust any visibility (no scrolling for this comp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -15,18 +15,20 @@
|
|||||||
*/
|
*/
|
||||||
package docking.widgets;
|
package docking.widgets;
|
||||||
|
|
||||||
import java.awt.BorderLayout;
|
import java.awt.*;
|
||||||
import java.awt.Rectangle;
|
import java.awt.event.*;
|
||||||
import java.awt.event.MouseListener;
|
import java.util.ArrayList;
|
||||||
import java.awt.event.MouseMotionListener;
|
import java.util.HashMap;
|
||||||
import java.util.*;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.border.Border;
|
||||||
import javax.swing.event.HyperlinkEvent;
|
import javax.swing.event.HyperlinkEvent;
|
||||||
import javax.swing.event.HyperlinkListener;
|
import javax.swing.event.HyperlinkListener;
|
||||||
import javax.swing.text.DefaultCaret;
|
import javax.swing.text.DefaultCaret;
|
||||||
|
|
||||||
import docking.DockingUtils;
|
import docking.DockingUtils;
|
||||||
|
import generic.theme.GColor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A component that acts like a label, but adds the ability to render HTML anchors and the
|
* A component that acts like a label, but adds the ability to render HTML anchors and the
|
||||||
@ -37,7 +39,9 @@ import docking.DockingUtils;
|
|||||||
* this component will display the hyperlinks properly and will notify any registered
|
* this component will display the hyperlinks properly and will notify any registered
|
||||||
* listeners ({@link #addHyperlinkListener(String, HyperlinkListener)} that the user has clicked the link
|
* listeners ({@link #addHyperlinkListener(String, HyperlinkListener)} that the user has clicked the link
|
||||||
* by the given name.
|
* by the given name.
|
||||||
|
* @deprecated Replaced by {@link GHyperlinkComponent}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.3", forRemoval = true)
|
||||||
public class HyperlinkComponent extends JPanel {
|
public class HyperlinkComponent extends JPanel {
|
||||||
|
|
||||||
private JTextPane textPane;
|
private JTextPane textPane;
|
||||||
@ -55,6 +59,7 @@ public class HyperlinkComponent extends JPanel {
|
|||||||
textPane.setCaret(new NonScrollingCaret());
|
textPane.setCaret(new NonScrollingCaret());
|
||||||
|
|
||||||
textPane.addHyperlinkListener(new HyperlinkListener() {
|
textPane.addHyperlinkListener(new HyperlinkListener() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hyperlinkUpdate(HyperlinkEvent e) {
|
public void hyperlinkUpdate(HyperlinkEvent e) {
|
||||||
String anchorText = e.getDescription();
|
String anchorText = e.getDescription();
|
||||||
@ -84,6 +89,22 @@ public class HyperlinkComponent extends JPanel {
|
|||||||
textPane.setPreferredSize(getPreferredSize());
|
textPane.setPreferredSize(getPreferredSize());
|
||||||
|
|
||||||
hyperlinkListeners = new HashMap<String, List<HyperlinkListener>>();
|
hyperlinkListeners = new HashMap<String, List<HyperlinkListener>>();
|
||||||
|
|
||||||
|
Color FOCUS_COLOR = new GColor("color.border.button.focused");
|
||||||
|
Border FOCUSED_BORDER = BorderFactory.createLineBorder(FOCUS_COLOR);
|
||||||
|
Border defaultBorder = textPane.getBorder();
|
||||||
|
textPane.addFocusListener(new FocusListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusLost(FocusEvent e) {
|
||||||
|
textPane.setBorder(defaultBorder);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void focusGained(FocusEvent e) {
|
||||||
|
textPane.setBorder(FOCUSED_BORDER);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,6 +180,11 @@ public class HyperlinkComponent extends JPanel {
|
|||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setVisible(boolean e) {
|
||||||
|
super.setVisible(e);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void adjustVisibility(Rectangle nloc) {
|
protected void adjustVisibility(Rectangle nloc) {
|
||||||
// we don't want to adjust any visibility (no scrolling for this comp)
|
// we don't want to adjust any visibility (no scrolling for this comp)
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -66,7 +66,7 @@ public class LookAndFeelUtils {
|
|||||||
* @return true if the UI is using Aqua
|
* @return true if the UI is using Aqua
|
||||||
*/
|
*/
|
||||||
public static boolean isUsingAquaUI(ComponentUI UI) {
|
public static boolean isUsingAquaUI(ComponentUI UI) {
|
||||||
return ThemeManager.getInstance().isUsingAquaUI(UI);
|
return ThemeManager.getInstance().isUsingAquaUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,4 +77,11 @@ public class LookAndFeelUtils {
|
|||||||
return ThemeManager.getInstance().isUsingNimbusUI();
|
return ThemeManager.getInstance().isUsingNimbusUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the current UI is the FlatLaf Dark or FlatLaf Light Look and Feel.
|
||||||
|
* @return true if the current UI is the FlatLaf Dark or FlatLaf Light Look and Feel
|
||||||
|
*/
|
||||||
|
public static boolean isUsingFlatUI() {
|
||||||
|
return ThemeManager.getInstance().isUsingFlatUI();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -490,13 +490,32 @@ public abstract class ThemeManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the given UI object is using the Aqua Look and Feel.
|
* Returns true if the given UI object is using the Aqua Look and Feel.
|
||||||
* @param UI the UI to examine.
|
* @param UI the UI to examine. (This parameter is ignored)
|
||||||
* @return true if the UI is using Aqua
|
* @return true if the UI is using Aqua
|
||||||
|
* @deprecated use {@link #isUsingAquaUI()}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated(since = "11.3", forRemoval = true)
|
||||||
public boolean isUsingAquaUI(ComponentUI UI) {
|
public boolean isUsingAquaUI(ComponentUI UI) {
|
||||||
return getLookAndFeelType() == LafType.MAC;
|
return getLookAndFeelType() == LafType.MAC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the current UI is using the Aqua Look and Feel.
|
||||||
|
* @return true if the UI is using Aqua
|
||||||
|
*/
|
||||||
|
public boolean isUsingAquaUI() {
|
||||||
|
return getLookAndFeelType() == LafType.MAC;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the current UI is the FlatLaf Dark or FlatLaf Light Look and Feel.
|
||||||
|
* @return true if the current UI is the FlatLaf Dark or FlatLaf Light Look and Feel
|
||||||
|
*/
|
||||||
|
public boolean isUsingFlatUI() {
|
||||||
|
return getLookAndFeelType() == LafType.FLAT_DARK ||
|
||||||
|
getLookAndFeelType() == LafType.FLAT_LIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if 'Nimbus' is the current Look and Feel
|
* Returns true if 'Nimbus' is the current Look and Feel
|
||||||
* @return true if 'Nimbus' is the current Look and Feel
|
* @return true if 'Nimbus' is the current Look and Feel
|
||||||
@ -618,7 +637,7 @@ public abstract class ThemeManager {
|
|||||||
OperatingSystem OS = Platform.CURRENT_PLATFORM.getOperatingSystem();
|
OperatingSystem OS = Platform.CURRENT_PLATFORM.getOperatingSystem();
|
||||||
return switch (OS) {
|
return switch (OS) {
|
||||||
case MAC_OS_X -> new MacTheme();
|
case MAC_OS_X -> new MacTheme();
|
||||||
case WINDOWS -> new WindowsTheme();
|
case WINDOWS -> new WindowsTheme();
|
||||||
default -> new FlatLightTheme();
|
default -> new FlatLightTheme();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
*
|
*
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
*
|
*
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -20,10 +20,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.HyperlinkEvent.EventType;
|
|
||||||
|
|
||||||
import docking.EmptyBorderToggleButton;
|
import docking.EmptyBorderToggleButton;
|
||||||
import docking.widgets.HyperlinkComponent;
|
import docking.widgets.GHyperlinkComponent;
|
||||||
import docking.widgets.checkbox.GCheckBox;
|
import docking.widgets.checkbox.GCheckBox;
|
||||||
import docking.widgets.label.*;
|
import docking.widgets.label.*;
|
||||||
import generic.theme.*;
|
import generic.theme.*;
|
||||||
@ -180,7 +179,7 @@ public class PluginManagerComponent extends JPanel implements Scrollable {
|
|||||||
nameLabel.setForeground(new GColor("color.fg.pluginpanel.name"));
|
nameLabel.setForeground(new GColor("color.fg.pluginpanel.name"));
|
||||||
labelPanel.add(nameLabel);
|
labelPanel.add(nameLabel);
|
||||||
|
|
||||||
HyperlinkComponent configureHyperlink = createConfigureHyperlink();
|
GHyperlinkComponent configureHyperlink = createConfigureHyperlink();
|
||||||
labelPanel.add(configureHyperlink);
|
labelPanel.add(configureHyperlink);
|
||||||
|
|
||||||
labelPanel.setBorder(BorderFactory.createEmptyBorder(0, 25, 0, 40));
|
labelPanel.setBorder(BorderFactory.createEmptyBorder(0, 25, 0, 40));
|
||||||
@ -188,14 +187,13 @@ public class PluginManagerComponent extends JPanel implements Scrollable {
|
|||||||
add(centerPanel);
|
add(centerPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private HyperlinkComponent createConfigureHyperlink() {
|
private GHyperlinkComponent createConfigureHyperlink() {
|
||||||
HyperlinkComponent configureHyperlink =
|
GHyperlinkComponent configureHyperlink =
|
||||||
new HyperlinkComponent("<html> <a href=\"Configure\">Configure</a>");
|
new GHyperlinkComponent();
|
||||||
configureHyperlink.addHyperlinkListener("Configure", e -> {
|
configureHyperlink.addLink("Configure", () -> {
|
||||||
if (e.getEventType() == EventType.ACTIVATED) {
|
managePlugins(PluginPackageComponent.this.pluginPackage);
|
||||||
managePlugins(PluginPackageComponent.this.pluginPackage);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
configureHyperlink.setBackground(BG);
|
configureHyperlink.setBackground(BG);
|
||||||
return configureHyperlink;
|
return configureHyperlink;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user