// 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.FlowLayout;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import org.infinity.icon.Icons;
/**
* Encapsulates a flexible button panel that can be used to streamline the bottom panel present
* in almost all resource views.
*/
public class ButtonPanel extends JPanel
{
/** Predefined controls that can be added or inserted into the button panel. */
public enum Control {
/** "Find..." (JButton) */
FIND_BUTTON,
/** "Find..." (ButtonPopupMenu) */
FIND_MENU,
/** "Find references..." (JButton) */
FIND_REFERENCES,
/** "View/Edit..." (JButton) */
VIEW_EDIT,
/** "Print..." (JButton) */
PRINT,
/** "Export..." (JButton) */
EXPORT_BUTTON,
/** "Export..." (ButtonPopupMenu) */
EXPORT_MENU,
/** "Save" (JButton) */
SAVE,
/** "Add..." (ButtonPopupMenu) */
ADD,
/** "Remove" (JButton) */
REMOVE,
/** "Trim spaces" (JButton) */
TRIM_SPACES,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_1,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_2,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_3,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_4,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_5,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_6,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_7,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_8,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_9,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_10,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_11,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_12,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_13,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_14,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_15,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_16,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_17,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_18,
/** Unused control type. Can be used to identify custom controls. */
CUSTOM_19,
}
private static final int DefaultGapSize = 4;
private final List<Entry> listControls = new ArrayList<Entry>();
private int gapSize;
/**
* Creates a component of the specified type.
* @param type One of the predefined types defined in the enum {@code Control}.
* @return The resulting component.
*/
public static JComponent createControl(Control type)
{
JComponent retVal = null;
switch (type) {
case ADD:
{
ButtonPopupMenu bpm = new ButtonPopupMenu("Add...", new JMenuItem[]{});
bpm.setIcon(Icons.getIcon(Icons.ICON_ADD_16));
retVal = bpm;
break;
}
case EXPORT_BUTTON:
{
JButton b = new JButton("Export...", Icons.getIcon(Icons.ICON_EXPORT_16));
b.setToolTipText("NB! Will export last *saved* version");
b.setMnemonic('e');
retVal = b;
break;
}
case EXPORT_MENU:
{
ButtonPopupMenu bpm = new ButtonPopupMenu("Export...", new JMenuItem[]{});
bpm.setIcon(Icons.getIcon(Icons.ICON_EXPORT_16));
retVal = bpm;
break;
}
case FIND_BUTTON:
{
JButton b = new JButton("Find...", Icons.getIcon(Icons.ICON_FIND_16));
b.setMnemonic('f');
retVal = b;
break;
}
case FIND_MENU:
{
ButtonPopupMenu bpm = new ButtonPopupMenu("Find...", new JMenuItem[]{});
bpm.setIcon(Icons.getIcon(Icons.ICON_FIND_16));
retVal = bpm;
break;
}
case FIND_REFERENCES:
{
JButton b = new JButton("Find references...", Icons.getIcon(Icons.ICON_FIND_16));
b.setMnemonic('f');
retVal = b;
break;
}
case PRINT:
{
JButton b = new JButton(Icons.getIcon(Icons.ICON_PRINT_16));
b.setMargin(new Insets(b.getMargin().top, 3, b.getMargin().bottom, 3));
b.setToolTipText("Print");
retVal = b;
break;
}
case REMOVE:
{
JButton b = new JButton("Remove", Icons.getIcon(Icons.ICON_REMOVE_16));
b.setMnemonic('r');
retVal = b;
break;
}
case SAVE:
{
JButton b = new JButton("Save", Icons.getIcon(Icons.ICON_SAVE_16));
b.setMnemonic('a');
retVal = b;
break;
}
case TRIM_SPACES:
{
retVal = new JButton("Trim spaces", Icons.getIcon(Icons.ICON_REFRESH_16));
break;
}
case VIEW_EDIT:
{
JButton b = new JButton("View/Edit", Icons.getIcon(Icons.ICON_ZOOM_16));
b.setMnemonic('v');
retVal = b;
break;
}
default:
}
return retVal;
}
public ButtonPanel()
{
gapSize = DefaultGapSize;
setLayout(new FlowLayout(FlowLayout.CENTER, gapSize, DefaultGapSize));
}
/** Returns the gap size between the components */
public int getGapSize()
{
return gapSize;
}
/** Sets the gap size between the components. */
public void setGapSize(int size)
{
if (size < 0) size = DefaultGapSize;
if (size != getGapSize()) {
gapSize = size;
updateButtonBar();
}
}
/**
* Adds one of the predefined controls to the button panel.
* @param type The predefined control to add.
* @return The added component, or {@code null} on error.
*/
public JComponent addControl(Control type)
{
return addControl(getControlCount(), type);
}
/**
* Adds the specified component to the button panel.
* @param component The custom component to add.
* @return The added component, or {@code null} on error.
*/
public JComponent addControl(JComponent component)
{
return addControl(getControlCount(), component);
}
/**
* Adds the specified component to the button panel and associates it with a specific type.
* @param component The custom component to add.
* @param type The {@code Control} type to attach.
* @return The added component, or {@code null} on error.
*/
public JComponent addControl(JComponent component, Control type)
{
return addControl(getControlCount(), component, type);
}
/**
* Inserts one of the predefined control into the button panel at the specified position if possible.
* @param position The requested position where to place the control (ordered from left to right)
* @param type The predefined control to add.
* @return The inserted component, or {@code null} on error.
*/
public JComponent addControl(int position, Control type)
{
return addControl(position, createControl(type), type);
}
/**
* Inserts the specified control into the button panel at the specified position if possible.
* @param position The requested position where to place the control (ordered from left to right)
* @param component The custom component to add.
* @return The inserted component, or {@code null} on error.
*/
public JComponent addControl(int position, JComponent component)
{
return addControl(position, component, null);
}
/**
* Inserts the specified control into the button panel at the specified position if possible and
* associates it with a specific type.
* @param position The requested position where to place the control (ordered from left to right)
* @param component The custom component to add.
* @param type The {@code Control} type to attach.
* @return The inserted component, or {@code null} on error.
*/
public JComponent addControl(int position, JComponent component, Control type)
{
if (component != null && position >= 0 && position <= listControls.size()) {
listControls.add(position, new Entry(component, type));
updateButtonBar();
return component;
}
return null;
}
/** Returns the number of assigned controls in the button panel. */
public int getControlCount()
{
return listControls.size();
}
/**
* Returns the position of the specified control in the button panel.
* Returns -1 if the control does not exist.
*/
public int getControlPosition(JComponent component)
{
int retVal = -1;
if (component != null) {
retVal = getControlIndex(component);
}
return retVal;
}
/**
* Returns the component at the specified index.
* @param index The index of the component.
* @return The component or {@code null} if index is out of bounds.
*/
public JComponent getControl(int index)
{
if (index >= 0 && index < listControls.size()) {
return listControls.get(index).getComponent();
}
return null;
}
/**
* Returns the first available component of the specified type.
* @param type One of the predefined types defined in the enum {@code Control}.
* Specifying {@code null} will return the first available non-predefined component.
* @return The component or {@code null} if not found.
*/
public JComponent getControlByType(Control type)
{
for (int i = 0; i < listControls.size(); i++) {
if (listControls.get(i).getType() == type) {
return listControls.get(i).getComponent();
}
}
return null;
}
/**
* Returns all available components of the specified type.
* @param type One of the predefined types defined in the enum {@code Control}.
* @return An array of matching components. The array will be empty if no component has been found.
*/
public JComponent[] getControlsByType(Control type)
{
// determining number of available components
int max = 0;
for (int i = 0; i < listControls.size(); i++) {
if (listControls.get(i).getType() == type) {
max++;
}
}
// getting components
JComponent[] retVal = new JComponent[max];
int cnt = 0;
for (int i = 0; i < listControls.size() && cnt < max; i++) {
if (listControls.get(i).getType() == type) {
retVal[cnt] = listControls.get(i).getComponent();
cnt++;
}
}
return retVal;
}
/** Removes the control at the specified position from the button panel. */
public void removeControl(int position)
{
if (position >= 0 && position < listControls.size()) {
listControls.remove(position);
}
updateButtonBar();
}
/** Removes the specified control from the button panel. */
public void removeControl(JComponent control)
{
if (control != null) {
int idx = getControlIndex(control);
removeControl(idx);
}
}
/** Removes all controls from the button panel. */
public void removeAllControls()
{
listControls.clear();
updateButtonBar();
}
/**
* Moves the specified control to another position.
* @param control The control to move.
* @param newPosition The new position of the control.
*/
public void moveControl(JComponent control, int newPosition)
{
if (control != null) {
int idx = getControlIndex(control);
moveControl(idx, newPosition);
}
}
/**
* Moves a control to another position.
* @param curPosition The position of the control to move.
* @param newPosition The new position of the control.
*/
public void moveControl(int curPosition, int newPosition)
{
if (curPosition >= 0 && curPosition < listControls.size()) {
if (newPosition < 0) newPosition = 0;
if (newPosition >= listControls.size()) newPosition = listControls.size() - 1;
if (curPosition != newPosition) {
Entry e = listControls.get(curPosition);
listControls.remove(curPosition);
listControls.add(newPosition, e);
}
}
}
// Recreates the button panel from the available components
private void updateButtonBar()
{
removeAll();
setLayout(new FlowLayout(FlowLayout.CENTER, gapSize, DefaultGapSize));
for (int i = 0; i < listControls.size(); i++) {
add(listControls.get(i).getComponent());
}
revalidate();
}
// Returns the list index of the specified component, returns -1 if not found.
private int getControlIndex(JComponent comp)
{
int retVal = -1;
if (comp != null) {
for (int i = 0; i < listControls.size(); i++) {
if (listControls.get(i).getComponent() == comp) {
retVal = i;
break;
}
}
}
return retVal;
}
//-------------------------- INNER CLASSES --------------------------
private class Entry
{
private JComponent component;
private Control type;
public Entry(JComponent component, Control type)
{
this.component = component;
this.type = type;
}
public JComponent getComponent() { return component; }
public Control getType() { return type; }
}
}