/* * Copyright (C) 2007 IsmAvatar <IsmAvatar@gmail.com> * Copyright (C) 2009 Quadduc <quadduc@gmail.com> * * This file is part of LateralGM. * LateralGM is free software and comes with ABSOLUTELY NO WARRANTY. * See LICENSE for details. */ package org.lateralgm.subframes; import static javax.swing.GroupLayout.DEFAULT_SIZE; import static javax.swing.GroupLayout.PREFERRED_SIZE; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import javax.swing.GroupLayout; import javax.swing.GroupLayout.Alignment; import javax.swing.GroupLayout.SequentialGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSeparator; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.LayoutStyle.ComponentPlacement; import javax.swing.ListSelectionModel; import org.lateralgm.components.NumberField; import org.lateralgm.components.ResourceMenu; import org.lateralgm.components.impl.EditorScrollPane; import org.lateralgm.components.impl.ResNode; import org.lateralgm.components.visual.PathEditor; import org.lateralgm.components.visual.PathEditor.PPathEditor; import org.lateralgm.main.LGM; import org.lateralgm.main.Prefs; import org.lateralgm.messages.Messages; import org.lateralgm.resources.Path; import org.lateralgm.resources.Path.PPath; import org.lateralgm.resources.Room; import org.lateralgm.resources.sub.PathPoint; import org.lateralgm.resources.sub.PathPoint.PPathPoint; import org.lateralgm.ui.swing.propertylink.FormattedLink; import org.lateralgm.ui.swing.propertylink.PropertyLinkFactory; import org.lateralgm.ui.swing.util.ArrayListModel; import org.lateralgm.util.PropertyLink; import org.lateralgm.util.PropertyMap.PropertyUpdateEvent; import org.lateralgm.util.PropertyMap.PropertyUpdateListener; public class PathFrame extends InstantiableResourceFrame<Path,PPath> { private static final long serialVersionUID = 1L; private JList<PathPoint> list; private NumberField tx, ty, tsp; private NumberField tpr; private JButton add, insert, delete; private JCheckBox smooth, closed; private final PathEditor pathEditor; private final PropertyLinkFactory<PPathEditor> peplf; private final PathEditorPropertyListener pepl = new PathEditorPropertyListener(); public PathFrame(Path res, ResNode node) { super(res,node); pathEditor = new PathEditor(res); pathEditor.properties.updateSource.addListener(pepl); peplf = new PropertyLinkFactory<PPathEditor>(pathEditor.properties,this); GroupLayout layout = new GroupLayout(getContentPane()) { @Override public void layoutContainer(Container parent) { // TODO: The layout of the preview/editor does not need to effect the layout of the // toolbar and preview pane. I feel as though this is grossly overengineered for the path // frame and the room editor. Not to mention the path editor feels clunky anyway. Dimension m = PathFrame.this.getMinimumSize(); Dimension s = PathFrame.this.getSize(); Dimension r = new Dimension(Math.max(m.width,s.width),Math.max(m.height,s.height)); if (!r.equals(s)) PathFrame.this.setSize(r); else super.layoutContainer(parent); } }; setLayout(layout); JToolBar toolbar = makeToolBar(); JPanel side = makeSide(res); JComponent preview = makePreview(); SequentialGroup orientationGroup = layout.createSequentialGroup(); if (Prefs.rightOrientation) { orientationGroup /* */.addComponent(preview,240,640,DEFAULT_SIZE) /* */.addComponent(side,200,DEFAULT_SIZE,200); } else { orientationGroup /* */.addComponent(side,200,DEFAULT_SIZE,200) /* */.addComponent(preview,240,640,DEFAULT_SIZE); } layout.setHorizontalGroup(layout.createParallelGroup() /**/.addComponent(toolbar, PREFERRED_SIZE, PREFERRED_SIZE, Short.MAX_VALUE) /**/.addGroup(orientationGroup)); layout.setVerticalGroup(layout.createSequentialGroup() /**/.addComponent(toolbar,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) /**/.addGroup(layout.createParallelGroup() /* */.addComponent(side) /* */.addComponent(preview,DEFAULT_SIZE,480,DEFAULT_SIZE))); pack(); list.setSelectedIndex(0); } private JToolBar makeToolBar() { JToolBar tool = new JToolBar(); // Using GroupLayout here for baseline alignment support. GroupLayout layout = new GroupLayout(tool); tool.setLayout(layout); tool.setFloatable(false); JLabel lsx = new JLabel(Messages.getString("PathFrame.SNAP_X")); NumberField sx = new NumberField(1,999); plf.make(sx,PPath.SNAP_X); JLabel lsy = new JLabel(Messages.getString("PathFrame.SNAP_Y")); NumberField sy = new NumberField(1,999); plf.make(sy,PPath.SNAP_Y); // For some reason, JToolBar + GroupLayout makes the button too small to show all the text. // Using a JCheckBox instead. This also mathces the other components better. JToggleButton grid = new JToggleButton(); grid.setToolTipText(Messages.getString("PathFrame.GRID")); grid.setActionCommand("PathFrame.GRID"); grid.setIcon(LGM.getIconForKey("PathFrame.GRID")); peplf.make(grid,PPathEditor.SHOW_GRID); ResourceMenu<Room> room = new ResourceMenu<Room>(Room.class, Messages.getString("PathFrame.NO_ROOM"),160); plf.make(room,PPath.BACKGROUND_ROOM); JSeparator saveSep = new JSeparator(JSeparator.VERTICAL); layout.setHorizontalGroup(layout.createSequentialGroup() /**/.addComponent(save) /**/.addPreferredGap(ComponentPlacement.RELATED) /**/.addComponent(saveSep,PREFERRED_SIZE,PREFERRED_SIZE,PREFERRED_SIZE) /**/.addPreferredGap(ComponentPlacement.RELATED) /**/.addComponent(lsx).addComponent(sx,PREFERRED_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) /**/.addPreferredGap(ComponentPlacement.RELATED) /**/.addComponent(lsy).addComponent(sy,PREFERRED_SIZE,DEFAULT_SIZE,PREFERRED_SIZE) /**/.addPreferredGap(ComponentPlacement.RELATED) /**/.addComponent(grid) /**/.addPreferredGap(ComponentPlacement.RELATED) /**/.addComponent(room,DEFAULT_SIZE,DEFAULT_SIZE,PREFERRED_SIZE).addContainerGap()); layout.setVerticalGroup(layout.createBaselineGroup(false,false) /**/.addComponent(save) /**/.addComponent(saveSep) /**/.addComponent(lsx).addComponent(sx) /**/.addComponent(lsy).addComponent(sy) /**/.addComponent(grid).addComponent(room)); return tool; } private JPanel makeSide(Path res) { JPanel side1 = new JPanel(null); GroupLayout layout = new GroupLayout(side1); side1.setLayout(layout); final JLabel lName = new JLabel(Messages.getString("PathFrame.NAME")); //$NON-NLS-1$ list = new JList<PathPoint>(new ArrayListModel<PathPoint>(res.points)); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); peplf.make(list,PPathEditor.SELECTED_POINT); list.setFont(new Font("Monospaced",Font.PLAIN,10)); //$NON-NLS-1$ list.setVisibleRowCount(5); JScrollPane p = new JScrollPane(list); JLabel lx = new JLabel(Messages.getString("PathFrame.X")); //$NON-NLS-1$ tx = new NumberField(0); tx.setColumns(5); add = new JButton(Messages.getString("PathFrame.ADD")); //$NON-NLS-1$ add.addActionListener(this); JLabel ly = new JLabel(Messages.getString("PathFrame.Y")); //$NON-NLS-1$ ty = new NumberField(0); ty.setColumns(4); insert = new JButton(Messages.getString("PathFrame.INSERT")); //$NON-NLS-1$ insert.addActionListener(this); JLabel lsp = new JLabel(Messages.getString("PathFrame.SP")); //$NON-NLS-1$ tsp = new NumberField(0,1000000,100); tsp.setColumns(5); delete = new JButton(Messages.getString("PathFrame.DELETE")); //$NON-NLS-1$ delete.addActionListener(this); smooth = new JCheckBox(Messages.getString("PathFrame.SMOOTH")); //$NON-NLS-1$ plf.make(smooth,PPath.SMOOTH); closed = new JCheckBox(Messages.getString("PathFrame.CLOSED")); //$NON-NLS-1$ plf.make(closed,PPath.CLOSED); JLabel lpr = new JLabel(Messages.getString("PathFrame.PRECISION")); //$NON-NLS-1$ tpr = new NumberField(1,8); plf.make(tpr,PPath.PRECISION); layout.setAutoCreateContainerGaps(true); layout.setAutoCreateGaps(true); layout.setHorizontalGroup(layout.createParallelGroup() /**/.addGroup(layout.createSequentialGroup() /* */.addComponent(lName).addComponent(name)) /**/.addGroup(layout.createSequentialGroup() /* */.addComponent(smooth).addComponent(closed)) /**/.addGroup(layout.createSequentialGroup() /* */.addComponent(lpr).addComponent(tpr)) /**/.addComponent(p) /**/.addGroup(layout.createSequentialGroup() /* */.addComponent(add,DEFAULT_SIZE,DEFAULT_SIZE,Integer.MAX_VALUE) /* */.addComponent(insert,DEFAULT_SIZE,DEFAULT_SIZE,Integer.MAX_VALUE)) /**/.addComponent(delete,DEFAULT_SIZE,DEFAULT_SIZE,Integer.MAX_VALUE) /**/.addGroup(layout.createSequentialGroup() /* */.addGroup(layout.createParallelGroup(Alignment.TRAILING) /* */.addComponent(lx).addComponent(ly).addComponent(lsp)) /* */.addGroup(layout.createParallelGroup() /* */.addComponent(tx).addComponent(ty).addComponent(tsp)))); layout.setVerticalGroup(layout.createSequentialGroup() /**/.addGroup(layout.createBaselineGroup(true,false) /* */.addComponent(lName).addComponent(name)) /**/.addGroup(layout.createBaselineGroup(true,false) /* */.addComponent(smooth).addComponent(closed)) /**/.addGroup(layout.createBaselineGroup(true,false) /* */.addComponent(lpr).addComponent(tpr)) /**/.addComponent(p,PREFERRED_SIZE,DEFAULT_SIZE,DEFAULT_SIZE) /**/.addGroup(layout.createBaselineGroup(true,false) /* */.addComponent(add).addComponent(insert)) /**/.addComponent(delete) /**/.addGroup(layout.createBaselineGroup(true,false) /* */.addComponent(lx).addComponent(tx)) /**/.addGroup(layout.createBaselineGroup(true,false) /* */.addComponent(ly).addComponent(ty)) /**/.addGroup(layout.createBaselineGroup(true,false) /* */.addComponent(lsp).addComponent(tsp))); return side1; } private JComponent makePreview() { //include a status bar return new EditorScrollPane(pathEditor); } protected boolean areResourceFieldsEqual() { return res.points.equals(resOriginal.points); } public void commitChanges() { res.setName(name.getText()); } @Override public Dimension getMinimumSize() { Dimension p = getContentPane().getSize(); Dimension l = getContentPane().getMinimumSize(); Dimension s = getSize(); l.width += s.width - p.width; l.height += s.height - p.height; return l; } //Button was clicked public void actionPerformed(ActionEvent e) { Object s = e.getSource(); if (s == add) { res.points.add(new PathPoint((Integer) tx.getValue(),(Integer) ty.getValue(), (Integer) tsp.getValue())); list.setSelectedIndex(res.points.size() - 1); } if (s == insert) { int i = list.getSelectedIndex(); if (i == -1) return; res.points.add(i, new PathPoint((Integer) tx.getValue(),(Integer) ty.getValue(),(Integer) tsp.getValue())); list.setSelectedIndex(i); } if (s == delete) { int i = list.getSelectedIndex(); Object o = list.getSelectedValue(); if (o == null) return; res.points.remove(o); if (i >= res.points.size()) i = res.points.size() - 1; list.setSelectedIndex(i); } super.actionPerformed(e); } FormattedLink<PPathPoint> ltx, lty, ltsp; private class PathEditorPropertyListener extends PropertyUpdateListener<PPathEditor> { @Override public void updated(PropertyUpdateEvent<PPathEditor> e) { switch (e.key) { case SELECTED_POINT: PropertyLink.removeAll(ltx,lty,ltsp); PathPoint pp = e.map.get(e.key); if (pp != null) { PropertyLinkFactory<PPathPoint> ppplf = new PropertyLinkFactory<PPathPoint>( pp.properties,null); ltx = ppplf.make(tx,PPathPoint.X); lty = ppplf.make(ty,PPathPoint.Y); ltsp = ppplf.make(tsp,PPathPoint.SPEED); } else { ltx = null; lty = null; ltsp = null; } break; default: break; } } } }