package com.vitco.layout.content.texture; import com.jidesoft.action.CommandMenuBar; import com.jidesoft.swing.JideScrollPane; import com.vitco.core.data.Data; import com.vitco.core.data.notification.DataChangeAdapter; import com.vitco.core.world.WorldManager; import com.vitco.layout.content.ViewPrototype; import com.vitco.manager.action.types.StateActionPrototype; import com.vitco.manager.async.AsyncAction; import com.vitco.manager.async.AsyncActionManager; import com.vitco.manager.pref.PrefChangeListener; import com.vitco.settings.VitcoSettings; import com.vitco.util.misc.CFileDialog; import com.vitco.util.misc.SwingAsyncHelper; import com.vitco.util.misc.ThumbnailFileChooser; import com.vitco.util.misc.WrapLayout; import org.springframework.beans.factory.annotation.Autowired; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; /** * Manages the different Textures (in the window where the user adds them!). */ public class TextureManager extends ViewPrototype implements TextureManagerInterface { protected AsyncActionManager asyncActionManager; @Autowired public final void setAsyncActionManager(AsyncActionManager asyncActionManager) { this.asyncActionManager = asyncActionManager; } // var & setter protected Data data; @Autowired public final void setData(Data data) { this.data = data; } // true if there exist textured private boolean texturesExist = false; // currently selected texture private int selectedTexture = -1; // texture panel class private final class TexturePanel extends JPanel { private final Integer textureId; // we need the hash to determine when to change the picture private String hash = ""; private final Color inactiveColor = VitcoSettings.TEXTURE_BORDER; private final Color activeColor = VitcoSettings.TEXTURE_BORDER_ACTIVE; private final Color selectedColor = VitcoSettings.TEXTURE_BORDER_SELECTED; private boolean selected = false; // reference to this instance private final TexturePanel thisTexturePanel = this; private TexturePanel(final Integer textureId) { this.textureId = textureId; this.setBorder(BorderFactory.createLineBorder(inactiveColor)); this.setLayout(new BorderLayout(0, 0)); //this.setToolTipText("Texture #" + textureId); this.addMouseListener(new MouseAdapter() { @Override public void mousePressed(final MouseEvent e) { super.mousePressed(e); asyncActionManager.addAsyncAction(new AsyncAction() { @Override public void performAction() { // unselect if this is already selected data.selectTextureSoft(selected ? -1 : textureId); } }); } @Override public void mouseEntered(MouseEvent e) { super.mouseEntered(e); setBorder(BorderFactory.createLineBorder(activeColor)); } @Override public void mouseExited(MouseEvent e) { super.mouseExited(e); setBorder(BorderFactory.createLineBorder(selected?selectedColor:inactiveColor)); } }); refresh(); } private void refresh() { String newHash = data.getTextureHash(textureId); if (!hash.equals(newHash)) { // only redraw when hash changed removeAll(); // make sure nothing is in this container ImageIcon myPicture = data.getTexture(textureId); JLabel picLabel = new JLabel(myPicture); add( picLabel ); hash = newHash; } boolean selectedNew = textureId == selectedTexture; if (selectedNew != selected) { selected = selectedNew; this.setBorder(BorderFactory.createLineBorder(selected?selectedColor:inactiveColor)); } SwingAsyncHelper.handle(new Runnable() { @Override public void run() { thisTexturePanel.revalidate(); thisTexturePanel.repaint(); } }, errorHandler); } } // import texture file chooser final ThumbnailFileChooser fc_import = new ThumbnailFileChooser(32, 32); // export texture file chooser final CFileDialog fc_export = new CFileDialog(); // handles the textures of the data class object @Override public JComponent build(final Frame mainFrame) { // panel that holds everything final JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); final JPanel textureWrapperPanel = new JPanel(); textureWrapperPanel.setLayout(new WrapLayout(FlowLayout.CENTER, 3, 3)); textureWrapperPanel.setBackground(VitcoSettings.FRAME_BG_COLOR); final JideScrollPane scrollPane = new JideScrollPane(textureWrapperPanel); scrollPane.setBorder(BorderFactory.createMatteBorder(1, 1, 0, 1, VitcoSettings.DEFAULT_BORDER_COLOR)); panel.add(scrollPane, BorderLayout.CENTER); // add filter fc_import.addFileType("png"); fc_export.addFileType("png"); // create the menu actions actionManager.registerAction("texturemg_action_add", new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { File toOpen = fc_import.openFile(mainFrame); if (toOpen != null) { try { data.addTexture(ImageIO.read(toOpen)); } catch (IOException error) { console.addLine(langSelector.getString("texturemg_general_file_error")); } } } }); actionGroupManager.addAction("texture_manager_buttons", "texturemg_action_remove", new StateActionPrototype() { @Override public boolean getStatus() { return selectedTexture != -1; } @Override public void action(ActionEvent e) { if (getStatus()) { boolean success = data.removeTexture(selectedTexture); if (!success) { console.addLine(langSelector.getString("texturemg_delete_failed_texture_in_use")); } } } }); actionGroupManager.addAction("texture_manager_buttons", "texturemg_action_replace", new StateActionPrototype() { @Override public boolean getStatus() { return selectedTexture != -1; } @Override public void action(ActionEvent e) { if (getStatus()) { File toOpen = fc_import.openFile(mainFrame); if (toOpen != null) { try { ImageIcon texture = new ImageIcon(ImageIO.read(toOpen)); // make sure we can identify the texture ImageIcon textureDrawnOnTop = data.getTexture(selectedTexture); textureDrawnOnTop.getImage().getGraphics().drawImage(texture.getImage(), 0, 0, null); data.replaceTexture(selectedTexture, textureDrawnOnTop); } catch (IOException error) { console.addLine(langSelector.getString("texturemg_general_file_error")); } } } } }); actionGroupManager.addAction("texture_manager_buttons", "texturemg_action_export", new StateActionPrototype() { @Override public boolean getStatus() { return selectedTexture != -1; } @Override public void action(ActionEvent e) { if (getStatus()) { File exportTo = fc_export.saveFile(mainFrame); if (exportTo != null) { String dir = exportTo.getPath(); // query if file already exists if (!exportTo.exists() || JOptionPane.showConfirmDialog(mainFrame, dir + " " + langSelector.getString("replace_file_query"), langSelector.getString("replace_file_query_title"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) { ImageIcon texture = data.getTexture(selectedTexture); BufferedImage img = new BufferedImage(texture.getIconWidth(), texture.getIconHeight(), BufferedImage.TYPE_INT_ARGB); img.getGraphics().drawImage(texture.getImage(), 0, 0, null); try { ImageIO.write(img, "png", exportTo); console.addLine(langSelector.getString("texturemg_export_success")); } catch (IOException e1) { console.addLine(langSelector.getString("texturemg_export_failed")); } } } } } }); actionGroupManager.addAction("texture_manager_buttons", "texturemg_action_clear", new StateActionPrototype() { @Override public boolean getStatus() { return texturesExist; } @Override public void action(ActionEvent e) { if (getStatus()) { data.removeAllTexture(); } } }); actionGroupManager.registerGroup("texture_manager_buttons"); // handle texture change notification (logic + layout) data.addDataChangeListener(new DataChangeAdapter() { // list of texture images currently on display private final HashMap<Integer, TexturePanel> texturePanels = new HashMap<Integer, TexturePanel>(); // list of textures with md5 hash private final HashMap<Integer, String> textureHash = new HashMap<Integer, String>(); @Override public void onTextureDataChanged() { ArrayList<Integer> dataTextureList = new ArrayList<Integer>(Arrays.asList(data.getTextureList())); // previously selected texture int prevSelectedTexture = selectedTexture; // remember if textures exist texturesExist = dataTextureList.size() > 0; // get the currently selected texture selectedTexture = data.getSelectedTexture(); // update buttons actionGroupManager.refreshGroup("texture_manager_buttons"); // removed textures for (Integer texId : new ArrayList<Integer>(textureHash.keySet())) { if (!dataTextureList.contains(texId)) { // delete from internal store textureHash.remove(texId); // delete texture from world boolean removed = WorldManager.removeTile(String.valueOf(texId)); assert removed; // remove panel textureWrapperPanel.remove(texturePanels.get(texId)); texturePanels.remove(texId); } } // added textures for (int texId : dataTextureList) { boolean changed = false; if (textureHash.containsKey(texId)) { if (!data.getTextureHash(texId).equals(textureHash.get(texId))) { // hash changed changed = true; // update panel texturePanels.get(texId).refresh(); } } else { // new texture changed = true; // =================== // create panel TexturePanel panel = new TexturePanel(texId); texturePanels.put(texId, panel); // insert to correct place Integer[] currentObjects = new Integer[texturePanels.size()]; texturePanels.keySet().toArray(currentObjects); // sort Arrays.sort(currentObjects, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }); int pos = 0; while (texId > currentObjects[pos] && pos < currentObjects.length) { pos++; } textureWrapperPanel.add(panel, null, pos); // =================== } if (changed) { // store/update internal hash textureHash.put(texId, data.getTextureHash(texId)); // load/update texture into world WorldManager.loadTile(String.valueOf(texId), data.getTexture(texId)); } } // update the UI (force!) SwingAsyncHelper.handle(new Runnable() { @Override public void run() { textureWrapperPanel.validate(); textureWrapperPanel.repaint(); } }, errorHandler); // this updates the values for the getBound() function scrollPane.validate(); // scroll the selected texture into view and refresh panel TexturePanel selectedTexturePanel = texturePanels.get(selectedTexture); if (selectedTexturePanel != null) { textureWrapperPanel.scrollRectToVisible(selectedTexturePanel.getBounds()); selectedTexturePanel.refresh(); } // refresh prev selected panels TexturePanel prevSelectedTexturePanel = texturePanels.get(prevSelectedTexture); if (prevSelectedTexturePanel != null) { prevSelectedTexturePanel.refresh(); } } }); // deselect any texture when the color changes preferences.addPrefChangeListener("currently_used_color", new PrefChangeListener() { @Override public void onPrefChange(Object newValue) { data.selectTexture(-1); } }); // create menu bar to bottom CommandMenuBar menuPanel = new CommandMenuBar(); //menuPanel.setOrientation(1); // top down orientation menuGenerator.buildMenuFromXML(menuPanel, "com/vitco/layout/content/texture/toolbar.xml"); panel.add(menuPanel, BorderLayout.SOUTH); return panel; } @PreDestroy public final void finish() { preferences.storeString("texture_import_dialog_last_directory", fc_import.getDialogPath()); preferences.storeString("texture_export_dialog_last_directory", fc_export.getDialogPath()); } @PostConstruct public final void init() { if (preferences.contains("texture_import_dialog_last_directory")) { File file = new File(preferences.loadString("texture_import_dialog_last_directory")); fc_import.setDialogPath(file); } if (preferences.contains("texture_export_dialog_last_directory")) { File file = new File(preferences.loadString("texture_export_dialog_last_directory")); fc_export.setDialogPath(file); } } }