// Near Infinity - An Infinity Engine Browser and Editor // Copyright (C) 2001 - 2005 Jon Olav Hauglid // See LICENSE.txt for license information package org.infinity.datatype; import java.awt.BorderLayout; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; import java.util.regex.PatternSyntaxException; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import org.infinity.gui.StructViewer; import org.infinity.resource.AbstractStruct; import org.infinity.resource.StructEntry; public class Flag extends Datatype implements Editable, IsNumeric, ActionListener { public static final String DESC_NONE = "No flags set"; protected String nodesc; protected String[] table, toolTable; private ActionListener container; private JButton bAll, bNone; private JCheckBox[] checkBoxes; private long value; Flag(ByteBuffer buffer, int offset, int length, String name) { this(null, buffer, offset, length, name); } Flag(StructEntry parent, ByteBuffer buffer, int offset, int length, String name) { super(parent, offset, length, name); read(buffer, offset); } /** * @param stable Contains default value when no flag is selected and a list of flag descriptions. * Optionally you can combine flag descriptions with tool tips, using the defaul * separator char ';'. */ public Flag(ByteBuffer buffer, int offset, int length, String name, String[] stable) { this(null, buffer, offset, length, name, stable); } /** * @param stable Contains default value when no flag is selected and a list of flag descriptions. * Optionally you can combine flag descriptions with tool tips, using the specified * separator char. * @param separator Character that can be used to split flag description and tool tip. */ public Flag(ByteBuffer buffer, int offset, int length, String name, String[] stable, char separator) { this(null, buffer, offset, length, name, stable, separator); } /** * @param stable Contains default value when no flag is selected and a list of flag descriptions. * Optionally you can combine flag descriptions with tool tips, using the defaul * separator char ';'. */ public Flag(StructEntry parent, ByteBuffer buffer, int offset, int length, String name, String[] stable) { this(parent, buffer, offset, length, name, stable, ';'); } /** * @param stable Contains default value when no flag is selected and a list of flag descriptions. * Optionally you can combine flag descriptions with tool tips, using the specified * separator char. * @param separator Character that can be used to split flag description and tool tip. */ public Flag(StructEntry parent, ByteBuffer buffer, int offset, int length, String name, String[] stable, char separator) { this(parent, buffer, offset, length, name); setEmptyDesc((stable == null || stable.length == 0) ? null : stable[0]); setFlagDescriptions(length, stable, 1, separator); } // --------------------- Begin Interface ActionListener --------------------- @Override public void actionPerformed(ActionEvent event) { if (event.getSource() == bAll) { for (final JCheckBox checkBox : checkBoxes) checkBox.setSelected(true); } else if (event.getSource() == bNone) { for (final JCheckBox checkBox : checkBoxes) checkBox.setSelected(false); } container.actionPerformed(new ActionEvent(this, 0, StructViewer.UPDATE_VALUE)); } // --------------------- End Interface ActionListener --------------------- // --------------------- Begin Interface Editable --------------------- @Override public JComponent edit(ActionListener container) { this.container = container; checkBoxes = new JCheckBox[table.length]; for (int i = 0; i < table.length; i++) { if (table[i] == null || table[i].isEmpty()) { checkBoxes[i] = new JCheckBox("Unknown (" + i + ')'); } else { checkBoxes[i] = new JCheckBox(table[i] + " (" + i + ')'); } if (toolTable[i] != null && !toolTable[i].isEmpty()) { checkBoxes[i].setToolTipText(toolTable[i]); } checkBoxes[i].addActionListener(container); checkBoxes[i].setActionCommand(StructViewer.UPDATE_VALUE); } bAll = new JButton("Select all"); bNone = new JButton("Select none"); bAll.setMargin(new Insets(0, bAll.getMargin().left, 0, bAll.getMargin().right)); bNone.setMargin(bAll.getMargin()); bAll.addActionListener(this); bNone.addActionListener(this); JPanel bPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 6, 0)); bPanel.add(bAll); bPanel.add(bNone); bPanel.add(new JLabel("None = " + nodesc)); JPanel boxPanel = new JPanel(new GridLayout(0, 4)); int rows = checkBoxes.length >> 2; if (rows << 2 != checkBoxes.length) { for (int i = 0; i < checkBoxes.length; i++) { boxPanel.add(checkBoxes[i]); checkBoxes[i].setSelected(isFlagSet(i)); } } else { for (int i = 0; i < rows; i++) for (int j = 0; j < 4; j++) { int index = i + j * rows; boxPanel.add(checkBoxes[index]); checkBoxes[index].setSelected(isFlagSet(index)); } } JPanel panel = new JPanel(new BorderLayout()); panel.add(boxPanel, BorderLayout.CENTER); panel.add(bPanel, BorderLayout.SOUTH); panel.setMinimumSize(DIM_BROAD); panel.setPreferredSize(DIM_WIDE); return panel; } @Override public void select() { } @Override public boolean updateValue(AbstractStruct struct) { // updating value value = 0L; for (int i = 0; i < checkBoxes.length; i++) if (checkBoxes[i].isSelected()) { setFlag(i); } // notifying listeners fireValueUpdated(new UpdateEvent(this, struct)); return true; } // --------------------- End Interface Editable --------------------- // --------------------- Begin Interface Writeable --------------------- @Override public void write(OutputStream os) throws IOException { writeLong(os, value); } // --------------------- End Interface Writeable --------------------- //--------------------- Begin Interface Readable --------------------- @Override public int read(ByteBuffer buffer, int offset) { buffer.position(offset); switch (getSize()) { case 1: value = buffer.get() & 0xff; break; case 2: value = buffer.getShort() & 0xffff; break; case 4: value = buffer.getInt() & 0xffffffff; break; default: throw new IllegalArgumentException(); } return offset + getSize(); } //--------------------- End Interface Readable --------------------- @Override public String toString() { StringBuffer sb = new StringBuffer("( "); if (value == 0) sb.append(nodesc).append(' '); else { for (int i = 0; i < 8 * getSize(); i++) if (isFlagSet(i)) { if (i < table.length && table[i] != null && !table[i].equals("")) sb.append(table[i]).append('(').append(i).append(") "); else sb.append("Unknown(").append(i).append(") "); } } sb.append(')'); return sb.toString(); } public String getString(int i) { return table[i]; } public boolean isFlagSet(int i) { long bitnr = 1L << i; return (value & bitnr) == bitnr; } //--------------------- Begin Interface IsNumeric --------------------- @Override public long getLongValue() { return value; } @Override public int getValue() { return (int)value; } //--------------------- End Interface IsNumeric --------------------- public void setValue(long newValue) { value = newValue; } private void setFlag(int i) { long mask = 1L << i; value |= mask; } // Sets description for empty flags protected void setEmptyDesc(String desc) { nodesc = (desc != null) ? desc : DESC_NONE; } // Sets labels and optional tooltips for each flag protected void setFlagDescriptions(int size, String[] stable, int startOfs, char separator) { table = new String[8*size]; toolTable = new String[8*size]; if (stable != null) { for (int i = startOfs; i < stable.length; i++) { if (stable[i] == null) { stable[i] = ""; } String[] s = null; try { s = stable[i].split(String.valueOf(separator)); } catch (PatternSyntaxException pse) { pse.printStackTrace(); } if (s == null || s.length == 0) { table[i - startOfs] = stable[i]; toolTable[i - startOfs] = null; } else { table[i - startOfs] = s[0]; toolTable[i - startOfs] = (s.length > 1) ? s[1] : null; } } } } }