Files
ATCS/src/com/gpl/rpg/atcontentstudio/ui/sprites/SpritesheetEditor.java

440 lines
17 KiB
Java

package com.gpl.rpg.atcontentstudio.ui.sprites;
import com.gpl.rpg.atcontentstudio.ATContentStudio;
import com.gpl.rpg.atcontentstudio.Notification;
import com.gpl.rpg.atcontentstudio.model.GameDataElement;
import com.gpl.rpg.atcontentstudio.model.ProjectTreeNode;
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
import com.gpl.rpg.atcontentstudio.model.sprites.Spritesheet;
import com.gpl.rpg.atcontentstudio.ui.DefaultIcons;
import com.gpl.rpg.atcontentstudio.ui.Editor;
import com.gpl.rpg.atcontentstudio.ui.FieldUpdateListener;
import com.gpl.rpg.atcontentstudio.ui.ListenerListModel;
import com.gpl.rpg.atcontentstudio.utils.DesktopIntegration;
import com.jidesoft.swing.JideBoxLayout;
import com.jidesoft.swing.JideTabbedPane;
import javax.swing.*;
import javax.swing.event.ListDataListener;
import javax.swing.event.TableModelListener;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.event.*;
import java.util.List;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
public class SpritesheetEditor extends Editor {
private static final long serialVersionUID = 3956109815682889863L;
Map<String, JPanel> editorTabs = new LinkedHashMap<String, JPanel>();
JideTabbedPane editorTabsHolder;
private JSpinner widthField;
private JSpinner heightField;
private JCheckBox animatedBox;
@SuppressWarnings("rawtypes")
private JComboBox categoryBox;
private JPanel spriteViewPane;
public static JComponent getWarningLabel() {
JLabel label = new JLabel(
"<html><i>" +
"The data accompamying the image here is not part of the game.<br/>" +
"What you change here will be changed in your ATCS project only.<br/>" +
"None of this is exported to JSON or TMX, although it must be set correctly in order to choose tiles & icons correctly.<br/>" +
"</i></html>");
return label;
}
public SpritesheetEditor(Spritesheet sheet) {
super();
this.name = sheet.id;
this.target = sheet;
Image icon = sheet.getIcon(0);
if (icon == null) {
Notification.addError("Unable to open spritesheet " + sheet.id + " because the image is missing. This might be because its an UI spritesheet, which is not supported in ATCS");
return;
}
this.icon = new ImageIcon(icon);
JPanel pane = new JPanel();
final FieldUpdateListener listener = new SpritesheetFieldUpdater();
pane.setLayout(new JideBoxLayout(pane, JideBoxLayout.PAGE_AXIS, 6));
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new JideBoxLayout(buttonPane, JideBoxLayout.LINE_AXIS));
JButton openImage = new JButton(new ImageIcon(DefaultIcons.getTileLayerImage()));
openImage.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
DesktopIntegration.openImage(((Spritesheet) target).spritesheetFile);
}
});
buttonPane.add(openImage, JideBoxLayout.FIX);
buttonPane.add(getWarningLabel(), JideBoxLayout.FIX);
final JButton bookmark = new JButton(new ImageIcon(sheet.bookmark != null ? DefaultIcons.getBookmarkActiveIcon() : DefaultIcons.getBookmarkInactiveIcon()));
buttonPane.add(bookmark, JideBoxLayout.FIX);
bookmark.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
if (target.bookmark == null) {
target.getProject().bookmark(target);
bookmark.setIcon(new ImageIcon(DefaultIcons.getBookmarkActiveIcon()));
} else {
target.bookmark.delete();
bookmark.setIcon(new ImageIcon(DefaultIcons.getBookmarkInactiveIcon()));
}
}
});
buttonPane.add(new JPanel(), JideBoxLayout.VARY);
pane.add(buttonPane, JideBoxLayout.FIX);
addLabelField(pane, "Spritesheet ID: ", sheet.id);
addLabelField(pane, "File: ", sheet.spritesheetFile.getAbsolutePath());
widthField = addIntegerField(pane, "Sprite width (px): ", sheet.spriteWidth, false, true, listener);
heightField = addIntegerField(pane, "Sprite height (px): ", sheet.spriteHeight, false, true, listener);
animatedBox = addBooleanBasedCheckBox(pane, "Is an animation", sheet.animated, true, listener);
categoryBox = addEnumValueBox(pane, "Category: ", Spritesheet.Category.values(), sheet.category, true, listener);
spriteViewPane = new JPanel();
updateView(spriteViewPane);
pane.add(spriteViewPane, JideBoxLayout.FIX);
addBacklinksList(pane, sheet);
//Placeholder. Fills the eventual remaining space.
pane.add(new JPanel(), JideBoxLayout.VARY);
setLayout(new BorderLayout());
editorTabsHolder = new JideTabbedPane(JideTabbedPane.BOTTOM);
editorTabsHolder.setTabShape(JideTabbedPane.SHAPE_FLAT);
editorTabsHolder.setUseDefaultShowCloseButtonOnTab(false);
editorTabsHolder.setShowCloseButtonOnTab(false);
add(editorTabsHolder, BorderLayout.CENTER);
JScrollPane sheetScroller = new JScrollPane(pane);
sheetScroller.getVerticalScrollBar().setUnitIncrement(16);
editorTabsHolder.add("Spritesheet", sheetScroller);
JScrollPane rawScroller = new JScrollPane(new JLabel(new ImageIcon(sheet.spritesheet)));
rawScroller.getVerticalScrollBar().setUnitIncrement(16);
editorTabsHolder.add("Raw image", rawScroller);
}
private Thread animator = new Thread();
private boolean animate = true;
private JLabel iconLabel;
private List<ImageIcon> icons = null;
public void updateView(JPanel pane) {
Spritesheet sheet = (Spritesheet) target;
pane.removeAll();
pane.setLayout(new BorderLayout());
if (sheet.animated) {
iconLabel = new JLabel();
iconLabel.setBackground(Color.WHITE);
if (icons == null) {
icons = new ArrayList<ImageIcon>();
} else {
icons.clear();
}
int i = 0;
Image img;
while ((img = sheet.getImage(i++)) != null) {
icons.add(new ImageIcon(img));
}
if (i > 0) {
iconLabel.setIcon(icons.get(0));
}
pane.add(iconLabel, BorderLayout.CENTER);
resetAnimator();
} else {
JTable spritesTable = new JTable(new SpritesheetTableModel(sheet));
spritesTable.setDefaultRenderer(Image.class, new SpritesheetCellRenderer(sheet));
spritesTable.setCellSelectionEnabled(true);
spritesTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
spritesTable.getTableHeader().setVisible(false);
Enumeration<TableColumn> columns = spritesTable.getColumnModel().getColumns();
TableColumn col;
while (columns.hasMoreElements()) {
col = columns.nextElement();
col.setMinWidth(sheet.spriteWidth + 4);
col.setMaxWidth(sheet.spriteWidth + 4);
}
spritesTable.setRowHeight(sheet.spriteHeight + 4);
pane.add(new JScrollPane(spritesTable), BorderLayout.CENTER);
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
public static JList addBacklinksList(JPanel pane, Spritesheet sheet) {
final JList list = new JList(new SpritesheetsBacklinksListModel(sheet));
list.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if (e.getClickCount() == 2) {
if (list.getSelectedValue() instanceof TMXMap) {
ATContentStudio.frame.openEditor((TMXMap) list.getSelectedValue());
ATContentStudio.frame.selectInTree((TMXMap) list.getSelectedValue());
} else if (list.getSelectedValue() instanceof GameDataElement) {
ATContentStudio.frame.openEditor((GameDataElement) list.getSelectedValue());
ATContentStudio.frame.selectInTree((GameDataElement) list.getSelectedValue());
}
}
}
});
list.addKeyListener(new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
if (list.getSelectedValue() instanceof TMXMap) {
ATContentStudio.frame.openEditor((TMXMap) list.getSelectedValue());
ATContentStudio.frame.selectInTree((TMXMap) list.getSelectedValue());
} else if (list.getSelectedValue() instanceof GameDataElement) {
ATContentStudio.frame.openEditor((GameDataElement) list.getSelectedValue());
ATContentStudio.frame.selectInTree((GameDataElement) list.getSelectedValue());
}
}
}
});
list.setCellRenderer(new BacklinkCellRenderer(true));
JScrollPane scroller = new JScrollPane(list);
scroller.setBorder(BorderFactory.createTitledBorder("Elements pointing to this spritesheet."));
pane.add(scroller, JideBoxLayout.FIX);
return list;
}
public static class SpritesheetTableModel implements TableModel {
Spritesheet sheet;
public SpritesheetTableModel(Spritesheet sheet) {
this.sheet = sheet;
}
@Override
public int getRowCount() {
return (sheet.spritesheet.getHeight() / sheet.spriteHeight) + ((sheet.spritesheet.getHeight() % sheet.spriteHeight) == 0 ? 0 : 1);
}
@Override
public int getColumnCount() {
return (sheet.spritesheet.getWidth() / sheet.spriteWidth) + ((sheet.spritesheet.getWidth() % sheet.spriteWidth) == 0 ? 0 : 1);
}
@Override
public String getColumnName(int columnIndex) {
return "";
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return Image.class;
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return sheet.getImage((rowIndex * getColumnCount()) + columnIndex);
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
}
List<TableModelListener> listeners = new CopyOnWriteArrayList<TableModelListener>();
@Override
public void addTableModelListener(TableModelListener l) {
listeners.add(l);
}
@Override
public void removeTableModelListener(TableModelListener l) {
listeners.remove(l);
}
}
public static class SpritesheetCellRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = -4213756343124247612L;
Spritesheet sheet;
public SpritesheetCellRenderer(Spritesheet sheet) {
super();
this.sheet = sheet;
}
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (c instanceof JLabel) {
((JLabel) c).setText("");
if (value != null) {
((JLabel) c).setIcon(new ImageIcon((Image) value));
((JLabel) c).setToolTipText(sheet.id + ":" + ((row * table.getColumnCount()) + column));
}
}
return c;
}
}
@Override
public void setVisible(boolean aFlag) {
super.setVisible(aFlag);
animate = aFlag;
if (aFlag && animator != null) {
resetAnimator();
}
}
private void resetAnimator() {
new Thread() {
public void run() {
if (animator != null && animator.isAlive()) {
try {
animator.join();
} catch (InterruptedException e) {
}
}
animate = true;
animator = new Thread() {
public void run() {
int i = -1;
while (animate) {
if (icons != null) {
synchronized (icons) {
i = (i + 1) % icons.size();
iconLabel.setIcon(icons.get(i));
}
iconLabel.revalidate();
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
}
}
};
animator.start();
}
}.start();
}
public static class SpritesheetsBacklinksListModel implements ListenerListModel<ProjectTreeNode> {
Spritesheet sheet;
public SpritesheetsBacklinksListModel(Spritesheet sheet) {
this.sheet = sheet;
}
@Override
public int getSize() {
return sheet.getBacklinks().size();
}
@Override
public ProjectTreeNode getElementAt(int index) {
for (ProjectTreeNode node : sheet.getBacklinks()) {
if (index == 0) return node;
index--;
}
return null;
}
List<ListDataListener> listeners = new CopyOnWriteArrayList<ListDataListener>();
@Override
public List<ListDataListener> getListeners() {
return listeners;
}
}
public static class BacklinkCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 6819681566800482793L;
private boolean includeType;
public BacklinkCellRenderer(boolean includeType) {
super();
this.includeType = includeType;
}
@Override
public Component getListCellRendererComponent(@SuppressWarnings("rawtypes") JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (value == null) {
label.setText("none");
} else {
if (includeType && ((ProjectTreeNode) value).getDataType() != null) {
label.setText(((ProjectTreeNode) value).getDataType().toString() + "/" + ((ProjectTreeNode) value).getDesc());
} else {
label.setText(((ProjectTreeNode) value).getDesc());
}
if (((ProjectTreeNode) value).getIcon() == null) {
Notification.addError("Unable to find icon for " + ((ProjectTreeNode) value).getDesc());
} else {
label.setIcon(new ImageIcon(((ProjectTreeNode) value).getIcon()));
}
}
return label;
}
}
@Override
public void targetUpdated() {
this.icon = new ImageIcon(((Spritesheet) target).getIcon(0));
this.name = ((Spritesheet) target).id;
}
public class SpritesheetFieldUpdater implements FieldUpdateListener {
@Override
public void valueChanged(JComponent source, Object value) {
Spritesheet sheet = (Spritesheet) target;
if (source == widthField) {
sheet.spriteWidth = (Integer) value;
sheet.clearCache();
updateView(spriteViewPane);
spriteViewPane.revalidate();
spriteViewPane.repaint();
} else if (source == heightField) {
sheet.spriteHeight = (Integer) value;
sheet.clearCache();
updateView(spriteViewPane);
spriteViewPane.revalidate();
spriteViewPane.repaint();
} else if (source == animatedBox) {
sheet.animated = (Boolean) value;
if (!sheet.animated) {
animate = false;
}
updateView(spriteViewPane);
spriteViewPane.revalidate();
spriteViewPane.repaint();
} else if (source == categoryBox) {
sheet.category = (Spritesheet.Category) value;
}
sheet.save();
}
}
}