package hr.fer.zemris.ecf.gui; import hr.fer.zemris.ecf.gui.display.BrowsePanel; import hr.fer.zemris.ecf.gui.display.FrameDisplayer; import hr.fer.zemris.ecf.gui.display.IResultDisplay; import hr.fer.zemris.ecf.gui.display.ResultProgressFrame; import hr.fer.zemris.ecf.gui.display.ResultProgressFrameDisplayer; import hr.fer.zemris.ecf.gui.layout.EntryBlockSelection; import hr.fer.zemris.ecf.gui.layout.EntryFieldPanel; import hr.fer.zemris.ecf.gui.layout.EntryListPanel; import hr.fer.zemris.ecf.gui.layout.ParametersSelection; import hr.fer.zemris.ecf.gui.model.conf.ConfigurationKey; import hr.fer.zemris.ecf.gui.model.conf.IConfiguration; import hr.fer.zemris.ecf.gui.model.conf.impl.PropertyFile; import hr.fer.zemris.ecf.gui.model.log.ILog; import hr.fer.zemris.ecf.gui.model.log.impl.FileLog; import hr.fer.zemris.ecf.param.AlgGenRegInit; import hr.fer.zemris.ecf.param.AlgGenRegUser; import hr.fer.zemris.ecf.param.Algorithm; import hr.fer.zemris.ecf.param.Entry; import hr.fer.zemris.ecf.param.Genotype; import hr.fer.zemris.ecf.param.Registry; import hr.fer.zemris.ecf.tasks.TaskMannager; import hr.fer.zemris.ecf.xmldom.XmlReading; import hr.fer.zemris.ecf.xmldom.XmlWriting; import java.awt.BorderLayout; import java.awt.Desktop; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.CopyOption; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JOptionPane; import javax.swing.JTabbedPane; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; import javax.swing.WindowConstants; /** * Main frame of the application. * * @author Domagoj Stanković * @version 1.0 */ public class ECFLab extends JFrame { private static final long serialVersionUID = 1L; private static final String CONFIGURATION_FILE = "res/conf/conf.properties"; private static final String ICON_NEW_CONF_PATH = "res/img/toolbar/New.png"; private static final String ICON_OPEN_CONF_PATH = "res/img/toolbar/Open.png"; private static final String ICON_SAVE_CONF_PATH = "res/img/toolbar/Save.png"; private static final String ICON_SAVE_CONF_AS_PATH = "res/img/toolbar/Save_as.png"; private static final String ICON_OPEN_LOG_PATH = "res/img/toolbar/Chart.png"; private static final String ICON_SAVE_LOG_PATH = "res/img/toolbar/Save_data.png"; private static final String APP_TITLE = "ECF Lab"; private static final String APP_ICON_PATH = "res/img/icon/dna_icon.png"; private IConfiguration configuration; private ILog logger; private Map<String, Action> actions = new HashMap<>(); private JMenuBar menuBar = new JMenuBar(); private JTabbedPane tabbedPane; private JToolBar toolbar; private String ecfPath; private String parDumpPath; private AlgGenRegInit parDump; private IResultDisplay resultDisplay; private IResultDisplay openResultDisplay; /** * Creates a new main frame for ECF Lab. */ public ECFLab(IConfiguration configuration, ILog logger) { this.configuration = configuration; this.logger = logger; try { setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); setLookAndFeel(true); initGUI(); setTitle(APP_TITLE); try { Image image = ImageIO.read(new FileInputStream(APP_ICON_PATH)); setIconImage(image); } catch (IOException e) { logger.log(e); } setLocation(300, 100); setSize(1000, 600); setLayout(new BorderLayout()); add(toolbar, BorderLayout.NORTH); tabbedPane = new JTabbedPane(); add(tabbedPane, BorderLayout.CENTER); openResultDisplay = new FrameDisplayer(); resultDisplay = new ResultProgressFrameDisplayer(logger); setVisible(true); chooseECFExe(); } catch (Exception e) { logger.log(e); reportError(e.getMessage()); } } /** * Displays dialog for choosing ECF executable file. */ private void chooseECFExe() { BrowsePanel ecfExePanel = new BrowsePanel(); int retVal = JOptionPane.showConfirmDialog(this, ecfExePanel, "Choose executable ECF file", JOptionPane.OK_CANCEL_OPTION); if (retVal == JOptionPane.OK_OPTION) { ecfPath = ecfExePanel.getText(); parDumpPath = configuration.getValue(ConfigurationKey.DEFAULT_PARAMS_DUMP); setTitle(APP_TITLE + " - " + ecfPath); parDump = callParDump(); } } /** * Initializes exit action, other actions and menu bar. */ private void initGUI() { addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { exit(); } }); initActions(); initMenuBar(); initToolbar(); } /** * Initializes toolbar. */ private void initToolbar() { toolbar = new JToolBar(); toolbar.setFloatable(false); JButton button = makeToolbarButton(ICON_NEW_CONF_PATH, "NewConf", "New configuration"); toolbar.add(button); button = makeToolbarButton(ICON_OPEN_CONF_PATH, "OpenConf", "Open existing configuration"); toolbar.add(button); button = makeToolbarButton(ICON_SAVE_CONF_PATH, "SaveConf", "Save configuration"); toolbar.add(button); button = makeToolbarButton(ICON_SAVE_CONF_AS_PATH, "SaveConfAs", "Save configuration As"); toolbar.add(button); toolbar.addSeparator(); button = makeToolbarButton(ICON_OPEN_LOG_PATH, "OpenLog", "Open log file"); toolbar.add(button); button = makeToolbarButton(ICON_SAVE_LOG_PATH, "SaveLog", "Save log file"); toolbar.add(button); } protected JButton makeToolbarButton(String imgPath, String action, String toolTipText) { JButton button = new JButton(actions.get(action)); button.setText(""); button.setToolTipText(toolTipText);; ImageIcon icon = new ImageIcon(imgPath); button.setIcon(icon); return button; } /** * Initializes main actions for ECF Lab GUI. */ private void initActions() { Action action; action = new AbstractAction("New") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { newTab("New configuration"); } }; action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_DOWN_MASK)); action.putValue(Action.SHORT_DESCRIPTION, "Create new configuration"); actions.put("NewConf", action); action = new AbstractAction("Open") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { openConf(); } }; action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_O, InputEvent.CTRL_DOWN_MASK)); action.putValue(Action.SHORT_DESCRIPTION, "Open existing configuration"); actions.put("OpenConf", action); action = new AbstractAction("Save") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { saveConf(); } }; action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK)); action.putValue(Action.SHORT_DESCRIPTION, "Save configuration"); actions.put("SaveConf", action); action = new AbstractAction("Save As") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { saveConfAs(); } }; action.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_A, InputEvent.CTRL_DOWN_MASK)); action.putValue(Action.SHORT_DESCRIPTION, "Save configuration as"); actions.put("SaveConfAs", action); action = new AbstractAction("Open") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { openLog(); } }; action.putValue(Action.SHORT_DESCRIPTION, "Open log file"); actions.put("OpenLog", action); action = new AbstractAction("Save") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { saveLog(); } }; action.putValue(Action.SHORT_DESCRIPTION, "Save log file"); actions.put("SaveLog", action); action = new AbstractAction("Results frame") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { ResultProgressFrame.getInstance().setVisible(true); } }; actions.put("ResultsFrame", action); action = new AbstractAction("Change ECF") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { chooseECFExe(); } }; action.putValue(Action.SHORT_DESCRIPTION, "Change ECF executable file"); actions.put("ChangeECFExe", action); action = new AbstractAction("ECF home page") { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { ecfHomePage(); } }; action.putValue(Action.SHORT_DESCRIPTION, "Go to ECF home page"); actions.put("ecfHomePage", action); } /** * Copies log file created during last experiment to the destination path. */ protected void saveLog() { ParametersSelection ps = (ParametersSelection) tabbedPane.getSelectedComponent(); boolean b = ps.wasRunBefore(); if (!b) { JOptionPane.showMessageDialog(this, "This experiment was never run before!", "Action unavailable", JOptionPane.INFORMATION_MESSAGE); return; } JFileChooser fc = new JFileChooser(); int retVal = fc.showSaveDialog(this); if (retVal == JFileChooser.APPROVE_OPTION) { File file = fc.getSelectedFile(); Path source = Paths.get(ps.getLastLogFilePath()); Path target = Paths.get(file.getAbsolutePath()); CopyOption options = StandardCopyOption.REPLACE_EXISTING; try { Files.copy(source, target, options); } catch (IOException e) { e.printStackTrace(); } JOptionPane.showMessageDialog(this, "Log file copied successfully!", "Saved successfully", JOptionPane.INFORMATION_MESSAGE); } } /** * Opens dialog for choosing log file to be viewed. Then displays results. */ protected void openLog() { BrowsePanel logPathPanel = new BrowsePanel(); int retVal = JOptionPane.showConfirmDialog(this, logPathPanel, "Choose log file", JOptionPane.OK_CANCEL_OPTION); if (retVal == JOptionPane.OK_OPTION) { try { openResultDisplay.displayResult(logPathPanel.getText()); } catch (Exception e) { logger.log(e); reportError(e.getMessage()); } } } /** * Displays ECF home page in users default browser. */ protected void ecfHomePage() { URI uri; try { uri = new URI(configuration.getValue(ConfigurationKey.ECF_HOME_PAGE)); Desktop.getDesktop().browse(uri); } catch (URISyntaxException e) { logger.log(e); e.printStackTrace(); } catch (IOException e) { logger.log(e); e.printStackTrace(); } } /** * Saves current configuration under the name chosen in the file chooser * dialog. */ protected void saveConfAs() { JFileChooser fc = new JFileChooser(); int retVal = fc.showSaveDialog(this); if (retVal == JFileChooser.APPROVE_OPTION) { File file = fc.getSelectedFile(); String path = file.getAbsolutePath(); ParametersSelection ps = (ParametersSelection) tabbedPane.getSelectedComponent(); XmlWriting.write(path, ps.getParameters()); JOptionPane.showMessageDialog(this, "Saved under name: " + path, "Saved succesfully", JOptionPane.INFORMATION_MESSAGE); } } /** * Saves current configuration under the name written in the define panel of * the selected {@link ParametersSelection} panel. */ protected void saveConf() { ParametersSelection ps = (ParametersSelection) tabbedPane.getSelectedComponent(); String path = ps.getDefinePanel().getParamsPath(); XmlWriting.write(path, ps.getParameters()); tabbedPane.setTitleAt(tabbedPane.getSelectedIndex(), path); } protected void openConf() { try { JFileChooser fc = new JFileChooser(); int retVal = fc.showOpenDialog(this); if (retVal != JFileChooser.APPROVE_OPTION) { return; } File file = fc.getSelectedFile(); AlgGenRegUser agru = XmlReading.readArchive(file); ParametersSelection ps = newTab(file.getAbsolutePath()); List<Algorithm> algs = agru.algorithm; for (Algorithm alg : algs) { List<Entry> entries = alg.getEntryList(); ps.getAlgSel().show(alg.getName()); EntryListPanel enp = ps.getAlgSel().getSelectedEntryList(); for (Entry entry : entries) { EntryFieldPanel efp = enp.getEntryField(entry.key); efp.setSelected(true); efp.setText(entry.value); } EntryBlockSelection<Algorithm> algSel = ps.getAlgSel(); algSel.add(); } List<Genotype> gens = agru.genotypes.get(0); for (Genotype gen : gens) { List<Entry> entries = gen.getEntryList(); ps.getGenSel().show(gen.getName()); EntryListPanel enp = ps.getGenSel().getSelectedEntryList(); for (Entry entry : entries) { EntryFieldPanel efp = enp.getEntryField(entry.key); efp.setSelected(true); efp.setText(entry.value); } EntryBlockSelection<Genotype> genSel = ps.getGenSel(); genSel.add(); } Registry reg = agru.registry; List<Entry> entries = reg.getEntryList(); EntryListPanel enp = ps.getRegList(); for (Entry entry : entries) { EntryFieldPanel efp = enp.getEntryField(entry.key); efp.setSelected(true); efp.setText(entry.value); } ps.getDefinePanel().setParamsPath(file.getAbsolutePath()); } catch (Exception e) { String message = e.getMessage(); if (message == null) { message = "Error"; } message = message.trim(); reportError(message.isEmpty() ? "Error" : message); logger.log(e); } } /** * Creates new tab with {@link ParametersSelection} panel. * * @param tabName * Name of the new tab * @return Created {@link ParametersSelection} panel */ protected ParametersSelection newTab(String tabName) { ParametersSelection parSel = new ParametersSelection(this); tabbedPane.add(tabName, parSel); tabbedPane.setSelectedIndex(tabbedPane.getTabCount() - 1); return parSel; } /** * Calls parameters dump from ECF executable file. * * @return */ protected AlgGenRegInit callParDump() { TaskMannager tm = new TaskMannager(); return tm.getInitialECFparams(ecfPath, parDumpPath); } /** * Initializes menu bar with all main actions. */ private void initMenuBar() { JMenu confMenu = new JMenu("Configuration"); confMenu.add(actions.get("NewConf")); confMenu.add(actions.get("OpenConf")); confMenu.add(actions.get("SaveConf")); confMenu.add(actions.get("SaveConfAs")); JMenu logMenu = new JMenu("Log"); logMenu.add(actions.get("OpenLog")); logMenu.add(actions.get("SaveLog")); logMenu.add(actions.get("ResultsFrame")); JMenu exeMenu = new JMenu("ECF"); exeMenu.add(actions.get("ChangeECFExe")); exeMenu.add(actions.get("ecfHomePage")); menuBar.add(confMenu); menuBar.add(logMenu); menuBar.add(exeMenu); setJMenuBar(menuBar); } /** * Displays warning dialog with specified message and "Error" title. * * @param errorMessage * Message to be shown */ public void reportError(String errorMessage) { JOptionPane.showMessageDialog(this, errorMessage, "Error", JOptionPane.INFORMATION_MESSAGE); } /** * Exit method. Defines actions that have to be done before closing the * frame. */ protected void exit() { boolean b = Boolean.parseBoolean(configuration.getValue(ConfigurationKey.CONFIRM_EXIT)); if (b) { int ret = JOptionPane.showConfirmDialog(this, "Are you sure you want to exit?", "Really exit?", JOptionPane.YES_NO_OPTION); if (ret == JOptionPane.YES_OPTION) { exitConfirmed(); } } else { exitConfirmed(); } } private void exitConfirmed() { ResultProgressFrame.disposeInstance(); dispose(); } /** * Logger for errors. * * @return Error logger */ public ILog getLogger() { return logger; } /** * External application configuration. * * @return Configuration */ public IConfiguration getConfiguration() { return configuration; } /** * All main actions. * * @return Main actions. */ public Map<String, Action> getActions() { return actions; } /** * Current ECF executable file path. * * @return ECF exe path */ public String getEcfPath() { return ecfPath; } /** * Path to the parameters dump file. * * @return Path to the parameters dump file */ public String getParDumpPath() { return parDumpPath; } /** * Object with all parameters from the selected ECF exe. * * @return {@link AlgGenRegInit} object with all the parameters from the * current ECF executable file. */ public AlgGenRegInit getParDump() { return parDump; } /** * @return Object which main purpose is to display a result of experiment. */ public IResultDisplay getResultDisplay() { return resultDisplay; } /** * Runs this application. * * @param args * Not used */ public static void main(String[] args) { final IConfiguration configuration = new PropertyFile(CONFIGURATION_FILE); final ILog log = new FileLog(configuration.getValue(ConfigurationKey.LOG_FILE_PATH)); Thread.setDefaultUncaughtExceptionHandler(new EDTExceptionHandler(log)); System.setProperty("sun.awt.exception.handler", EDTExceptionHandler.class.getName()); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { startGUIApp(configuration, log); } }); } /** * Starts GUI. */ protected static void startGUIApp(IConfiguration configuration, ILog log) { new ECFLab(configuration, log); } /** * Sets {@link LookAndFeel} to the system or to the java look. * * @param system * True for system look, false for java look */ protected void setLookAndFeel(boolean system) { String newLookAndFeel = system ? UIManager.getSystemLookAndFeelClassName() : UIManager .getCrossPlatformLookAndFeelClassName(); try { UIManager.setLookAndFeel(newLookAndFeel); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) { e.printStackTrace(); } } /** * Class for handling EDT exceptions. * * @author Domagoj Stanković * @version 1.0 */ public static class EDTExceptionHandler implements Thread.UncaughtExceptionHandler { private ILog log; public EDTExceptionHandler(ILog log) { super(); this.log = log; } public void handle(Throwable thrown) { log.log(getStackTraceString(thrown.getStackTrace())); } @Override public void uncaughtException(Thread t, Throwable e) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(baos); e.printStackTrace(ps); try { String message = baos.toString(StandardCharsets.UTF_8.name()); log.log(message); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } } private String getStackTraceString(StackTraceElement[] stackTrace) { StringBuilder sb = new StringBuilder(); for (StackTraceElement ste : stackTrace) { sb.append(ste.toString() + "\n"); } return sb.toString(); } } }