mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-06-02 03:27:33 +08:00
GP-1634 Fix GhidraFileChooser dialog redraw lag spikes on Windows
When resizing the dialog, calls to the OS's geFileIcon take long enough to cause lag spikes and redraw issues (depending on the number of files in the directory). This change caches the icons.
This commit is contained in:
+27
-8
@@ -15,11 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package docking.widgets.filechooser;
|
package docking.widgets.filechooser;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileFilter;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
|
||||||
import javax.swing.Icon;
|
import javax.swing.Icon;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.filechooser.FileSystemView;
|
import javax.swing.filechooser.FileSystemView;
|
||||||
@@ -40,6 +41,16 @@ public class LocalFileChooserModel implements GhidraFileChooserModel {
|
|||||||
private FileSystemView fsView = FileSystemView.getFileSystemView();
|
private FileSystemView fsView = FileSystemView.getFileSystemView();
|
||||||
private Map<File, String> rootDescripMap = new HashMap<>();
|
private Map<File, String> rootDescripMap = new HashMap<>();
|
||||||
private Map<File, Icon> rootIconMap = new HashMap<>();
|
private Map<File, Icon> rootIconMap = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a cache of file icons, as returned from the OS's file icon service.
|
||||||
|
* <p>
|
||||||
|
* This cache is cleared each time a directory is requested (via {@link #getListing(File, FileFilter)}
|
||||||
|
* so that any changes to a file's icon are visible the next time the user hits
|
||||||
|
* refresh or navigates into a directory.
|
||||||
|
*/
|
||||||
|
private Map<File, Icon> fileIconMap = new HashMap<>();
|
||||||
|
|
||||||
private File[] roots = new File[0];
|
private File[] roots = new File[0];
|
||||||
private GhidraFileChooserListener listener;
|
private GhidraFileChooserListener listener;
|
||||||
|
|
||||||
@@ -176,6 +187,11 @@ public class LocalFileChooserModel implements GhidraFileChooserModel {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public File[] getListing(File directory, final FileFilter filter) {
|
public File[] getListing(File directory, final FileFilter filter) {
|
||||||
|
// This clears the previously cached icons and avoids issues with modifying the map
|
||||||
|
// while its being used by other methods by throwing away the instance and allocating
|
||||||
|
// a new one.
|
||||||
|
fileIconMap = new HashMap<>();
|
||||||
|
|
||||||
if (directory == null) {
|
if (directory == null) {
|
||||||
return new File[0];
|
return new File[0];
|
||||||
}
|
}
|
||||||
@@ -188,18 +204,21 @@ public class LocalFileChooserModel implements GhidraFileChooserModel {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Icon getIcon(File file) {
|
public Icon getIcon(File file) {
|
||||||
if (rootIconMap.containsKey(file)) {
|
Icon result = rootIconMap.get(file);
|
||||||
return rootIconMap.get(file);
|
if (result == null && file != null && file.exists()) {
|
||||||
|
result = fileIconMap.computeIfAbsent(file, this::getSystemIcon);
|
||||||
}
|
}
|
||||||
if (file != null && file.exists()) {
|
return (result != null) ? result : PROBLEM_FILE_ICON;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Icon getSystemIcon(File file) {
|
||||||
try {
|
try {
|
||||||
return fsView.getSystemIcon(file);
|
return fsView.getSystemIcon(file);
|
||||||
}
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
// ignore, fall thru
|
// ignore, return null
|
||||||
}
|
}
|
||||||
}
|
return null;
|
||||||
return PROBLEM_FILE_ICON;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user