package gui;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.DefaultListModel;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JComboBox;
import javax.swing.JTextField;
import puzzledice.CombinePuzzleBlock;
import puzzledice.DoorUnlockBlock;
import puzzledice.FilterBlock;
import puzzledice.InsertionPuzzleBlock;
import puzzledice.ItemRequestPuzzleBlock;
import puzzledice.ORBlock;
import puzzledice.PropertyChangePuzzleBlock;
import puzzledice.PuzzleBlock;
import puzzledice.SpawnPuzzleBlock;
import puzzledice.OutputBlock;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.ListSelectionEvent;
import com.mxgraph.model.mxCell;
import com.mxgraph.view.mxGraph;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
public class PuzzleEditPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private static final String[] puzzleBlockTypes = {"Output", "Spawn Puzzle", "Combine Puzzle", "Property Change Puzzle", "Item Request Puzzle", "Insertion Puzzle", "Filter", "OR Block", "Door Unlock Puzzle"};
private final JTextField txtBlockName;
private final JComboBox puzzleTypeSelect;
private static JList blockList;
private final static DefaultListModel blockListModel = new DefaultListModel();
private final JButton btnChangeBlock;
private int selectedBlockIndex;
private PuzzleBlock selectedBlock;
// Used for generating the textual descriptions
private static int nextItemNumber = 0;
private static int nextNPCNumber = 0;
/**
* Create the panel.
*/
public PuzzleEditPanel() {
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
JPanel panel_1 = new JPanel();
add(panel_1);
panel_1.setLayout(new BoxLayout(panel_1, BoxLayout.Y_AXIS));
JPanel panel_2 = new JPanel();
panel_1.add(panel_2);
panel_2.setLayout(new BoxLayout(panel_2, BoxLayout.X_AXIS));
JButton btnNewPuzzle = new JButton("Add");
panel_2.add(btnNewPuzzle);
btnNewPuzzle.setAlignmentX(Component.CENTER_ALIGNMENT);
puzzleTypeSelect = new JComboBox();
puzzleTypeSelect.setModel(new DefaultComboBoxModel(puzzleBlockTypes));
puzzleTypeSelect.setMaximumSize(new Dimension(Integer.MAX_VALUE, btnNewPuzzle.getPreferredSize().height));
panel_2.add(puzzleTypeSelect);
JScrollPane scrollPane = new JScrollPane();
panel_1.add(scrollPane);
blockList = new JList(blockListModel);
scrollPane.setViewportView(blockList);
final JPanel editingPanel = new JPanel();
//editing panel should be invisible until a puzzle is selected
editingPanel.setVisible(false);
add(editingPanel);
editingPanel.setLayout(new BoxLayout(editingPanel, BoxLayout.Y_AXIS));
JButton btnDeletePuzzle = new JButton("Delete Puzzle Block");
btnDeletePuzzle.setAlignmentX(Component.CENTER_ALIGNMENT);
editingPanel.add(btnDeletePuzzle);
final JLabel lblPuzzleType = new JLabel("Type:");
lblPuzzleType.setAlignmentX(Component.CENTER_ALIGNMENT);
editingPanel.add(lblPuzzleType);
JPanel namePanel = new JPanel();
editingPanel.add(namePanel);
namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.X_AXIS));
JLabel lblNameLabel = new JLabel("Block Name:");
namePanel.add(lblNameLabel);
txtBlockName = new JTextField();
txtBlockName.setMaximumSize(new Dimension(Integer.MAX_VALUE, txtBlockName.getPreferredSize().height));
txtBlockName.setText("Block Name");
namePanel.add(txtBlockName);
txtBlockName.setColumns(10);
btnChangeBlock = new JButton("Change");
namePanel.add(btnChangeBlock);
btnChangeBlock.setVisible(false);
txtBlockName.addFocusListener(new FocusAdapter() {
@Override
public void focusLost(FocusEvent arg0) {
maybeChangeBlockName(txtBlockName.getText(), blockList.getSelectedIndex());
}
});
txtBlockName.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
maybeChangeBlockName(txtBlockName.getText(), blockList.getSelectedIndex());
}
});
txtBlockName.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void changedUpdate(DocumentEvent arg0) {
btnChangeBlock.setVisible(true);
}
@Override
public void insertUpdate(DocumentEvent arg0) {
btnChangeBlock.setVisible(true);
}
@Override
public void removeUpdate(DocumentEvent arg0) {
btnChangeBlock.setVisible(true);
}
});
btnChangeBlock.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
maybeChangeBlockName(txtBlockName.getText(), blockList.getSelectedIndex());
}
});
// Add a new puzzle block to the list when
btnNewPuzzle.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
PuzzleBlock blockToAdd;
String puzzleType = (String)puzzleTypeSelect.getSelectedItem();
if (puzzleType.equals("Output"))
blockToAdd = new OutputBlock();
else if (puzzleType.equals("Spawn Puzzle"))
blockToAdd = new SpawnPuzzleBlock();
else if(puzzleType.equals("Combine Puzzle"))
blockToAdd = new CombinePuzzleBlock();
else if(puzzleType.equals("Property Change Puzzle"))
blockToAdd = new PropertyChangePuzzleBlock();
else if(puzzleType.equals("Item Request Puzzle"))
blockToAdd = new ItemRequestPuzzleBlock();
else if(puzzleType.equals("Insertion Puzzle"))
blockToAdd = new InsertionPuzzleBlock();
else if(puzzleType.equals("Filter"))
blockToAdd = new FilterBlock();
else if(puzzleType.equals("OR Block"))
blockToAdd = new ORBlock();
else if(puzzleType.equals("Door Unlock Puzzle"))
blockToAdd = new DoorUnlockBlock();
// If we don't have a constructor for the type yet, silently do nothing.
else
return;
mxGraph puzzleGraph = WindowMain.getPuzzleGraph();
Object parent = puzzleGraph.getDefaultParent();
puzzleGraph.getModel().beginUpdate();
try
{
Object cell = puzzleGraph.insertVertex(parent, null, blockToAdd, 0, 0, 0, 0, blockToAdd.getCellStyle());
mxCell edge = (mxCell)puzzleGraph.insertEdge(parent, null, null, WindowMain.getHierarchyRoot(), cell);
edge.setVisible(false);
blockToAdd.setGraphCell(cell);
puzzleGraph.updateCellSize(cell);
}
finally
{
puzzleGraph.getModel().endUpdate();
WindowMain.updatePuzzleGraph();
}
blockListModel.addElement(blockToAdd);
blockList.setSelectedIndex(blockListModel.getSize()-1);
txtBlockName.setText(blockToAdd.getName());
lblPuzzleType.setText("Type: " + puzzleType);
// Make sure the editing panel is visible before we try to grant one of its members focus
editingPanel.setVisible(true);
txtBlockName.selectAll();
txtBlockName.requestFocusInWindow();
// Set the change block name button invisible until we actually change the text.
btnChangeBlock.setVisible(false);
}
});
blockList.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent arg0) {
if(btnChangeBlock.isVisible())
maybeChangeBlockName(txtBlockName.getText(), selectedBlockIndex);
// remove the custom editing UI if we need to
if(selectedBlock != null && selectedBlock.getUI() != null) {
editingPanel.remove(selectedBlock.getUI());
}
selectedBlockIndex = blockList.getSelectedIndex();
selectedBlock = (PuzzleBlock)blockList.getSelectedValue();
if(selectedBlock != null) {
// update the selected block with any new information
selectedBlock.update();
// add the custom editing UI if we can
if(selectedBlock.getUI() != null) {
editingPanel.add(selectedBlock.getUI());
editingPanel.validate();
editingPanel.repaint();
}
txtBlockName.setText(selectedBlock.getName());
lblPuzzleType.setText("Type: " + selectedBlock.getPuzzleType());
editingPanel.setVisible(true);
// Set the change block button invisible until we actually change the text
btnChangeBlock.setVisible(false);
// for now, here's where we update the textual description
resetTextualDescription();
}
else
editingPanel.setVisible(false);
}
});
btnDeletePuzzle.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
PuzzleBlock blockToDelete = (PuzzleBlock)blockList.getSelectedValue();
// Only delete the block if the user confirms
if (JOptionPane.showConfirmDialog((Component)arg0.getSource(), "Really delete Puzzle Block: " + blockToDelete.getName() + "?", "Confirm Delete", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) {
int newIndex = Math.max(0, blockList.getSelectedIndex()-1);
blockListModel.remove(blockList.getSelectedIndex());
// Now go through the blocks and remove the reference to this block
for(Object o : blockListModel.toArray()) {
PuzzleBlock p = (PuzzleBlock)o;
p.maybeRemoveRef(blockToDelete);
}
// Let the block to its own cleaning if necessary
blockToDelete.onDelete();
// Delete this block from the graph
mxGraph puzzleGraph = WindowMain.getPuzzleGraph();
puzzleGraph.getModel().beginUpdate();
try
{
Object[] cells = new Object[]{blockToDelete.getGraphCell()};
puzzleGraph.removeCells(cells, true);
}
finally
{
puzzleGraph.getModel().endUpdate();
WindowMain.updatePuzzleGraph();
}
// If we can, select a new block from the list
if(blockListModel.size() >= 1)
blockList.setSelectedIndex(newIndex);
}
}
});
}
private void maybeChangeBlockName(String newBlockName, int selectedIndex)
{
PuzzleBlock currentBlock = (PuzzleBlock)blockListModel.get(selectedIndex);
if(newBlockName.equals(currentBlock.getName()))
return;
currentBlock.setName(newBlockName);
blockListModel.set(selectedIndex, currentBlock);
btnChangeBlock.setVisible(false);
mxGraph puzzleGraph = WindowMain.getPuzzleGraph();
puzzleGraph.getModel().beginUpdate();
try
{
puzzleGraph.updateCellSize(currentBlock.getGraphCell());
}
finally
{
puzzleGraph.getModel().endUpdate();
WindowMain.updatePuzzleGraph();
}
}
// This function provides a way for other components to access the current list of puzzle blocks
public static PuzzleBlock[] getBlockList()
{
PuzzleBlock[] retArray = new PuzzleBlock[blockListModel.size()];
blockListModel.copyInto(retArray);
return retArray;
}
// Function to update the currently selected block (used by the AreaEditPanel)
public static void updateCurrentBlock()
{
PuzzleBlock currentBlock = (PuzzleBlock)blockList.getSelectedValue();
if(currentBlock != null)
currentBlock.update();
}
// Used when generating the textual descriptions to create random item names
public static String nextItemName()
{
return "ITEM-" + ++nextItemNumber;
}
public static String nextNPCName()
{
return "NPC-" + ++nextNPCNumber;
}
public static void resetTextualDescription()
{
nextItemNumber = 0;
nextNPCNumber = 0;
PuzzleBlock currentBlock = (PuzzleBlock)blockList.getSelectedValue();
String newDescription = "Full Puzzle Description: ";
if(currentBlock != null)
newDescription += currentBlock.getTextualDescription();
WindowMain.updateTextDescription(newDescription);
}
public void justLoaded()
{
if (blockList.getModel().getSize() > 0)
blockList.setSelectedIndex(0);
WindowMain.updatePuzzleGraph();
}
public void clear()
{
blockListModel.clear();
blockList.setSelectedValue(null, false);
}
// A function to add puzzles to the list
public static void addPuzzle(PuzzleBlock blockToAdd)
{
mxGraph puzzleGraph = WindowMain.getPuzzleGraph();
Object parent = puzzleGraph.getDefaultParent();
puzzleGraph.getModel().beginUpdate();
try
{
Object cell = puzzleGraph.insertVertex(parent, null, blockToAdd, 0, 0, 0, 0, blockToAdd.getCellStyle());
mxCell edge = (mxCell)puzzleGraph.insertEdge(parent, null, null, WindowMain.getHierarchyRoot(), cell);
edge.setVisible(false);
blockToAdd.setGraphCell(cell);
puzzleGraph.updateCellSize(cell);
}
finally
{
puzzleGraph.getModel().endUpdate();
}
blockListModel.addElement(blockToAdd);
}
}