/** * @file Jeie.java * @brief Main class * * @section License * * Copyright (C) 2008, 2009, 2012 IsmAvatar <IsmAvatar@gmail.com> * Copyright (C) 2009, 2012 Serge Humphrey <bobtheblueberry@gmail.com> * Copyright (C) 2013 jimn346 <jds9496@gmail.com> * Copyright (C) 2014 Robert B. Colton * * This file is a part of JEIE. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. **/ package org.jeie; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.AbstractButton; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFormattedTextField; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.filechooser.FileFilter; import javax.swing.text.NumberFormatter; import org.jeie.Canvas.RenderMode; import org.jeie.resources.Resources; public class Jeie implements ActionListener { private JFrame frame; private JToggleButton bGrid; private JScrollPane scroll; public File file; public Canvas canvas; public Palette pal; private JMenuBar menuBar; private JToolBar toolBar; private ToolPanel toolPanel; public final String TITLE = "Easy Image Editor "; public Jeie(BufferedImage image) { if (image == null) image = createWhiteBufferedImage(32,32); pal = new Palette(); canvas = new Canvas(image); scroll = new JScrollPane(canvas); toolPanel = new ToolPanel(new ToolDelegate(canvas)); toolPanel.selectDefault(); JPanel p = new JPanel(new BorderLayout()); p.add(makeToolBar(),BorderLayout.NORTH); p.add(toolPanel,BorderLayout.WEST); scroll.getVerticalScrollBar().setUnitIncrement(10); scroll.getHorizontalScrollBar().setUnitIncrement(10); p.add(scroll,BorderLayout.CENTER); p.add(pal,BorderLayout.SOUTH); frame = new JFrame(); frame.setJMenuBar(makeMenuBar()); frame.setContentPane(p); frame.setMinimumSize(new Dimension(500,500)); updateTitle(); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); frame.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { doClose(); } }); new FramePrefsHandler(frame); } public void updateTitle() { if (file == null) frame.setTitle(TITLE + "<untitled>"); else frame.setTitle(TITLE + file.getName()); } public JMenuBar makeMenuBar() { menuBar = new JMenuBar(); JMenu fm = new JMenu(Resources.getString("Jeie.FILE")); menuBar.add(fm); addMenuItem(fm,"NEW"); addMenuItem(fm,"OPEN"); fm.addSeparator(); addMenuItem(fm,"SAVE"); addMenuItem(fm,"SAVE_AS"); fm.addSeparator(); addMenuItem(fm,"EXIT"); JMenu em = new JMenu(Resources.getString("Jeie.EDIT")); menuBar.add(em); addMenuItem(em,"UNDO"); addMenuItem(em,"REDO"); JMenu vm = new JMenu(Resources.getString("Jeie.VIEW")); menuBar.add(vm); addMenuItem(vm,"ZOOM_IN"); addMenuItem(vm,"ZOOM_OUT"); vm.addSeparator(); addMenuItem(vm,"TILED"); addMenuItem(vm,"GRID").setActionCommand("GRID_MENU"); menuBar.add(new TransformMenu(this)); menuBar.add(new EffectsMenu(this)); return menuBar; } public JMenuItem addMenuItem(JMenu menu, String key) { JMenuItem mi = new JMenuItem(Resources.getString("Jeie." + key),Resources.getIconForKey("Jeie." + key)); mi.setAccelerator(KeyStroke.getKeyStroke(Resources.getKeyboardString("Jeie." + key))); mi.setActionCommand(key); mi.addActionListener(this); menu.add(mi); return mi; } public JToolBar makeToolBar() { toolBar = new JToolBar(); toolBar.setFloatable(false); addButton(toolBar,new JButton(),"NEW"); addButton(toolBar,new JButton(),"OPEN"); addButton(toolBar,new JButton(),"SAVE"); toolBar.addSeparator(); addButton(toolBar,new JButton(),"UNDO"); addButton(toolBar,new JButton(),"REDO"); toolBar.addSeparator(); addButton(toolBar,new JButton(),"ZOOM_OUT"); addButton(toolBar,new JButton(),"ZOOM_IN"); toolBar.addSeparator(); bGrid = addButton(toolBar,new JToggleButton(),"GRID"); bGrid.setSelected(canvas.isGridDrawn); return toolBar; } public <K extends AbstractButton>K addButton(Container c, K b, String key) { c.add(b); b.setActionCommand(key); b.addActionListener(this); b.setIcon(Resources.getIconForKey("Jeie." + key)); b.setToolTipText(Resources.getString("Jeie." + key)); return b; } protected class ToolDelegate extends MouseAdapter { protected Tool tool; protected Canvas canvas; public ToolDelegate(Canvas canvas) { this.canvas = canvas; canvas.addMouseListener(this); canvas.addMouseMotionListener(this); canvas.addMouseWheelListener(this); } public void setTool(Tool t) { if (tool != null) tool.finish(canvas,pal); tool = t; if (toolPanel != null) toolPanel.showOptions(tool); } protected MouseEvent refactor(MouseEvent e) { int x = e.getX() / canvas.getZoom(); int y = e.getY() / canvas.getZoom(); return new MouseEvent((Component) e.getSource(),e.getID(),e.getWhen(),e.getModifiers(),x,y, e.getClickCount(),e.isPopupTrigger(),e.getButton()); } public void mousePressed(MouseEvent e) { if (tool != null) tool.mousePress(refactor(e),canvas,pal); } public void mouseReleased(MouseEvent e) { if (tool != null) tool.mouseRelease(refactor(e),canvas,pal); } public void mouseDragged(MouseEvent e) { if (tool != null) tool.mouseMove(refactor(e),canvas,pal,true); } public void mouseMoved(MouseEvent e) { if (tool != null) tool.mouseMove(refactor(e),canvas,pal,false); } public void mouseWheelMoved(MouseWheelEvent e) { if (e.isControlDown()) { int rot = e.getWheelRotation(); if (rot < 0) canvas.zoomIn(); else if (rot > 0) canvas.zoomOut(); } } } public static void main(String[] args) { // java6u10 regression causes graphical xor to be very slow System.setProperty("sun.java2d.d3d","false"); //$NON-NLS-1$ //$NON-NLS-2$ UIManager.put("swing.boldMetal",false); //$NON-NLS-1$ BufferedImage bi = null; try { bi = ImageIO.read(Jeie.class.getResource("/test.png")); } catch (IOException e) { e.printStackTrace(); } Jeie j = new Jeie(bi); j.frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); } public static BufferedImage createWhiteBufferedImage(int w, int h) { BufferedImage image = new BufferedImage(w,h,BufferedImage.TYPE_INT_ARGB); Graphics g = image.getGraphics(); g.setColor(Color.WHITE); g.fillRect(0,0,w,h); return image; } public void actionPerformed(ActionEvent e) { String act = e.getActionCommand(); if (act.equals("ZOOM_IN")) { canvas.zoomIn(); return; } if (act.equals("ZOOM_OUT")) { canvas.zoomOut(); return; } if (act.equals("UNDO")) { if (!canvas.acts.isEmpty()) { canvas.redoActs.addFirst(canvas.acts.removeLast()); canvas.redrawCache(); } return; } if (act.equals("REDO")) { if (!canvas.redoActs.isEmpty()) { canvas.acts.add(canvas.redoActs.removeFirst()); canvas.redrawCache(); } return; } if (act.equals("NEW")) { doNew(true); return; } if (act.equals("OPEN")) { doOpen(); return; } if (act.equals("SAVE")) { doSave(false); return; } if (act.equals("SAVE_AS")) { doSave(true); return; } if (act.equals("EXIT")) { doClose(); return; } if (act.equals("TILED")) { canvas.renderMode = (canvas.renderMode != RenderMode.TILED) ? RenderMode.TILED : RenderMode.NORMAL; canvas.repaint(); } if (act.equals("GRID")) { canvas.isGridDrawn = bGrid.isSelected(); canvas.repaint(); return; } if (act.equals("GRID_MENU")) { bGrid.setSelected(!bGrid.isSelected()); canvas.isGridDrawn = bGrid.isSelected(); canvas.repaint(); return; } } public boolean hasChanged() { return !canvas.acts.isEmpty(); } public boolean doNew(boolean askforsize) { if (!checkSave()) return false; int width = 256; int height = 256; if (askforsize) { NumberFormatter nf = new NumberFormatter(); nf.setMinimum(new Integer(1)); JFormattedTextField wField = new JFormattedTextField(nf); wField.setValue(new Integer(width)); JFormattedTextField hField = new JFormattedTextField(nf); hField.setValue(new Integer(height)); JPanel myPanel = new JPanel(); GridLayout layout = new GridLayout(0,2); myPanel.setLayout(layout); myPanel.add(new JLabel(Resources.getString("Jeie.NEW_WIDTH"))); myPanel.add(wField); //myPanel.add(Box.createHorizontalStrut(15)); // a spacer myPanel.add(new JLabel(Resources.getString("Jeie.NEW_HEIGHT"))); myPanel.add(hField); int result = JOptionPane.showConfirmDialog(frame,myPanel,Resources.getString("Jeie.NEW_TITLE"), JOptionPane.OK_CANCEL_OPTION,JOptionPane.PLAIN_MESSAGE); if (result == JOptionPane.CANCEL_OPTION) { return false; } width = (Integer) wField.getValue(); height = (Integer) hField.getValue(); } //TODO: Ask for sizes file = null; BufferedImage img = createWhiteBufferedImage(width,height); canvas.setImage(img); scroll.updateUI(); updateTitle(); return true; } public void doClose() { if (!hasChanged()) System.exit(0); int c = JOptionPane.showConfirmDialog(frame, Resources.getString("Jeie.UNSAVED_MESSAGE"), Resources.getString("Jeie.UNSAVED_TITLE"), JOptionPane.YES_NO_CANCEL_OPTION); if (c == JOptionPane.CANCEL_OPTION) return; if (c == JOptionPane.YES_OPTION) if (doSave(false)) { System.exit(0); } if (c == JOptionPane.NO_OPTION) System.exit(0); } /** * @return false if the action was canceled */ public boolean checkSave() { if (hasChanged()) { int c = JOptionPane.showConfirmDialog(frame, Resources.getString("Jeie.UNSAVED_MESSAGE"), Resources.getString("Jeie.UNSAVED_TITLE"), JOptionPane.YES_NO_CANCEL_OPTION); if (c == JOptionPane.CANCEL_OPTION) return false; if (c == JOptionPane.YES_OPTION) return doSave(false); if (c == JOptionPane.NO_OPTION) return true; } return true; } public boolean doOpen() { if (!checkSave()) return false; File f = getFile(false); if (f == null) return false; try { BufferedImage img = ImageIO.read(f); canvas.setImage(img); file = f; scroll.updateUI(); updateTitle(); return true; } catch (IOException e) { JOptionPane.showMessageDialog(frame, Resources.getString("Jeie.OPEN_FAIL_MESSAGE") + " \"" + f.getPath() + "\"", Resources.getString("Jeie.OPEN_FAIL_TITLE"), JOptionPane.ERROR_MESSAGE); e.printStackTrace(); } return false; } public boolean doSave(boolean saveAs) { File f = file; if (saveAs || file == null) { f = getFile(true); if (f == null) return false; // just use PNG.. String name = f.getName().toLowerCase(); if (!name.endsWith(".png")) { if (name.contains(".")) name = name.substring(0,name.lastIndexOf('.')); f = new File(f.getParentFile(),name + ".png"); } } try { ImageIO.write(canvas.getRenderImage(),"PNG",f); file = f; return true; } catch (IOException e) { e.printStackTrace(); } return false; } private File getFile(final boolean save) { final JFileChooser fc = new JFileChooser((file != null) ? file.getParent() : null); fc.setFileFilter(new FileFilter() { @Override public String getDescription() { return "Image Files"; } @Override public boolean accept(File f) { if (f.isDirectory()) return true; String[] filters; if (save) filters = ImageIO.getWriterFileSuffixes(); else filters = ImageIO.getReaderFileSuffixes(); String name = f.getName().toLowerCase(); for (String s : filters) if (name.endsWith(s.toLowerCase())) return true; return false; } }); int result; if (save) result = fc.showSaveDialog(frame); else result = fc.showOpenDialog(frame); if (result != JFileChooser.APPROVE_OPTION) return null; File f = fc.getSelectedFile(); if (f == null || !save || !f.exists()) return f; int o = JOptionPane.showConfirmDialog(fc,"File " + f.getName() + " already exists. Replace?"); if (o == JOptionPane.YES_OPTION) return f; if (o == JOptionPane.NO_OPTION) return getFile(save); return null; } }