fix: handle null bounds in WindowLocation to prevent NPE on dialog dispose (PR #2826)
* fix: handle null bounds in WindowLocation to prevent NPE on dialog dispose The equals(), hashCode(), and toString() methods in WindowLocation could throw NullPointerException when bounds is null. This happens when a dialog window is disposed before its bounds are fully initialized (e.g., on macOS when closing the search dialog). Use Objects.equals()/Objects.hashCode() for null-safe comparisons and add a null guard in toString(). Fixes #2571 * additional null checks and null annotations --------- Co-authored-by: easonysliu <easonysliu@tencent.com> Co-authored-by: Skylot <118523+skylot@users.noreply.github.com>
This commit is contained in:
@@ -220,38 +220,49 @@ public class JadxSettings {
|
||||
recentProjects.remove(projectPath);
|
||||
}
|
||||
|
||||
private static String makeWindowId(Window window) {
|
||||
return window.getClass().getSimpleName();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantValue")
|
||||
public void saveWindowPos(Window window) {
|
||||
if (window == null) {
|
||||
return;
|
||||
}
|
||||
synchronized (dataWriteSync) {
|
||||
WindowLocation pos = new WindowLocation(window.getClass().getSimpleName(), window.getBounds());
|
||||
settingsData.getWindowPos().put(pos.getWindowId(), pos);
|
||||
Rectangle bounds = window.getBounds();
|
||||
if (bounds != null) {
|
||||
WindowLocation pos = new WindowLocation(makeWindowId(window), bounds);
|
||||
settingsData.getWindowPos().put(pos.getWindowId(), pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean loadWindowPos(Window window) {
|
||||
Map<String, WindowLocation> windowPos = settingsData.getWindowPos();
|
||||
WindowLocation pos = windowPos.get(window.getClass().getSimpleName());
|
||||
if (pos == null || pos.getBounds() == null) {
|
||||
String windowId = makeWindowId(window);
|
||||
WindowLocation pos = settingsData.getWindowPos().get(windowId);
|
||||
if (pos == null) {
|
||||
return false;
|
||||
}
|
||||
if (!isAccessibleInAnyScreen(pos)) {
|
||||
Rectangle bounds = pos.getBounds();
|
||||
if (bounds == null || !isAccessibleInAnyScreen(windowId, bounds)) {
|
||||
return false;
|
||||
}
|
||||
window.setBounds(pos.getBounds());
|
||||
window.setBounds(bounds);
|
||||
if (window instanceof MainWindow) {
|
||||
((JFrame) window).setExtendedState(getMainWindowExtendedState());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isAccessibleInAnyScreen(WindowLocation pos) {
|
||||
Rectangle windowBounds = pos.getBounds();
|
||||
private static boolean isAccessibleInAnyScreen(String windowId, Rectangle windowBounds) {
|
||||
for (GraphicsDevice gd : GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()) {
|
||||
Rectangle screenBounds = gd.getDefaultConfiguration().getBounds();
|
||||
if (screenBounds.intersects(windowBounds)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
LOG.debug("Window saved position was ignored: {}", pos);
|
||||
LOG.debug("Window saved position was ignored: {}, bounds: {}", windowId, windowBounds);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
package jadx.gui.settings;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class WindowLocation {
|
||||
private String windowId;
|
||||
private Rectangle bounds;
|
||||
private @Nullable Rectangle bounds;
|
||||
|
||||
// Don't remove. Used in json serialization
|
||||
// Don't remove. Used in JSON serialization
|
||||
public WindowLocation() {
|
||||
}
|
||||
|
||||
public WindowLocation(String windowId, Rectangle bounds) {
|
||||
public WindowLocation(String windowId, @Nullable Rectangle bounds) {
|
||||
this.windowId = windowId;
|
||||
this.bounds = bounds;
|
||||
}
|
||||
@@ -23,37 +27,31 @@ public class WindowLocation {
|
||||
this.windowId = windowId;
|
||||
}
|
||||
|
||||
public Rectangle getBounds() {
|
||||
public @Nullable Rectangle getBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public void setBounds(Rectangle bounds) {
|
||||
public void setBounds(@Nullable Rectangle bounds) {
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return windowId.hashCode();
|
||||
return Objects.hashCode(windowId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean equals(Object o) {
|
||||
if (o instanceof WindowLocation) {
|
||||
WindowLocation that = (WindowLocation) o;
|
||||
return windowId.equals(that.windowId) && bounds.equals(that.bounds);
|
||||
return Objects.equals(windowId, that.windowId)
|
||||
&& Objects.equals(bounds, that.bounds);
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WindowLocation{"
|
||||
+ "id='" + windowId + '\''
|
||||
+ ", x=" + bounds.getX()
|
||||
+ ", y=" + bounds.getY()
|
||||
+ ", width=" + bounds.getWidth()
|
||||
+ ", height=" + bounds.getHeight()
|
||||
+ '}';
|
||||
return "WindowLocation{id=" + windowId + ", bounds=" + bounds + '}';
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user