From 131228a250c36991d43eb019a5688c0d27f02cef Mon Sep 17 00:00:00 2001 From: dev747368 <48332326+dev747368@users.noreply.github.com> Date: Wed, 8 May 2019 16:42:30 -0400 Subject: [PATCH] GT-2855 - fix list cell renderering sizing --- .../widgets/filechooser/DirectoryList.java | 67 ++++++++++--------- .../filechooser/DirectoryListModel.java | 4 ++ .../filechooser/FileListCellRenderer.java | 5 +- .../filechooser/GhidraFileChooser.java | 54 ++------------- .../widgets/list/GListCellRenderer.java | 28 ++++++++ 5 files changed, 77 insertions(+), 81 deletions(-) diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/DirectoryList.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/DirectoryList.java index 3230d625d7..c31518fc18 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/DirectoryList.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/DirectoryList.java @@ -32,9 +32,13 @@ import docking.widgets.list.GList; import ghidra.util.exception.AssertException; class DirectoryList extends GList implements GhidraFileChooserDirectoryModelIf { + private static final int DEFAULT_ICON_SIZE = 16; + private static final int WIDTH_PADDING = 14; + private static final int HEIGHT_PADDING = 5; private GhidraFileChooser chooser; private DirectoryListModel model; + private FileListCellRenderer cellRenderer; private JLabel listEditorLabel; private JTextField listEditorField; private JPanel listEditor; @@ -52,7 +56,7 @@ class DirectoryList extends GList implements GhidraFileChooserDirectoryMod private void build() { setLayoutOrientation(JList.VERTICAL_WRAP); - setCellRenderer(new FileListCellRenderer(chooser)); + setCellRenderer((cellRenderer = new FileListCellRenderer(getFont(), chooser))); addMouseListener(new MouseAdapter() { @Override @@ -281,36 +285,6 @@ class DirectoryList extends GList implements GhidraFileChooserDirectoryMod setSelectedIndices(indices); } - // overridden to account for the renderers insets, to avoid clipping - @Override - public void setFixedCellWidth(int width) { - - int fullWidth = width; - ListCellRenderer renderer = getCellRenderer(); - if (renderer instanceof JComponent) { - JComponent c = (JComponent) renderer; - Insets insets = c.getInsets(); - fullWidth += insets.left + insets.right; - } - - super.setFixedCellWidth(fullWidth); - } - - // overridden to account for the renderers insets, to avoid clipping - @Override - public void setFixedCellHeight(int height) { - - int fullHeight = height; - ListCellRenderer renderer = getCellRenderer(); - if (renderer instanceof JComponent) { - JComponent c = (JComponent) renderer; - Insets insets = c.getInsets(); - fullHeight += insets.top + insets.bottom; - } - - super.setFixedCellHeight(fullHeight); - } - private boolean isEditing() { return (editedFile != null); } @@ -367,6 +341,37 @@ class DirectoryList extends GList implements GhidraFileChooserDirectoryMod } } + /** + * Resizes this list's cell dimensions based on the string widths found in the supplied + * list of files. + *

+ * If there there are no files, uses the JScrollPane that contains us for the cellwidth. + * + * @param files list of files to use to resize the list's fixed cell dimensions. If null, uses + * the model's current set of files. + */ + void recomputeListCellDimensions(List files) { + files = (files != null) ? files : model.getAllFiles(); + Dimension cellDims = + cellRenderer.computePlainTextListCellDimensions(this, files, 0, DEFAULT_ICON_SIZE); + if (cellDims.width == 0 && getParent() != null) { + // special case: if there were no files to measure, use the containing JScrollPane's + // width + if (getParent().getParent() instanceof JScrollPane) { + JScrollPane parent = (JScrollPane) getParent().getParent(); + Dimension parentSize = parent.getSize(); + Insets insets = parent.getInsets(); + cellDims.width = + parentSize.width - (insets != null ? insets.right + insets.left : 0); + } + } + else { + cellDims.width += DEFAULT_ICON_SIZE + WIDTH_PADDING; + } + setFixedCellWidth(cellDims.width); + setFixedCellHeight(cellDims.height + HEIGHT_PADDING); + } + /*junit*/ JTextField getListEditorText() { return listEditorField; } diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/DirectoryListModel.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/DirectoryListModel.java index 54547ca800..4f2a4907eb 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/DirectoryListModel.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/DirectoryListModel.java @@ -74,6 +74,10 @@ class DirectoryListModel extends AbstractListModel { return fileList.indexOf(file); } + public List getAllFiles() { + return new ArrayList<>(fileList); + } + @Override public int getSize() { return fileList.size(); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/FileListCellRenderer.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/FileListCellRenderer.java index 714cec4134..d6a7eb0bae 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/FileListCellRenderer.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/FileListCellRenderer.java @@ -16,6 +16,7 @@ package docking.widgets.filechooser; import java.awt.Component; +import java.awt.Font; import java.io.File; import javax.swing.JList; @@ -28,9 +29,11 @@ class FileListCellRenderer extends GListCellRenderer { private GhidraFileChooser chooser; private GhidraFileChooserModel model; - public FileListCellRenderer(GhidraFileChooser chooser) { + public FileListCellRenderer(Font font, GhidraFileChooser chooser) { + super(font); this.chooser = chooser; this.model = chooser.getModel(); + setShouldAlternateRowBackgroundColors(false); } @Override diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/GhidraFileChooser.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/GhidraFileChooser.java index c6e24750a2..73655abf08 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/GhidraFileChooser.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/filechooser/GhidraFileChooser.java @@ -88,9 +88,6 @@ public class GhidraFileChooser extends DialogComponentProvider static final String NEW_FOLDER = "New Folder"; private static final int PAD = 5; - private static final int DEFAULT_ICON_SIZE = 16; - private static final int HEIGHT_PADDING = 5; - private static final int WIDTH_PADDING = 14; private static Icon refreshIcon = Icons.REFRESH_ICON; private static Icon backIcon = ResourceManager.loadImage("images/left.png"); @@ -555,18 +552,11 @@ public class GhidraFileChooser extends DialogComponentProvider private JScrollPane buildDirectoryList() { directoryListModel = new DirectoryListModel(); - // SCR 3392 - 12/7/07 - // initially added to resize the cells of the list when the new files are - // inserted into the list (like when creating a single new folder) directoryListModel.addListDataListener(new ListDataListener() { @Override public void contentsChanged(ListDataEvent e) { - int size = directoryListModel.getSize(); - List fileList = new ArrayList<>(size); - for (int i = 0; i < size; i++) { - fileList.add(directoryListModel.getFile(i)); - } - computeListCellDimensions(fileList); + // called when the list changes because a new file is inserted (ie. create new folder action) + directoryList.recomputeListCellDimensions(null); } @Override @@ -850,42 +840,6 @@ public class GhidraFileChooser extends DialogComponentProvider return worker.isBusy(); } - private void computeListCellDimensions(List files) { - - Font font = directoryList.getFont(); - FontMetrics metrics = directoryList.getFontMetrics(font); - - int maxWidth = getDefaultMaxWidth(files, metrics); - - directoryList.setFixedCellWidth(maxWidth + DEFAULT_ICON_SIZE + WIDTH_PADDING); - int rowHeight = Math.max(metrics.getHeight(), DEFAULT_ICON_SIZE); - directoryList.setFixedCellHeight(rowHeight + HEIGHT_PADDING); - } - - private int getDefaultMaxWidth(List theFiles, FontMetrics metrics) { - int maxWidth = 0; - if (theFiles.size() > 0) { - for (File file : theFiles) { - maxWidth = Math.max(maxWidth, metrics.stringWidth(getDisplayName(file))); - } - return maxWidth; - } - - Dimension scrollSize = directoryScroll.getSize(); - if (scrollSize.width == 0) { - return 0; - } - - Border border = directoryScroll.getBorder(); - if (border != null) { - Insets borderInsets = border.getBorderInsets(directoryScroll); - if (borderInsets != null) { - scrollSize.width -= (borderInsets.right + borderInsets.left); - } - } - return scrollSize.width - WIDTH_PADDING - DEFAULT_ICON_SIZE; - } - String getDisplayName(File file) { if (file != null) { if (GhidraFileChooser.MY_COMPUTER.equals(getCurrentDirectory())) { @@ -906,8 +860,10 @@ public class GhidraFileChooser extends DialogComponentProvider } private void setDirectoryList(File directory, List files) { + // if the visible listing is still the same directory as this incoming list of files if (currentDirectory().equals(directory)) { - computeListCellDimensions(files); + // recompute list cell dims before causing an update to the model + directoryList.recomputeListCellDimensions(files); directoryTableModel.setFiles(files); directoryTable.scrollRectToVisible(new Rectangle(0, 0, 0, 0)); directoryListModel.setFiles(files); diff --git a/Ghidra/Framework/Docking/src/main/java/docking/widgets/list/GListCellRenderer.java b/Ghidra/Framework/Docking/src/main/java/docking/widgets/list/GListCellRenderer.java index ac052c9ecb..16cb5bf4fe 100644 --- a/Ghidra/Framework/Docking/src/main/java/docking/widgets/list/GListCellRenderer.java +++ b/Ghidra/Framework/Docking/src/main/java/docking/widgets/list/GListCellRenderer.java @@ -16,6 +16,7 @@ package docking.widgets.list; import java.awt.*; +import java.util.List; import java.util.function.Function; import javax.swing.*; @@ -121,4 +122,31 @@ public class GListCellRenderer extends AbstractGCellRenderer implements ListC protected void configureFont(JList list, ListModel model, int index) { setFont(defaultFont); } + + /** + * Returns the width, height necessary to display the largest element in this list. + *

+ * Useful for setting a JList's fixed cell width and height to the actual necessary size. + *

+ * NOTE: the items and the renderer must be in plain text mode, not HTML rendering mode. + * + * @param list the JList that uses this cell renderer + * @param items the items to measure + * @param minWidth the minimum width that can be returned + * @param minHeight the minimum height that can be returned + * @return a new Dimension containing a width and height value necessary to display the largest + * element in the list + */ + public Dimension computePlainTextListCellDimensions(JList list, + List items, + int minWidth, int minHeight) { + configureFont(list, list.getModel(), 0); + FontMetrics metrics = getFontMetrics(getFont()); + int maxWidth = minWidth; + for (E item : items) { + String text = getItemText(item).toString(); + maxWidth = Math.max(maxWidth, metrics.stringWidth(text)); + } + return new Dimension(maxWidth, Math.max(metrics.getHeight(), minHeight)); + } }