package com.gpl.rpg.atcontentstudio.ui.sprites; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.ArrayList; import java.util.Enumeration; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CopyOnWriteArrayList; import javax.swing.BorderFactory; import javax.swing.DefaultListCellRenderer; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTable; import javax.swing.ListModel; import javax.swing.ListSelectionModel; import javax.swing.event.ListDataEvent; 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 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.utils.DesktopIntegration; import com.jidesoft.swing.JideBoxLayout; import com.jidesoft.swing.JideTabbedPane; public class SpritesheetEditor extends Editor { private static final long serialVersionUID = 3956109815682889863L; Map editorTabs = new LinkedHashMap(); 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( "" + "The data accompamying the image here is not part of the game.
" + "What you change here will be changed in your ATCS project only.
" + "None of this is exported to JSON or TMX, although it must be set correctly in order to choose tiles & icons correctly.
" + "
"); return label; } public SpritesheetEditor(Spritesheet sheet) { super(); this.icon = new ImageIcon(sheet.getIcon(0)); this.name = sheet.id; this.target = sheet; 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); 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 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(); } 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 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 listeners = new CopyOnWriteArrayList(); @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 ListModel { 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 listeners = new CopyOnWriteArrayList(); @Override public void addListDataListener(ListDataListener l) { listeners.add(l); } @Override public void removeListDataListener(ListDataListener l) { listeners.remove(l); } public void fireListChanged() { for (ListDataListener l : listeners) { l.contentsChanged(new ListDataEvent(this, ListDataEvent.CONTENTS_CHANGED, 0, this.getSize())); } } } public static class BacklinkCellRenderer extends DefaultListCellRenderer { private static final long serialVersionUID = 6819681566800482793L; private boolean includeType = false; 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; } } } }