Skip to content

Commit

Permalink
Add options to show preview icons for ITM and SPL resources in resource
Browse files Browse the repository at this point in the history
tree and resource selection lists
  • Loading branch information
Argent77 committed Jul 11, 2023
1 parent bd08567 commit e578e1f
Show file tree
Hide file tree
Showing 9 changed files with 408 additions and 9 deletions.
6 changes: 6 additions & 0 deletions src/org/infinity/AppOption.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ public class AppOption {
/** Menu Options: ShowTreeSearchNames (Boolean, Default: true) */
public static final AppOption SHOW_TREE_SEARCH_NAMES = new AppOption(OptionsMenuItem.OPTION_SHOWTREESEARCHNAMES,
"Show Search Names in Resource Tree", true);
/** Menu Options: Show Icons in Resource List (Boolean, Default: false) */
public static final AppOption SHOW_RESOURCE_LIST_ICONS = new AppOption(OptionsMenuItem.OPTION_SHOW_RESOURCE_LIST_ICONS,
"Show Icons in Resource List", false);
/** Menu Options: Show Icons in Resource Tree (Boolean, Default: false) */
public static final AppOption SHOW_RESOURCE_TREE_ICONS = new AppOption(OptionsMenuItem.OPTION_SHOW_RESOURCE_TREE_ICONS,
"Show Icons in Resource Tree", false);
/** Menu Options: HighlightOverridden (Boolean, Default: true) */
public static final AppOption HIGHLIGHT_OVERRIDDEN = new AppOption(OptionsMenuItem.OPTION_HIGHLIGHT_OVERRIDDEN,
"Show Overridden Files in Bold in Resource Tree", true);
Expand Down
93 changes: 91 additions & 2 deletions src/org/infinity/NearInfinity.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
import java.util.function.Supplier;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

Expand Down Expand Up @@ -119,6 +120,7 @@
import org.infinity.util.CharsetDetector;
import org.infinity.util.CreMapCache;
import org.infinity.util.FileDeletionHook;
import org.infinity.util.IconCache;
import org.infinity.util.IdsMapCache;
import org.infinity.util.IniMapCache;
import org.infinity.util.LauncherUtils;
Expand Down Expand Up @@ -235,6 +237,7 @@ public final class NearInfinity extends JFrame implements ActionListener, Viewab
private int tablePanelHeight;
private ProgressMonitor pmProgress;
private int progressIndex;
private SwingWorker<Void, Void> iconCacheWorker;

private static Path findKeyfile() {
JFileChooser chooser;
Expand Down Expand Up @@ -428,7 +431,7 @@ private NearInfinity(Options options) {
System.exit(10);
}

showProgress("Starting Near Infinity" + Misc.MSG_EXPAND_LARGE, 6);
showProgress("Starting Near Infinity" + Misc.MSG_EXPAND_LARGE, 7);
SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
Expand Down Expand Up @@ -456,6 +459,9 @@ protected Void doInBackground() throws Exception {
// FileWatcher.getInstance().start();
// }

advanceProgress("Caching resources...");
cacheResourceIcons(false);

return null;
}
};
Expand Down Expand Up @@ -827,7 +833,6 @@ public void openGame(Path keyFile) {
Path oldKeyFile = Profile.getChitinKey();
ChildFrame.closeWindows();
clearCache(false);
BaseOpcode.reset();
Profile.openGame(keyFile, BrowserMenuBar.getInstance().getGameMenu().getBookmarkName(keyFile));

// making sure vital game resources are accessible
Expand Down Expand Up @@ -916,6 +921,7 @@ public void refreshGame() {
containerpanel.revalidate();
containerpanel.repaint();
}
cacheResourceIcons(true);
} finally {
blocker.setBlocked(false);
}
Expand Down Expand Up @@ -1160,6 +1166,7 @@ private static boolean reloadFactory(boolean refreshOnly) {

// Central method for clearing cached data
private static void clearCache(boolean refreshOnly) {
NearInfinity.getInstance().cancelCacheResourceIcons();
if (ResourceFactory.getKeyfile() != null) {
ResourceFactory.getKeyfile().closeBIFFFiles();
}
Expand All @@ -1168,6 +1175,7 @@ private static void clearCache(boolean refreshOnly) {
}
DlcManager.close();
FileManager.reset();
IconCache.clearCache();
IdsMapCache.clearCache();
IniMapCache.clearCache();
Table2daCache.clearCache();
Expand Down Expand Up @@ -1459,6 +1467,87 @@ private JPanel createJavaInfoPanel() {
return infoPanel;
}

/**
* Cancels an ongoing resource icon cache operation.
*
* The method returns only after the operation has been successfully cancelled.
*/
private void cancelCacheResourceIcons() {
if (iconCacheWorker != null) {
iconCacheWorker.cancel(false);
for (int i = 0; i < 100 && iconCacheWorker.getProgress() < 100; i++) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
iconCacheWorker = null;
}
}

/**
* Preloads icons for all ITM and SPL resources into the cache.
*
* @param threaded Whether to perform the operation in a separate thread.
*/
private void cacheResourceIcons(boolean threaded) {
// Operation for caching resource icons
final Supplier<Void> operation = () -> {
try {
IconCache.clearCache();
final List<Integer> sizeList = new ArrayList<>();
if (BrowserMenuBar.getInstance().getOptions().showResourceTreeIcons()) {
sizeList.add(IconCache.getDefaultTreeIconSize());
}
if (BrowserMenuBar.getInstance().getOptions().showResourceListIcons()) {
sizeList.add(IconCache.getDefaultListIconSize());
}
if (!sizeList.isEmpty()) {
final String[] types = { "ITM", "SPL" };
int[] sizes = sizeList.stream().mapToInt(Integer::intValue).toArray();
for (final String type : types) {
final List<ResourceEntry> resources = ResourceFactory.getResources(type);
if (resources != null) {
for (final ResourceEntry e : resources) {
for (final int size : sizes) {
if (iconCacheWorker != null && iconCacheWorker.isCancelled()) {
return null;
}
IconCache.get(e, size);
}
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
};

// ensure that ongoing operations have ended before starting a new operation
cancelCacheResourceIcons();

if (threaded) {
iconCacheWorker = new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() throws Exception {
setProgress(0);
try {
operation.get();
} catch (Exception e) {
e.printStackTrace();
}
setProgress(100);
return null;
}
};
iconCacheWorker.execute();
} else {
operation.get();
}
}

// -------------------------- INNER CLASSES --------------------------

private static final class Options {
Expand Down
15 changes: 13 additions & 2 deletions src/org/infinity/datatype/ResourceRef.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
Expand Down Expand Up @@ -128,7 +129,9 @@ public JComponent edit(final ActionListener container) {
}
addExtraEntries(values);
Collections.sort(values, IGNORE_CASE_EXT_COMPARATOR);
list = new TextListPanel<>(values, false);
boolean showIcons = BrowserMenuBar.getInstance().getOptions().showResourceListIcons() &&
Arrays.stream(types).anyMatch(s -> s.equalsIgnoreCase("ITM") || s.equalsIgnoreCase("SPL"));
list = new TextListPanel<>(values, false, showIcons);
list.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent event) {
Expand Down Expand Up @@ -385,7 +388,7 @@ private void setValue(String newValue) {

// -------------------------- INNER CLASSES --------------------------
/** Class that represents resource reference in the list of choice. */
static final class ResourceRefEntry {
public static final class ResourceRefEntry {
final ResourceEntry entry;

/**
Expand All @@ -404,6 +407,14 @@ private ResourceRefEntry(ResourceEntry entry) {
this.name = name;
}

public ResourceEntry getEntry() {
return entry;
}

public String getName() {
return name;
}

@Override
public String toString() {
return entry == null ? name : BrowserMenuBar.getInstance().getOptions().getResRefMode().format(entry);
Expand Down
10 changes: 10 additions & 0 deletions src/org/infinity/gui/PreferencesDialog.java
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ public String toString() {
"With this option enabled Near Infinity shows the search name of resources in the resource tree "
+ "in parentheses if available, such as creature, item or spell names.",
AppOption.SHOW_TREE_SEARCH_NAMES),
OptionCheckBox.create(AppOption.SHOW_RESOURCE_TREE_ICONS.getName(), AppOption.SHOW_RESOURCE_TREE_ICONS.getLabel(),
"With this option enabled Near Infinity shows icons alongside names in the resource tree for ITM and "
+ "SPL resources."
+ "<p><strong>Caution:</strong> Enabling this option may result in noticeable lags on slower systems.</p>",
AppOption.SHOW_RESOURCE_TREE_ICONS),
OptionCheckBox.create(AppOption.SHOW_RESOURCE_LIST_ICONS.getName(), AppOption.SHOW_RESOURCE_LIST_ICONS.getLabel(),
"With this option enabled Near Infinity shows icons alongside names in resource selection lists for "
+ "ITM and SPL resources."
+ "<p><strong>Caution:</strong> Enabling this option may result in noticeable lags on slower systems.</p>",
AppOption.SHOW_RESOURCE_LIST_ICONS),
OptionCheckBox.create(AppOption.HIGHLIGHT_OVERRIDDEN.getName(), AppOption.HIGHLIGHT_OVERRIDDEN.getLabel(),
"If checked, files that are listed in the <em>chitin.key</em> and are also available in the "
+ "Override folder, will be shown in <strong>bold</strong> in the resource tree."
Expand Down
17 changes: 16 additions & 1 deletion src/org/infinity/gui/ResourceTree.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
Expand All @@ -23,8 +24,10 @@
import java.util.Locale;
import java.util.Stack;

import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
Expand Down Expand Up @@ -55,6 +58,7 @@
import org.infinity.resource.key.ResourceEntry;
import org.infinity.resource.key.ResourceTreeFolder;
import org.infinity.resource.key.ResourceTreeModel;
import org.infinity.util.IconCache;
import org.infinity.util.io.FileEx;
import org.infinity.util.io.FileManager;
import org.infinity.util.io.StreamUtils;
Expand Down Expand Up @@ -703,7 +707,15 @@ public void popupMenuCanceled(PopupMenuEvent event) {
}

private static final class ResourceTreeRenderer extends DefaultTreeCellRenderer {
private final int iconSize;

private ResourceTreeRenderer() {
super();
final JLabel l = new JLabel();
final FontMetrics fm = l.getFontMetrics(l.getFont());
int fontHeight = fm.getHeight();
// scale icon size up to the next multiple of 4
this.iconSize = Math.max(IconCache.getDefaultTreeIconSize(), (fontHeight + 3) & ~3);
}

@Override
Expand All @@ -713,6 +725,9 @@ public Component getTreeCellRendererComponent(JTree tree, Object o, boolean sel,
Font font = tree.getFont();
if (leaf && o instanceof ResourceEntry) {
final ResourceEntry e = (ResourceEntry) o;
boolean showIcon = BrowserMenuBar.getInstance().getOptions().showResourceTreeIcons() &&
(e.getExtension().equalsIgnoreCase("ITM") || e.getExtension().equalsIgnoreCase("SPL"));
final Icon icon = showIcon ? IconCache.get(e, iconSize) : e.getIcon();

final BrowserMenuBar options = BrowserMenuBar.getInstance();
if (options.getOptions().showTreeSearchNames()) {
Expand All @@ -724,7 +739,7 @@ public Component getTreeCellRendererComponent(JTree tree, Object o, boolean sel,
final boolean hasTitle = title != null && !title.isEmpty() && !"No such index".equals(title);
setText(hasTitle ? name + " - " + title : name);
}
setIcon(e.getIcon());
setIcon(icon);
// Do not use bold in Override mode othrewise almost all entries will be in bold, which looks not so good
final boolean inOverrideMode = options.getOptions().getOverrideMode() == OverrideMode.InOverride;
if (e.hasOverride() && !inOverrideMode && options.getOptions().highlightOverridden()) {
Expand Down
39 changes: 36 additions & 3 deletions src/org/infinity/gui/TextListPanel.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Graphics;
Expand All @@ -18,6 +19,7 @@
import java.util.Locale;

import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JComponent;
import javax.swing.JList;
import javax.swing.JPanel;
Expand All @@ -36,29 +38,39 @@
import javax.swing.event.ListSelectionListener;

import org.infinity.NearInfinity;
import org.infinity.datatype.ResourceRef;
import org.infinity.icon.Icons;
import org.infinity.util.FilteredListModel;
import org.infinity.util.IconCache;
import org.infinity.util.Misc;

public final class TextListPanel<E> extends JPanel
public class TextListPanel<E> extends JPanel
implements DocumentListener, ListSelectionListener, ActionListener, ChangeListener {
private static boolean filterEnabled = false;

private final FilteredListModel<E> listmodel = new FilteredListModel<>(filterEnabled);
private final JList<E> list = new JList<>();
private final JScrollPane scrollPane;
private final JTextField tfield = new JTextField();
private final JToggleButton tbFilter = new JToggleButton(Icons.ICON_FILTER_16.getIcon(), filterEnabled);

private boolean sortValues = true;

public TextListPanel(List<? extends E> values) {
this(values, true);
this(values, true, false);
}

public TextListPanel(List<? extends E> values, boolean sortValues) {
this(values, sortValues, false);
}

public TextListPanel(List<? extends E> values, boolean sortValues, boolean showIcons) {
super(new BorderLayout());
this.sortValues = sortValues;
setValues(values);
if (showIcons) {
list.setCellRenderer(new IconCellRenderer());
}
list.setModel(listmodel);
list.setSelectedIndex(0);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
Expand All @@ -67,6 +79,8 @@ public TextListPanel(List<? extends E> values, boolean sortValues) {
listmodel.addFilterChangeListener(this);
tfield.getDocument().addDocumentListener(this);

scrollPane = new JScrollPane(list);

tbFilter.setToolTipText("Toggle filtering on or off");
tbFilter.addActionListener(this);
// Horizontal margins are too wasteful on default l&f
Expand All @@ -82,7 +96,7 @@ public TextListPanel(List<? extends E> values, boolean sortValues) {
pInput.add(tbFilter, BorderLayout.EAST);

add(pInput, BorderLayout.NORTH);
add(new JScrollPane(list), BorderLayout.CENTER);
add(scrollPane, BorderLayout.CENTER);
ensurePreferredComponentWidth(list, true);
ensurePreferredComponentWidth(tfield, false);
}
Expand Down Expand Up @@ -306,4 +320,23 @@ private void calculatePreferredComponentHeight(JComponent c) {
c.setPreferredSize(d);
c.invalidate();
}

// -------------------------- INNER CLASSES --------------------------

private static class IconCellRenderer extends DefaultListCellRenderer {
public IconCellRenderer() {
super();
}

@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected,
boolean cellHasFocus) {
super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value instanceof ResourceRef.ResourceRefEntry) {
final ResourceRef.ResourceRefEntry entry = (ResourceRef.ResourceRefEntry) value;
setIcon(IconCache.get(entry.getEntry(), IconCache.getDefaultListIconSize()));
}
return this;
}
}
}
Loading

0 comments on commit e578e1f

Please sign in to comment.