/* * Copyright (c) 2009, 2010, 2011 Daniel Rendall * This file is part of FractDim. * * FractDim 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. * * FractDim 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 FractDim. If not, see <http://www.gnu.org/licenses/> */ package uk.co.danielrendall.fractdim.app; import org.apache.log4j.*; import uk.co.danielrendall.fractdim.app.controller.FractalController; import uk.co.danielrendall.fractdim.app.gui.FractalPanel; import uk.co.danielrendall.fractdim.app.gui.MainWindow; import uk.co.danielrendall.fractdim.app.model.FractalDocument; import uk.co.danielrendall.fractdim.logging.Log; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.*; import java.awt.event.ActionEvent; import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.prefs.Preferences; /** * Run Fractal Dimension as a GUI app */ public class FractDim { private final static Preferences prefs; static { prefs = Preferences.userRoot().node("/uk/co/danielrendall/fractdim"); String lookAndFeel = UIManager.getSystemLookAndFeelClassName(); System.out.println("Using look and feel: " + lookAndFeel); try { UIManager.setLookAndFeel(lookAndFeel); } catch (Exception e) { System.out.println("Couldn't set look and feel - " + e.getMessage()); } try { Preferences loggingPrefs = prefs.node("logging"); Preferences loggers = loggingPrefs.node("loggers"); String layout = loggingPrefs.get("layout", "%m%n"); loggingPrefs.put("layout", layout); File logFile = new File(System.getProperty("user.home"), "fractdim.log"); String logFileName = loggingPrefs.get("logfile", logFile.getAbsolutePath()); loggingPrefs.put("logfile", logFileName); Logger.getRootLogger().addAppender(new FileAppender(new PatternLayout(layout),logFileName)); setLogLevel(Log.gui, loggers, Level.INFO); setLogLevel(Log.thread, loggers, Level.INFO); setLogLevel(Log.app, loggers, Level.INFO); setLogLevel(Log.misc, loggers, Level.INFO); setLogLevel(Log.test, loggers, Level.INFO); setLogLevel(Log.messages, loggers, Level.INFO); setLogLevel(Log.geom, loggers, Level.INFO); setLogLevel(Log.calc, loggers, Level.INFO); setLogLevel(Log.points, loggers, Level.INFO); setLogLevel(Log.squares, loggers, Level.INFO); setLogLevel(Log.recursion, loggers, Level.INFO); } catch (IOException e) { System.out.println("Unable to configure logging - " + e.getMessage()); } } private static void setLogLevel(Logger aLog, Preferences loggingPrefs, Level def) { String level = loggingPrefs.get(aLog.getName(), def.toString()); loggingPrefs.put(aLog.getName(), level); aLog.setLevel(Level.toLevel(level)); System.out.println("Log " + aLog.getName() + " is at level " + level); } private final static FractDim fractDim = new FractDim(); private final static int DEFAULT_WIDTH = 1024; private final static int DEFAULT_HEIGHT = 768; private final MainWindow window; private final JFileChooser openFileChooser; private final JFileChooser exportFileChooser; private final Map<FractalPanel, FractalController> controllers = new HashMap<FractalPanel, FractalController>(); private FractalController currentController = null; private static AtomicInteger ID = new AtomicInteger(1); private final String PREF_DIRECTORY_SVG = "initial.directory.svg"; private final String PREF_DIRECTORY_XLS = "initial.directory.xls"; private final String PREF_SCREEN_X = "initial.screenx"; private final String PREF_SCREEN_Y = "initial.screeny"; private final String PREF_WIDTH = "initial.width"; private final String PREF_HEIGHT = "initial.height"; private String xlsFileDir = ""; private String svgFileDir = ""; public static void main(String[] args) throws Exception { System.out.println("Fractal Dimension Calculator"); System.out.println("This program comes with ABSOLUTELY NO WARRANTY"); System.out.println("This is free software, and you are welcome to redistribute it under certain conditions"); fractDim.run(args); } public static FractDim instance() { return fractDim; } public FractDim() { window = new MainWindow(); File svgDir = new File(prefs.get(PREF_DIRECTORY_SVG, System.getProperty("user.home"))); if (svgDir.exists()) { openFileChooser = new JFileChooser(svgDir); svgFileDir = svgDir.getAbsolutePath(); } else { openFileChooser = new JFileChooser(); } File xlsDir = new File(prefs.get(PREF_DIRECTORY_XLS, svgDir.getAbsolutePath())); if (xlsDir.exists()) { exportFileChooser = new JFileChooser(xlsDir); xlsFileDir = xlsDir.getAbsolutePath(); } else { exportFileChooser = new JFileChooser(); } } private void run(String[] args) { Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); int xCenter = (int)(d.getWidth() / 2.0d); int yCenter = (int)(d.getHeight() / 2.0d); int width = prefs.getInt(PREF_WIDTH, DEFAULT_WIDTH); int height = prefs.getInt(PREF_HEIGHT, DEFAULT_HEIGHT); if ((width < 100) || (height < 100)) { width = DEFAULT_WIDTH; height = DEFAULT_HEIGHT; } int defaultLeft = xCenter - (width / 2); int defaultTop = yCenter - (height / 2); int left = prefs.getInt(PREF_SCREEN_X, defaultLeft); int top = prefs.getInt(PREF_SCREEN_Y, defaultTop); window.setBounds(left, top, width, height); window.setVisible(true); } // public void generateNewFractal() { // CompoundDataModel model = new CompoundDataModel(GenerateSettings.class); // // GenerateDialog gd = new GenerateDialog(); // gd.bindToModel(model); // gd.setModel(new GenerateSettings()); // // int res = JOptionPane.showOptionDialog((JFrame) getMainWindow(), gd, "Fractal options", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, null, null); // // // if (res == JOptionPane.YES_OPTION) { // setStatus(tr("Generating...")); // setBusy(true); // // Generator gen = new Generator(); // // GenerateSettings settings = (GenerateSettings) model.getNewModel(); // // String type = settings.getFractalType(); // Document doc = null; // try { // Class clazz = Class.forName("uk.co.danielrendall.fractdim.generate.fractals." + type); // // SVGDocument svg = gen.generateFractal(((Procedure)clazz.newInstance()), new Point(0, 0), new Point(settings.getEndX(), settings.getEndY()), settings.getDepth()); // // doc = Document.createNew(); // ((FDData) doc.getData()).setSvgWithMetadata(svg); // documents.add(doc); // doc.syncViewsWithData(); // setStatus(tr("Ready")); // } catch (Exception e) { // Log.app.warn("Couldn't load fractal type '" + type + "' - " + e.getMessage()); // } // // setBusy(false); // Application.getMessageDispatcher().dispatch( // doc, FractDim.DOCUMENT_GENERATED, doc); // } // } public File getExportFile(String possibleName) { exportFileChooser.setFileFilter(new FileNameExtensionFilter("Excel Files", "xls")); if (possibleName.toLowerCase().endsWith(".svg")) { possibleName = possibleName.substring(0, possibleName.lastIndexOf(".")) + ".xls"; } File suggested = new File(xlsFileDir, possibleName); exportFileChooser.setSelectedFile(suggested); int returnVal = exportFileChooser.showSaveDialog(window); if(returnVal == JFileChooser.APPROVE_OPTION) { File selectedFile = exportFileChooser.getSelectedFile(); Log.app.debug("Chosen file: " + selectedFile.getName()); xlsFileDir = selectedFile.getParent(); prefs.put(PREF_DIRECTORY_XLS, xlsFileDir); return selectedFile; } return null; } public void openFile(ActionEvent e) { Log.app.debug("Open File"); openFileChooser.setFileFilter(new FileNameExtensionFilter("SVG Files", "svg")); int returnVal = openFileChooser.showOpenDialog(window); if(returnVal == JFileChooser.APPROVE_OPTION) { File selectedFile = openFileChooser.getSelectedFile(); Log.app.debug("Chosen file: " + selectedFile.getName()); try { FractalController controller = FractalController.fromFile(selectedFile); add(controller); svgFileDir = selectedFile.getParent(); prefs.put(PREF_DIRECTORY_SVG, svgFileDir); } catch (IOException ex) { Log.app.warn("Unable to load document: " + ex.getMessage()); } } } public void quit(ActionEvent e) { Log.app.debug("Quit"); Rectangle bounds = window.getBounds(); prefs.putInt(PREF_SCREEN_X, bounds.x); prefs.putInt(PREF_SCREEN_Y, bounds.y); prefs.putInt(PREF_WIDTH, bounds.width); prefs.putInt(PREF_HEIGHT, bounds.height); window.close(); } public void add(FractalController controller) { FractalDocument document = controller.getDocument(); FractalPanel panel = controller.getPanel(); controllers.put(panel, controller); window.addTab(document.getName(), panel); controller.notifyAdded(); } public void remove(FractalController controller) { FractalPanel panel = controller.getPanel(); controller.notifyRemoving(); window.removeTab(panel); controllers.remove(panel); controller.notifyRemoved(); } /** * Called when the the user has switched tabs, so we can set the currentController * to be the correct one for the currently visible panel. * @param panel The newly selected panel */ public synchronized void notifyPanelChange(FractalPanel panel) { if (panel == null) { // all closed currentController = null; window.disableMenuItems(); } else { FractalController controller = controllers.get(panel); currentController = controller; if (controller == null) { Log.app.warn("Controller shouldn't be null!"); } else { currentController.enableMenuItems(); } } } /** * Called when a controller has done something (which may have been running in the background) and may want * to update global state (i.e. selected menu items). If the controller doing the calling is current, it gets * to do the update, otherwise it can wait until its panel is next selected. * @param fractalController */ public synchronized void updateMeIfCurrent(FractalController fractalController) { if (fractalController == currentController) { fractalController.enableMenuItems(); } } public static int newId() { return ID.getAndIncrement(); } }