// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.gui; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.KeyStroke; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; import javax.swing.event.EventListenerList; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.infinity.resource.Profile; import org.infinity.resource.ResourceFactory; import org.infinity.resource.key.ResourceEntry; public class ResourceChooser extends JComponent implements ActionListener { /** Return value if cancel is chosen. */ public static int CANCEL_OPTION = 0; /** Return value if approve (ok, yes) is chosen. */ public static int APPROVE_OPTION = 1; /** Return value if an error occured. */ public static int ERROR_OPTION = -1; // default component dimension private static Dimension DEFAULT_DIMENSION = new Dimension(200, 250); private final EventListenerList listeners = new EventListenerList(); private JComboBox<String> cbType; private TextListPanel lpResources; private int dialogResult; /** * Constructs a new Resource Chooser component. * The first available resource type will be preselected. */ public ResourceChooser() { this(null); } /** * Constructs a new Resource Chooser component with a preselected resource type. * @param initialExtension The format extension to preselect. */ public ResourceChooser(String initialExtension) { init(initialExtension); } //--------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == cbType) { Object o = cbType.getSelectedItem(); resetResourceList((o != null) ? o.toString() : null); } } //--------------------- End Interface ActionListener --------------------- public void addActionListener(ActionListener l) { if (l != null) { listeners.add(ActionListener.class, l); } } public ActionListener[] getActionListeners() { return listeners.getListeners(ActionListener.class); } public void removeActionListener(ActionListener l) { if (l != null) { listeners.remove(ActionListener.class, l); } } public void addListSelectionListener(ListSelectionListener l) { lpResources.addListSelectionListener(l); } public void removeListSelectionListener(ListSelectionListener l) { lpResources.removeListSelectionListener(l); } /** * Returns the format extension of the selected resource type. * @return Resource type as file extension string or {@code null} if no type is selected. */ public String getSelectedType() { if (cbType.getSelectedItem() != null) { return cbType.getSelectedItem().toString(); } else { return null; } } /** * Returns the filename of the selected resource from the list. Returns {@code null} if * no item has been selected. * @return The resource filename or {@code null} if no selection has been made. */ public String getSelectedItem() { if (lpResources != null) { if (lpResources.getSelectedValue() != null) { return lpResources.getSelectedValue().toString(); } } return null; } /** * Pops up a resource chooser dialog. * @param parent The parent component of the dialog. Can be {@code null}. * @return The return state of the dialog on pop down. Can be either */ public int showDialog(Component parent) { dialogResult = ERROR_OPTION; ResourceDialog dialog = new ResourceDialog(parent); try { dialog.setVisible(true); } finally { dialog.dispose(); } return dialogResult; } protected void fireActionPerformed(ActionEvent event) { ActionListener[] list = listeners.getListeners(ActionListener.class); for (final ActionListener l: list) { if (event == null) { event = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, null); } l.actionPerformed(event); } } private void init(String initialExtension) { JLabel lType = new JLabel("Resource type:"); cbType = new JComboBox<>(Profile.getAvailableResourceTypes()); if (cbType.getModel().getSize() > 0) { if (initialExtension != null) { cbType.setSelectedItem(initialExtension); } if (cbType.getSelectedIndex() < 0) { cbType.setSelectedIndex(0); } cbType.addActionListener(this); resetResourceList(cbType.getSelectedItem().toString()); } else { resetResourceList(null); } JPanel pMain = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0); pMain.add(lType, gbc); gbc = ViewerUtil.setGBC(gbc, 1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 8, 0, 0), 0, 0); pMain.add(cbType, gbc); gbc = ViewerUtil.setGBC(gbc, 0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(8, 0, 0, 0), 0, 0); pMain.add(lpResources, gbc); setLayout(new BorderLayout(8, 8)); add(pMain, BorderLayout.CENTER); setPreferredSize(DEFAULT_DIMENSION); } private void resetResourceList(String ext) { final List<ResourceEntry> resources = (ext != null) ? ResourceFactory.getResources(ext) : new ArrayList<ResourceEntry>(); if (lpResources != null) { // switching type in existing list panel RootPaneContainer rpc = (RootPaneContainer)SwingUtilities.getAncestorOfClass(RootPaneContainer.class, this); final WindowBlocker block = (rpc != null) ? new WindowBlocker(rpc) : null; if (block != null) { block.setBlocked(true); } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { lpResources.setValues(resources); if (lpResources.getModel().getSize() > 0) { lpResources.setSelectedIndex(0); lpResources.ensureIndexIsVisible(0); } if (block != null) { block.setBlocked(false); } } }); } else { // initializing new list panel (no need to block controls) lpResources = new TextListPanel(resources, true); lpResources.addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent event) { if (event.getClickCount() == 2) { dialogResult = (lpResources.getSelectedIndex() >= 0) ? APPROVE_OPTION : CANCEL_OPTION; fireActionPerformed(new ActionEvent(ResourceChooser.this, ActionEvent.ACTION_PERFORMED, null)); } } }); if (lpResources.getModel().getSize() > 0) { lpResources.setSelectedIndex(0); lpResources.ensureIndexIsVisible(0); } } } //-------------------------- INNER CLASSES -------------------------- private class ResourceDialog extends JDialog implements ActionListener { private Action acceptAction, cancelAction; private JButton bAccept, bCancel; public ResourceDialog(Component parent) { super((parent instanceof Frame) ? (Frame)parent : (Frame)SwingUtilities.getAncestorOfClass(Frame.class, parent), "Choose resource", true); init(); } @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == ResourceChooser.this) { dialogResult = APPROVE_OPTION; setVisible(false); } } @Override public void dispose() { ResourceChooser.this.removeActionListener(this); super.dispose(); } private void init() { acceptAction = new DialogOkAction(this); addListSelectionListener((DialogOkAction)acceptAction); cancelAction = new DialogCancelAction(this); bAccept = new JButton(acceptAction); bCancel = new JButton(cancelAction); Dimension d = new Dimension(Math.max(bAccept.getPreferredSize().width, bCancel.getPreferredSize().width), Math.max(bAccept.getPreferredSize().height, bCancel.getPreferredSize().height)); bAccept.setPreferredSize(d); bCancel.setPreferredSize(d); JPanel panelButtons = new JPanel(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); panelButtons.add(new JPanel(), gbc); gbc = ViewerUtil.setGBC(gbc, 1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(8, 4, 8, 0), 0, 0); panelButtons.add(bAccept, gbc); gbc = ViewerUtil.setGBC(gbc, 2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(8, 8, 8, 4), 0, 0); panelButtons.add(bCancel, gbc); gbc = ViewerUtil.setGBC(gbc, 3, 0, 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0); panelButtons.add(new JPanel(), gbc); JPanel panelMain = new JPanel(new GridBagLayout()); gbc = ViewerUtil.setGBC(gbc, 0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(8, 8, 8, 8), 0, 0); panelMain.add(ResourceChooser.this, gbc); ActionMap actionMap = panelButtons.getActionMap(); actionMap.put(cancelAction.getValue(Action.DEFAULT), cancelAction); actionMap.put(acceptAction.getValue(Action.DEFAULT), acceptAction); InputMap inputMap = panelButtons.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelAction.getValue(Action.DEFAULT)); inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), acceptAction.getValue(Action.DEFAULT)); getContentPane().add(panelMain, BorderLayout.CENTER); getContentPane().add(panelButtons, BorderLayout.SOUTH); pack(); setLocationRelativeTo(getParent()); ResourceChooser.this.addActionListener(this); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { ResourceChooser.this.dialogResult = CANCEL_OPTION; } }); } } private class DialogOkAction extends AbstractAction implements ListSelectionListener { public static final String ACTION_NAME = "OK"; private JDialog dialog; public DialogOkAction(JDialog dialog) { this.dialog = dialog; putValue(Action.DEFAULT, ACTION_NAME); putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); putValue(Action.NAME, ACTION_NAME); setEnabled(getSelectedItem() != null); } @Override public void actionPerformed(ActionEvent event) { dialogResult = APPROVE_OPTION; dialog.setVisible(false); fireActionPerformed(event); } @Override public void valueChanged(ListSelectionEvent e) { setEnabled(getSelectedItem() != null); } } private class DialogCancelAction extends AbstractAction { public static final String ACTION_NAME = "Cancel"; private JDialog dialog; public DialogCancelAction(JDialog dialog) { this.dialog = dialog; putValue(Action.DEFAULT, ACTION_NAME); putValue(Action.ACTION_COMMAND_KEY, ACTION_NAME); putValue(Action.NAME, ACTION_NAME); } @Override public void actionPerformed(ActionEvent event) { dialogResult = CANCEL_OPTION; dialog.setVisible(false); fireActionPerformed(event); } } }