/*
* Copyright (C) 2012 IsmAvatar <IsmAvatar@gmail.com>
*
* This file is part of LibMaker.
*
* LibMaker is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LibMaker is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License (COPYING) for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.lateralgm.libmaker.mockui;
import java.awt.Component;
import java.awt.Frame;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.DefaultListCellRenderer;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextField;
import javax.swing.LayoutStyle;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.lateralgm.libmaker.ActionPreview;
import org.lateralgm.libmaker.Messages;
import org.lateralgm.libmaker.backend.Action;
import org.lateralgm.libmaker.backend.Action.PAction;
import org.lateralgm.libmaker.backend.Library;
import org.lateralgm.libmaker.backend.Library.PLibrary;
import org.lateralgm.libmaker.backend.PropertyMap.PropertyListener;
import org.lateralgm.libmaker.backend.PropertyMap.PropertyUpdateEvent;
import org.lateralgm.libmaker.code.JoshTextArea;
import org.lateralgm.libmaker.components.ListListModel;
import org.lateralgm.libmaker.components.NumberField;
import org.lateralgm.libmaker.components.ObservableList.ListUpdateEvent;
import org.lateralgm.libmaker.components.ObservableList.ListUpdateListener;
import org.lateralgm.libmaker.components.SingleListSelectionModel;
import org.lateralgm.libmaker.uilink.PropertyLink.PLFactory;
public class MockUI extends JSplitPane implements ListSelectionListener,PropertyListener<PAction>,
ListUpdateListener
{
private static final long serialVersionUID = 1L;
Library lib;
ControlPane control;
GeneralPane general;
InterfacePane iface;
//The list is pulled up because we need it to control the other panels
ListListModel<Action> mActions;
JList lActions;
public MockUI()
{
super();
setLeftComponent(control = new ControlPane());
setRightComponent(makeMainPane());
}
private static JButton makeButton(String key, ActionListener listener)
{
JButton but = new JButton(Messages.getString(key),Messages.getIconForKey(key));
but.addActionListener(listener);
return but;
}
class ControlPane extends GroupPanel implements ActionListener
{
private static final long serialVersionUID = 1L;
JTextField tCaption;
NumberField tId;
JButton bCycle, bInfo, bCode;
JButton bAdd, bDel, bUp, bDown;
JCheckBox cbAdvanced;
Library lib;
PLFactory<PLibrary> plf;
protected void initKeyComponents()
{
plf = new PLFactory<PLibrary>();
tCaption = new JTextField();
plf.make(tCaption,PLibrary.CAPTION);
tId = new NumberField(0,1000000);
plf.make(tId,PLibrary.ID);
cbAdvanced = new JCheckBox(Messages.getString("MockUI.ADVANCED")); //$NON-NLS-1$
plf.make(cbAdvanced,PLibrary.ADVANCED);
lActions = new JList(mActions = new ListListModel<Action>());
lActions.setCellRenderer(new ActionListCellRenderer());
//Prototype only used for size calculation, so it doesn't need translating
lActions.setPrototypeCellValue("Sample Action Cell"); //$NON-NLS-1$
lActions.setSelectionModel(new SingleListSelectionModel());
lActions.addListSelectionListener(MockUI.this);
bCycle = new JButton(Messages.getIconForKey("MockUI.CYCLE")); //$NON-NLS-1$
bCycle.setToolTipText(Messages.getString("MockUI.CYCLE_TIP")); //$NON-NLS-1$
bCycle.addActionListener(this);
String key = "MockUI.INFO"; //$NON-NLS-1$
bInfo = new JButton(Messages.getString(key),Messages.getIconForKey(key));
bInfo.addActionListener(this);
key = "MockUI.CODE"; //$NON-NLS-1$
bCode = new JButton(Messages.getString(key),Messages.getIconForKey(key));
bCode.addActionListener(this);
bAdd = makeButton("MockUI.ADD",this); //$NON-NLS-1$
bDel = makeButton("MockUI.DELETE",this); //$NON-NLS-1$
bUp = makeButton("MockUI.UP",this); //$NON-NLS-1$
bDown = makeButton("MockUI.DOWN",this); //$NON-NLS-1$
}
@Override
protected void layoutComponents(GroupLayout layout)
{
initKeyComponents();
JLabel lCaption = new JLabel(Messages.getString("MockUI.CAPTION")); //$NON-NLS-1$
JLabel lId = new JLabel(Messages.getString("MockUI.LIB_ID")); //$NON-NLS-1$
JLabel labActions = new JLabel(Messages.getString("MockUI.ACTIONS")); //$NON-NLS-1$
JScrollPane listScroll = new JScrollPane(lActions);
JPanel listButtons = new JPanel(new GridLayout(2,2));
listButtons.add(bAdd);
listButtons.add(bUp);
listButtons.add(bDel);
listButtons.add(bDown);
int PREF = GroupLayout.PREFERRED_SIZE, DEF = GroupLayout.DEFAULT_SIZE;
layout.setHorizontalGroup(layout.createParallelGroup()
/**/.addComponent(lCaption)
/**/.addComponent(tCaption)
/**/.addComponent(lId)
/**/.addGroup(layout.createSequentialGroup()
/* */.addComponent(tId)
/* */.addComponent(bCycle,16,16,16))
/**/.addComponent(bInfo,PREF,DEF,Short.MAX_VALUE)
/**/.addComponent(bCode,PREF,DEF,Short.MAX_VALUE)
/**/.addComponent(cbAdvanced)
/**/.addComponent(labActions)
/**/.addComponent(listScroll,0,0,Short.MAX_VALUE)
/**/.addComponent(listButtons));
layout.setVerticalGroup(layout.createSequentialGroup()
/**/.addComponent(lCaption)
/**/.addComponent(tCaption,PREF,DEF,PREF)
/**/.addComponent(lId)
/**/.addGroup(layout.createParallelGroup()
/* */.addComponent(tId,PREF,DEF,PREF)
/* */.addComponent(bCycle,16,16,16))
/**/.addComponent(bInfo)
/**/.addComponent(bCode)
/**/.addComponent(cbAdvanced)
/**/.addPreferredGap(LayoutStyle.ComponentPlacement.UNRELATED)
/**/.addComponent(labActions)
/**/.addComponent(listScroll)
/**/.addComponent(listButtons,PREF,DEF,PREF));
}
public void setLibrary(Library lib)
{
this.lib = lib;
plf.setMap(lib.properties);
}
@Override
public void actionPerformed(ActionEvent e)
{
Object s = e.getSource();
if (s == bCycle)
{
tId.setValue(Library.randomId());
return;
}
if (s == bInfo)
{
InfoPane.showInDialog(null,lib);
return;
}
if (s == bCode)
{
String code = lib.get(PLibrary.INIT_CODE);
code = JoshTextArea.showInDialog((Frame) null,"Initialization Code",code);
if (code != null) lib.put(PLibrary.INIT_CODE,code);
return;
}
if (s == bAdd)
{
Action a = new Action();
lib.actions.add(a);
lActions.setSelectedValue(a,true);
}
if (s == bDel)
{
int ind = lActions.getSelectedIndex();
if (ind == -1) return;
lib.actions.remove(ind);
if (ind >= lib.actions.size()) ind = lib.actions.size() - 1;
lActions.setSelectedIndex(ind);
}
if (s == bUp)
{
int ind = lActions.getSelectedIndex();
if (ind < 1) return;
Action a1 = lib.actions.get(ind - 1);
Action a2 = lib.actions.get(ind);
lib.actions.set(ind - 1,a2);
lib.actions.set(ind,a1);
lActions.setSelectedIndex(ind - 1);
}
if (s == bDown)
{
int ind = lActions.getSelectedIndex();
if (ind == -1 || ind > lib.actions.size() - 2) return;
Action a1 = lib.actions.get(ind);
Action a2 = lib.actions.get(ind + 1);
lib.actions.set(ind,a2);
lib.actions.set(ind + 1,a1);
lActions.setSelectedIndex(ind + 1);
}
lActions.updateUI();
}
}
JPanel makeMainPane()
{
general = new GeneralPane();
iface = new InterfacePane();
general.setBorder(BorderFactory.createTitledBorder(Messages.getString("MockUI.GENERAL"))); //$NON-NLS-1$
iface.setBorder(BorderFactory.createTitledBorder(Messages.getString("MockUI.INTERFACE"))); //$NON-NLS-1$
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p,BoxLayout.PAGE_AXIS));
p.add(general);
p.add(iface);
return p;
}
public void previewSelectedAction(Frame parent)
{
Object o = lActions.getSelectedValue();
if (o == null || !(o instanceof Action))
{
JOptionPane.showMessageDialog(parent,Messages.getString("ActionPreview.NO_ACTION"));
return;
}
ActionPreview.showInFrame(parent,(Action) o);
}
public void setLibrary(Library lib)
{
this.lib = lib;
control.setLibrary(lib);
mActions.setList(lib.actions);
lActions.setSelectedIndex(lib.actions.isEmpty() ? -1 : 0);
lib.actions.addListUpdateListener(this);
for (Action a : lib.actions)
a.properties.addPropertyListener(this);
lActions.updateUI();
}
protected static class ActionListCellRenderer extends DefaultListCellRenderer
{
private static final long serialVersionUID = 1L;
public Component getListCellRendererComponent(JList list, Object value, int index,
boolean isSelected, boolean cellHasFocus)
{
//Handle non-actions the default way
if (!(value instanceof Action))
return super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus);
//instead of toString, use our NAME
Action act = ((Action) value);
value = act.get(PAction.NAME);
//generate the label
Component r = super.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus);
//apply our icon (currently, we do this by regenerating an icon each time)
BufferedImage tile = act.get(PAction.IMAGE);
if (tile != null)
setIcon(new ImageIcon(tile.getSubimage(0,0,24,24).getScaledInstance(16,16,0)));
return r;
}
}
public static interface ActionPanel
{
void setAction(Action a);
}
public static abstract class GroupPanel extends JPanel
{
private static final long serialVersionUID = 1L;
public GroupPanel()
{
super();
GroupLayout layout = new GroupLayout(this);
layout.setAutoCreateGaps(true);
layout.setAutoCreateContainerGaps(true);
setLayout(layout);
layoutComponents(layout);
}
protected abstract void layoutComponents(GroupLayout layout);
}
boolean wasSelection = true; //initially true so a change registers the first time
@Override
public void valueChanged(ListSelectionEvent e)
{
Object v = lActions.getSelectedValue();
boolean hasSelection = (v != null && v instanceof Action);
if (hasSelection != wasSelection)
{
wasSelection = hasSelection;
control.bDel.setEnabled(hasSelection);
control.bUp.setEnabled(hasSelection);
control.bDown.setEnabled(hasSelection);
general.setVisible(hasSelection);
}
iface.setVisible(hasSelection
&& ((Action) v).properties.get(PAction.KIND) == Action.Kind.NORMAL);
if (!hasSelection) return;
int ind = lActions.getSelectedIndex();
control.bUp.setEnabled(ind != 0);
control.bDown.setEnabled(ind != lActions.getModel().getSize() - 1);
Action a = (Action) v;
general.setAction(a);
iface.setAction(a);
}
@Override
public void propertyUpdate(PropertyUpdateEvent<PAction> e)
{
switch (e.key)
{
case IMAGE:
case NAME:
lActions.updateUI();
break;
case KIND:
iface.setVisible(e.map.get(PAction.KIND) == Action.Kind.NORMAL);
break;
}
}
@Override
public void listUpdate(ListUpdateEvent evt)
{
if (evt.type != ListUpdateEvent.Type.ADDED) return;
for (int i = evt.fromIndex; i <= evt.toIndex; i++)
lib.actions.get(i).properties.addPropertyListener(this);
lActions.updateUI();
}
}