/**
* Copyright 2014
* SMEdit https://github.com/StarMade/SMEdit
* SMTools https://github.com/StarMade/SMTools
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
**/
package jo.sm.ui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import static javax.swing.GroupLayout.DEFAULT_SIZE;
import static javax.swing.GroupLayout.PREFERRED_SIZE;
import javax.swing.JDialog;
import javax.swing.JFrame;
import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
import jo.sm.data.BlockTypes;
import jo.sm.data.CubeIterator;
import jo.sm.data.RenderPoly;
import jo.sm.data.SparseMatrix;
import jo.sm.logic.StarMadeLogic;
import jo.sm.ship.data.Block;
import jo.util.Paths;
import jo.vecmath.Point3i;
@SuppressWarnings("serial")
public class EditPanel extends JPanel {
private static final Logger log = Logger.getLogger(EditPanel.class.getName());
private boolean mPainting;
private final RenderPanel mRenderer;
/* For main construct window */
private JLabel mSymLabel;
private JLabel mCheckLabel;
private JLabel mColor;
private JLabel mCurrent;
private JLabel mChoice;
private JButton mGrey;
private JButton mBlack;
private JButton mRed;
private JButton mPurple;
private JButton mBlue;
private JButton mGreen;
private JButton mBrown;
private JButton mYellow;
private JButton mWhite;
private JButton mClear;
private JSpinner mRadius;
private JLabel mRaLabel;
private JButton mAll;
private JCheckBox mXSymmetry;
private JCheckBox mYSymmetry;
private JCheckBox mZSymmetry;
/* For tile properties dialog */
private JDialog propertiesDialog;
private JLabel tileImg;
public EditPanel(RenderPanel renderer, JFrame dialogOwner) {
mRenderer = renderer;
initComponents();
createPropertiesDialog(dialogOwner);
// link
MouseAdapter ma = new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
doMouseClick(e.getX(), e.getY());
}
@Override
public void mousePressed(MouseEvent ev) {
if (ev.getButton() == MouseEvent.BUTTON3) {
mPainting = true;
}
}
@Override
public void mouseReleased(MouseEvent ev) {
if (ev.getButton() == MouseEvent.BUTTON3) {
mPainting = false;
}
}
@Override
public void mouseDragged(MouseEvent ev) {
if (mPainting) {
doMouseClick(ev.getX(), ev.getY());
}
}
};
mRenderer.addMouseListener(ma);
mRenderer.addMouseMotionListener(ma);
mClear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doColorClick(null, (short) -1);
}
});
mAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doColorAll();
}
});
}
private void initComponents() {
mColor = new JLabel("Current Color:");
mCurrent = new JLabel("None");
mChoice = new JLabel("Color Choices:");
mGrey = newButton(BlockTypes.HULL_COLOR_GREY_ID);
mBlack = newButton(BlockTypes.HULL_COLOR_BLACK_ID);
mRed = newButton(BlockTypes.HULL_COLOR_RED_ID);
mPurple = newButton(BlockTypes.HULL_COLOR_PURPLE_ID);
mBlue = newButton(BlockTypes.HULL_COLOR_BLUE_ID);
mGreen = newButton(BlockTypes.HULL_COLOR_GREEN_ID);
mBrown = newButton(BlockTypes.HULL_COLOR_BROWN_ID);
mYellow = newButton(BlockTypes.HULL_COLOR_YELLOW_ID);
mWhite = newButton(BlockTypes.HULL_COLOR_WHITE_ID);
mRadius = new JSpinner(new SpinnerNumberModel(1, 1, 64, 1));
mSymLabel = new JLabel("Paint Symmetry:");
mCheckLabel = new JLabel(" X Y Z");
mRaLabel = new JLabel("Paint Radius:");
mXSymmetry = new JCheckBox();
mXSymmetry.setToolTipText("paint port/starboard");
mYSymmetry = new JCheckBox();
mYSymmetry.setToolTipText("paint dorsal/ventral");
mZSymmetry = new JCheckBox();
mZSymmetry.setToolTipText("paint fore/aft");
mClear = getDefaultButton("Stop painting", new ImageIcon(Paths.getIconDirectory() + "/stop.png")); //clear
mAll = getDefaultButton("Set all hulls to current color", new ImageIcon(Paths.getIconDirectory() + "/fill.png")); // paint all
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(mXSymmetry)
.addComponent(mYSymmetry)
.addComponent(mZSymmetry))
.addGroup(layout.createParallelGroup(Alignment.LEADING)
.addComponent(mColor)
.addComponent(mCurrent)
.addComponent(mChoice)
.addGroup(layout.createSequentialGroup()
.addComponent(mGrey)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mBlack))
.addGroup(layout.createSequentialGroup()
.addComponent(mRed)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mPurple))
.addGroup(layout.createSequentialGroup()
.addComponent(mBlue)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mGreen))
.addGroup(layout.createSequentialGroup()
.addComponent(mBrown)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mYellow))
.addGroup(layout.createSequentialGroup()
.addComponent(mWhite))
.addComponent(mRadius, PREFERRED_SIZE, 90, PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(mClear)
.addPreferredGap(RELATED)
.addComponent(mAll))
.addComponent(mSymLabel)
.addComponent(mRaLabel)
.addComponent(mCheckLabel)))
.addGap(10, 10, 10))
);
layout.setVerticalGroup(
layout.createParallelGroup(Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(mColor)
.addComponent(mCurrent, PREFERRED_SIZE, 32, PREFERRED_SIZE)
.addPreferredGap(RELATED)
.addComponent(mChoice)
.addPreferredGap(RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(mGrey)
.addComponent(mBlack))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(mRed)
.addComponent(mPurple))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(mBlue)
.addComponent(mGreen))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(mBrown)
.addComponent(mYellow))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(mWhite))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addPreferredGap(RELATED)
.addComponent(mRaLabel)
.addPreferredGap(RELATED)
.addComponent(mRadius, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
.addPreferredGap(RELATED)
.addComponent(mSymLabel)
.addPreferredGap(RELATED)
.addComponent(mCheckLabel)
.addPreferredGap(RELATED)
.addGroup(layout.createParallelGroup(Alignment.BASELINE)
.addComponent(mXSymmetry)
.addComponent(mYSymmetry)
.addComponent(mZSymmetry))
.addGroup(layout.createParallelGroup(Alignment.BASELINE)
.addComponent(mClear)
.addComponent(mAll)))
);
}
private void doColorClick(Icon color, short blockID) {
StarMadeLogic.getInstance().setSelectedBlockType(blockID);
if (StarMadeLogic.getInstance().getSelectedBlockType() == -1) {
mCurrent.setIcon(null);
mCurrent.setText("None");
} else {
mCurrent.setIcon(color);
mCurrent.setText("");
}
}
private void doColorAll() {
if (StarMadeLogic.getInstance().getSelectedBlockType() < 0) {
return;
}
SparseMatrix<Block> grid = StarMadeLogic.getModel();
Iterator<Point3i> i;
if ((StarMadeLogic.getInstance().getSelectedLower() != null) && (StarMadeLogic.getInstance().getSelectedUpper() != null)) {
i = new CubeIterator(StarMadeLogic.getInstance().getSelectedLower(), StarMadeLogic.getInstance().getSelectedUpper());
} else {
i = grid.iteratorNonNull();
}
colorByIterator(grid, i, false);
}
private void colorByIterator(SparseMatrix<Block> grid, Iterator<Point3i> i, boolean symmetric) {
List<Point3i> coords = new ArrayList<>();
while (i.hasNext()) {
coords.clear();
coords.add(i.next());
if (symmetric) {
if (mXSymmetry.isSelected()) {
for (int j = coords.size() - 1; j >= 0; j--) {
Point3i p1 = coords.get(j);
if (p1.x != 8) {
coords.add(new Point3i(16 - p1.x, p1.y, p1.z));
}
}
}
if (mYSymmetry.isSelected()) {
for (int j = coords.size() - 1; j >= 0; j--) {
Point3i p1 = coords.get(j);
if (p1.y != 8) {
coords.add(new Point3i(p1.x, 16 - p1.y, p1.z));
}
}
}
if (mZSymmetry.isSelected()) {
for (int j = coords.size() - 1; j >= 0; j--) {
Point3i p1 = coords.get(j);
if (p1.z != 8) {
coords.add(new Point3i(p1.x, p1.y, 16 - p1.z));
}
}
}
}
for (Point3i c : coords) {
paintBlock(grid, c);
}
}
mRenderer.repaint();
}
private void paintBlock(SparseMatrix<Block> grid, Point3i coords) {
Block block = grid.get(coords);
if (block == null) {
return;
}
short newID = BlockTypes.getColoredBlock(block.getBlockID(), StarMadeLogic.getInstance().getSelectedBlockType());
if (newID != -1) {
block.setBlockID(newID);
}
}
private void doMouseClick(int x, int y) {
if (StarMadeLogic.getInstance().getSelectedBlockType() < 0) {
return;
}
RenderPoly b = mRenderer.getTileAt(x, y);
if (b == null) {
return;
}
SparseMatrix<Block> grid = StarMadeLogic.getModel();
Point3i p = b.getPosition();
if (p == null) {
return;
}
int r = (Integer) mRadius.getValue() - 1;
Point3i lower = new Point3i(p.x - r, p.y - r, p.z - r);
Point3i upper = new Point3i(p.x + r, p.y + r, p.z + r);
colorByIterator(grid, new CubeIterator(lower, upper), true);
}
/**
* Makes a JButton with the given icon and tooltop. If the icon cannot be
* loaded, then the text will be used instead.
*
* Adds this RenderFame as an actionListener.
*
* @return a shiny new JButton
*
*/
private JButton getDefaultButton(final String tip, final ImageIcon i) {
final JButton button = new JButton();
button.setToolTipText(tip);
button.setIcon(i);
button.setFocusable(false);
button.setMargin(new Insets(5, 5, 5, 5));
button.setPreferredSize(new Dimension(32, 32));
button.setMaximumSize(new Dimension(32, 32));
return button;
}
private JButton newButton(final short blockID) {
ImageIcon rawImage = BlockTypeColors.getBlockImage(blockID);
final Image image = rawImage.getImage().getScaledInstance(26, 26, Image.SCALE_DEFAULT);
final JButton btn = new JButton(new ImageIcon(image));
btn.setMargin(new Insets(2, 2, 2, 2));
btn.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
if (SwingUtilities.isRightMouseButton(e)) {
showProperties(blockID);
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
});
btn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
doColorClick(btn.getIcon(), blockID);
}
});
return btn;
}
void createPropertiesDialog(JFrame dialogOwner) {
/* Setup for the proeprties dialog */
propertiesDialog = new JDialog(dialogOwner, "Block Properties");
propertiesDialog.setSize(100, 100); //to set location better
propertiesDialog.setLocationRelativeTo(null);
tileImg = new JLabel();
tileImg.setHorizontalAlignment(SwingConstants.CENTER);
tileImg.setBorder(new TitledBorder("Image"));
JPanel cp = (JPanel) propertiesDialog.getContentPane();
cp.setLayout(new BorderLayout());
JPanel p2 = new JPanel(new BorderLayout());
cp.add(p2, BorderLayout.CENTER);
cp.add(tileImg, BorderLayout.NORTH);
JPanel btns1 = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.HORIZONTAL;
c.insets = new Insets(3, 3, 3, 3);
c.gridx = 0;
c.gridy = 0;
btns1.add(new JLabel("ID"), c);
c.gridx = 1;
c.ipadx = 30;
c.fill = GridBagConstraints.NONE;
c.anchor = GridBagConstraints.WEST;
btns1.add(new JLabel("ID"), c);
c.ipadx = 0;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridx = 0;
c.gridy = 1;
btns1.add(new JLabel("Type"), c);
c.gridx = 1;
btns1.add(new JLabel("Type"), c);
c.gridx = 0;
c.gridy = 2;
btns1.add(new JLabel("Name"), c);
c.gridx = 1;
btns1.add(new JLabel("Name"), c);
c.gridx = 0;
c.gridy = 3;
c.gridx = 1;
p2.add(btns1, BorderLayout.NORTH);
c.gridx = 0;
c.gridy = 4;
c.gridwidth = 2;
propertiesDialog.setSize(100, 100);
propertiesDialog.setResizable(false);
}
void showProperties(final short blockID) {
ImageIcon rawImage = BlockTypeColors.getBlockImage(blockID);
final Image image = rawImage.getImage().getScaledInstance(64, 64, Image.SCALE_DEFAULT);
tileImg.setIcon(new ImageIcon(image));
propertiesDialog.pack();
propertiesDialog.setVisible(true);
}
}