Java tutorial
/* * Copyright (C) 2008 - 2015 Burton Alexander * * 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 2 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, write to the Free Software Foundation, Inc., 51 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ package net.sourceforge.entrainer.gui; import static net.sourceforge.entrainer.gui.EntrainerConstants.ABOUT_ENTRAINER_MENU_NAME; import static net.sourceforge.entrainer.gui.EntrainerConstants.ANIMATION_WINDOW_NAME; import static net.sourceforge.entrainer.gui.EntrainerConstants.FILE_MENU_NAME; import static net.sourceforge.entrainer.gui.EntrainerConstants.HELP_MENU_NAME; import static net.sourceforge.entrainer.gui.EntrainerConstants.NEW_XML_PROGRAM_MENU_NAME; import static net.sourceforge.entrainer.gui.laf.LAFConstants.SKINNABLE_LAF_CLASS_NAME; import static net.sourceforge.entrainer.mediator.MediatorConstants.MESSAGE; import static net.sourceforge.entrainer.mediator.MediatorConstants.START_ENTRAINMENT; import static net.sourceforge.entrainer.util.Utils.openBrowser; import java.awt.AWTException; import java.awt.Component; import java.awt.Dimension; import java.awt.MenuItem; import java.awt.PopupMenu; import java.awt.SystemTray; import java.awt.TrayIcon; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; import java.lang.Thread.UncaughtExceptionHandler; import java.net.URI; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import javafx.application.Platform; import javafx.embed.swing.JFXPanel; import javafx.event.EventHandler; import javafx.geometry.Insets; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.TitledPane; import javafx.scene.image.Image; import javafx.scene.layout.GridPane; import javafx.stage.FileChooser; import javafx.stage.FileChooser.ExtensionFilter; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.JCheckBoxMenuItem; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JRadioButtonMenuItem; import javax.swing.JSeparator; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.UIManager.LookAndFeelInfo; import javax.swing.filechooser.FileFilter; import net.sourceforge.entrainer.EntrainerResources; import net.sourceforge.entrainer.esp.EspConnectionRegister; import net.sourceforge.entrainer.gui.jfx.AnimationPane; import net.sourceforge.entrainer.gui.jfx.BackgroundPicturePane; import net.sourceforge.entrainer.gui.jfx.EntrainerFXSplash; import net.sourceforge.entrainer.gui.jfx.JFXUtils; import net.sourceforge.entrainer.gui.jfx.ShimmerOptionsPane; import net.sourceforge.entrainer.gui.jfx.SliderControlPane; import net.sourceforge.entrainer.gui.jfx.SoundControlPane; import net.sourceforge.entrainer.gui.jfx.animation.JFXAnimationRegister; import net.sourceforge.entrainer.gui.jfx.animation.JFXAnimationWindow; import net.sourceforge.entrainer.gui.jfx.shimmer.AbstractShimmer; import net.sourceforge.entrainer.gui.jfx.shimmer.LinearShimmerRectangle; import net.sourceforge.entrainer.gui.jfx.shimmer.ShimmerRegister; import net.sourceforge.entrainer.gui.popup.NotificationWindow; import net.sourceforge.entrainer.gui.socket.EntrainerSocketConnector; import net.sourceforge.entrainer.guitools.GuiUtil; import net.sourceforge.entrainer.mediator.EntrainerMediator; import net.sourceforge.entrainer.mediator.MediatorConstants; import net.sourceforge.entrainer.mediator.ReceiverAdapter; import net.sourceforge.entrainer.mediator.ReceiverChangeEvent; import net.sourceforge.entrainer.mediator.Sender; import net.sourceforge.entrainer.mediator.SenderAdapter; import net.sourceforge.entrainer.neuroph.jfx.NeuralizerPane; import net.sourceforge.entrainer.pausethread.EntrainerPausibleThread; import net.sourceforge.entrainer.pausethread.PauseEvent; import net.sourceforge.entrainer.socket.EntrainerSocketManager; import net.sourceforge.entrainer.socket.InvalidPortNumberException; import net.sourceforge.entrainer.sound.MasterLevelController; import net.sourceforge.entrainer.sound.SoundControl; import net.sourceforge.entrainer.sound.jsyn.JSynSoundControl; import net.sourceforge.entrainer.sound.tools.EntrainmentFrequencyPulseNotifier; import net.sourceforge.entrainer.sound.tools.Panner; import net.sourceforge.entrainer.xml.Settings; import net.sourceforge.entrainer.xml.SleeperManager; import net.sourceforge.entrainer.xml.SleeperManagerEvent; import net.sourceforge.entrainer.xml.SleeperManagerListener; import org.controlsfx.control.HiddenSidesPane; import org.controlsfx.dialog.Dialogs; import org.pushingpixels.trident.Timeline.TimelineState; import org.pushingpixels.trident.callback.TimelineCallbackAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.github.mrstampy.esp.dsp.lab.DefaultLabValues; import com.github.mrstampy.esp.dsp.lab.Lab; import com.github.mrstampy.esp.dsp.lab.RawEspConnection; import com.github.mrstampy.esp.multiconnectionsocket.ConnectionEvent; import com.github.mrstampy.esp.multiconnectionsocket.ConnectionEventListener; import com.github.mrstampy.esp.multiconnectionsocket.EspChannel; import com.github.mrstampy.esp.multiconnectionsocket.MultiConnectionSocketException; import com.github.mrstampy.esplab.EspPowerLabWindow; // TODO: Auto-generated Javadoc /** * This class is the gui frontend to the Entrainer project. * * @author burton */ public class EntrainerFX extends JFrame implements EntrainerResources { private static final int MIN_HEIGHT = 950; private static final long serialVersionUID = 1L; private static final Logger log = LoggerFactory.getLogger(EntrainerFX.class); // JSyn classes private SoundControl control; // Button controls private SoundControlPane soundControlPane = new SoundControlPane(); private SliderControlPane sliderControlPane = new SliderControlPane(); private IntervalMenu intervalMenu; private SleeperManager sleeperManager; private EntrainerPausibleThread sleeperManagerThread; private static EntrainerFX instance; private Settings settings; private EntrainerSocketManager socket; private boolean entrainerProgramInitialized; private Sender sender = new SenderAdapter(); private List<String> intervalCache = new ArrayList<String>(); private JFXAnimationWindow animationWindow; private JCheckBoxMenuItem connect; private final ImageIcon icon = GuiUtil.getIcon("/Brain.png"); private AbstractShimmer<?> shimmer = ShimmerRegister.getShimmer(LinearShimmerRectangle.NAME); private GridPane gp = new GridPane(); private JFXPanel mainPanel; // private ImageView background = new ImageView(); private EntrainerBackground background = new EntrainerBackground(); private AnimationPane animations = new AnimationPane(); private BackgroundPicturePane pictures = new BackgroundPicturePane(); private Group group; private ShimmerOptionsPane shimmerOptions = new ShimmerOptionsPane(); private Lab lab; private NeuralizerPane neuralizer = new NeuralizerPane(); private MasterLevelController masterLevelController; private JMenu espDevices; private JMenuItem startEspDevice; private JMenuItem stopEspDevice; private JMenuItem showEspLab; private JMenuItem saveEspLab; private JMenuItem loadEspLab; private JMenuItem chooseChannel; private EspConnectionListener espConnectionListener = new EspConnectionListener(); private ObjectMapper jsonMapper = new ObjectMapper(); private JCheckBoxMenuItem splashOnStartup; private HiddenSidesPane hiddenSidesPane; private EntrainerFX() { super("Entrainer FX"); init(); jsonMapper.enable(SerializationFeature.INDENT_OUTPUT); setIconImage(icon.getImage()); addWindowListener(new WindowAdapter() { @Override public void windowOpened(WindowEvent e) { settings.setAcceptUpdates(true); background.setDimension(mainPanel.getWidth(), mainPanel.getHeight()); } }); EntrainmentFrequencyPulseNotifier.start(); } /** * Gets the single instance of Entrainer. * * @return single instance of Entrainer */ public static synchronized EntrainerFX getInstance() { if (instance == null) { final CountDownLatch cdl = new CountDownLatch(1); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { try { if (instance == null) instance = new EntrainerFX(); } finally { cdl.countDown(); } } }); try { cdl.await(); } catch (InterruptedException e) { } instance.setLocationRelativeTo(null); } return instance; } /** * Gets the background image. * * @return the background image */ Image getBackgroundImage() { // TODO fix me return background.getCurrentImage(); } private void fireReceiverChangeEvent(boolean value, MediatorConstants parm) { sender.fireReceiverChangeEvent(new ReceiverChangeEvent(this, value, parm)); } /* * (non-Javadoc) * * @see java.awt.Window#pack() */ public void pack() { setMinimumSize(settings.getMinSize()); super.pack(); } /** * Overridden to set visible property to true always. Circumvents bug when * window is closing and exit is canceled. * * @param b * the new visible */ public void setVisible(boolean b) { if (b) displayComponents(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { EntrainerFX.super.setVisible(true); } }); } private void displayComponents() { mainPanel.setVisible(true); gp.setVisible(true); scaleSize(); SwingUtilities.invokeLater(new Runnable() { @Override public void run() { animationWindow = new JFXAnimationWindow(); initSocket(); initSettings(); setComponentNames(); addSystemTrayIcon(); setMessage("Started Entrainer"); } }); JFXUtils.runLater(new Runnable() { @Override public void run() { setShimmerSizes(); } }); } private void setShimmerSizes() { for (AbstractShimmer<?> shimmer : ShimmerRegister.getShimmers()) { shimmer.setWidth(getWidth()); shimmer.setHeight(getHeight()); } } private void scaleSize() { Dimension d = mainPanel.getPreferredSize(); Dimension screen = GuiUtil.getWorkingScreenSize(); int height = MIN_HEIGHT > screen.getHeight() ? (int) (screen.getHeight() - 50) : MIN_HEIGHT; setSize(new Dimension((int) d.getWidth(), height)); GuiUtil.centerOnScreen(EntrainerFX.this); JFXUtils.runLater(() -> unexpandTitledPanes()); } private void unexpandTitledPanes() { unexpandeTitledPane(sliderControlPane); unexpandeTitledPane(animations); unexpandeTitledPane(shimmerOptions); unexpandeTitledPane(neuralizer); unexpandeTitledPane(pictures); setMinimumSize(new Dimension(mainPanel.getPreferredSize().width, getHeight() / 2)); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { resizeBackground(); } }); } private void resizeBackground() { Dimension size = getSize(); JFXUtils.runLater(() -> setJFXSize(size)); } private void setJFXSize(Dimension size) { double width = size.getWidth(); hiddenSidesPane.setPrefSize(width, size.getHeight()); setTitledPaneWidth(sliderControlPane, width); setTitledPaneWidth(animations, width); setTitledPaneWidth(shimmerOptions, width); setTitledPaneWidth(neuralizer, width); setTitledPaneWidth(pictures, width); setShimmerSizes(); background.setDimension(size.getWidth(), size.getHeight()); } private void setTitledPaneWidth(TitledPane tp, double width) { tp.setPrefWidth(width); } private void unexpandeTitledPane(TitledPane tp) { tp.setExpanded(false); tp.setOpacity(0); } private void addSystemTrayIcon() { if (!SystemTray.isSupported()) return; TrayIcon icon = new TrayIcon(this.icon.getImage()); icon.setPopupMenu(getTrayIconPopup()); icon.setToolTip("EntrainerFX"); try { SystemTray.getSystemTray().add(icon); } catch (AWTException e) { GuiUtil.handleProblem(e); } } private PopupMenu getTrayIconPopup() { PopupMenu pop = new PopupMenu("EntrainerFX"); MenuItem start = new MenuItem("Start EntrainerFX"); start.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { fireReceiverChangeEvent(true, START_ENTRAINMENT); playPressed(); } }); pop.add(start); MenuItem stop = new MenuItem("Stop EntrainerFX"); stop.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { fireReceiverChangeEvent(false, START_ENTRAINMENT); stopPressed(); } }); pop.add(stop); MenuItem exit = new MenuItem("Exit"); exit.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { exitPressed(); } }); pop.add(exit); return pop; } private void init() { Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread arg0, Throwable arg1) { GuiUtil.handleProblem(arg1); } }); intervalMenu = new IntervalMenu(); control = new JSynSoundControl(); masterLevelController = new MasterLevelController(control); initMediator(); wireButtons(); layoutComponents(); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { exitPressed(); } }); initButtonIcons(); createPanner(); addMenu(); initSettings(); addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent e) { if (e.isControlDown() && e.getClickCount() == 1) { openBrowser(getLocalDocAddress()); } } }); } private void setComponentNames() { animationWindow.setName(ANIMATION_WINDOW_NAME); } private void initMediator() { EntrainerMediator.getInstance().addSender(sender); EntrainerMediator.getInstance().addReceiver(new ReceiverAdapter(this) { @Override protected void processReceiverChangeEvent(ReceiverChangeEvent e) { switch (e.getParm()) { case DELTA_AMPLITUDE: setAutoAmplitude(masterLevelController.getAmplitude()); break; case DELTA_ENTRAINMENT_FREQUENCY: setAutoEntrainment(masterLevelController.getEntrainmentFrequency()); break; case DELTA_FREQUENCY: setAutoFrequency(masterLevelController.getFrequency()); break; case DELTA_PINK_NOISE_AMPLITUDE: setAutoPinkNoise(masterLevelController.getPinkNoiseAmplitude()); break; case START_ENTRAINMENT: if (e.getSource() == soundControlPane) break; soundControlPane.setPlaying(e.getBooleanValue()); if (e.getBooleanValue()) { playPressed(); } else { stopPressed(); } break; case SHIMMER_RECTANGLE: setShimmerRectangle(e.getStringValue()); break; case ESP_CONNECTIONS_RELOADED: espDevices.removeAll(); SwingUtilities.invokeLater(() -> addEspDevices(espDevices)); break; case SPLASH_ON_STARTUP: SwingUtilities.invokeLater(() -> splashOnStartup.setSelected(e.getBooleanValue())); break; default: break; } } }); } private void setEspMenuItemsEnabled(boolean booleanValue) { espDevices.setEnabled(!booleanValue); startEspDevice.setEnabled(!booleanValue); stopEspDevice.setEnabled(booleanValue); } private void setShimmerRectangle(final String stringValue) { if (stringValue == null || stringValue.isEmpty()) return; final AbstractShimmer<?> shimmer = ShimmerRegister.getShimmer(stringValue); if (shimmer == null) return; JFXUtils.runLater(new Runnable() { @Override public void run() { swapShimmers(shimmer); } }); } private void swapShimmers(final AbstractShimmer<?> shimmer) { AbstractShimmer<?> old = (AbstractShimmer<?>) group.getChildren().get(1); if (old == shimmer) return; try { group.getChildren().remove(old); old.stop(); old.setInUse(false); shimmer.setInUse(true); group.getChildren().add(1, shimmer); if (control.isPlaying()) shimmer.start(); } catch (Throwable e) { GuiUtil.handleProblem(e); } } private void initButtonIcons() { soundControlPane.setPlayToolTip("Start Entrainment"); soundControlPane.setRecordToolTip("Flag/Clear Recording to '.wav' File"); soundControlPane.setStopToolTip("Stop Entrainment"); soundControlPane.setPauseToolTip("Pause/Resume an Entrainer Program"); animations.setAnimationToolTip("Run Animation During Entrainment Session"); shimmerOptions.setShimmerToolTip("Adds a shimmer effect to the application"); } private void initSettings() { settings = Settings.reload(); if (settings.getXmlProgram() != null && !settings.getXmlProgram().isEmpty() && isVisible()) { readXmlFile(settings.getXmlProgram()); } else { enableControls(true); } } private void initSocket() { if (socket == null) { socket = new EntrainerSocketManager(); if (settings.isSocketConnected()) { bindSocket(); if (isSocketBound()) connect.setSelected(true); } } } private void addMenu() { JMenuBar bar = new JMenuBar(); JMenu menu = new JMenu(FILE_MENU_NAME); addMnemonic(menu, KeyEvent.VK_F); menu.add(getStartItem()); menu.add(getStopItem()); menu.add(new JSeparator()); menu.add(getLoadXmlItem()); menu.add(getClearXmlItem()); menu.add(getEditXmlItem()); menu.add(getNewXmlItem()); menu.add(new JSeparator()); menu.add(getExitItem()); bar.add(menu); bar.add(getLookAndFeels()); bar.add(intervalMenu); bar.add(getEspMenu()); bar.add(getSocketMenu()); JMenu help = new JMenu(HELP_MENU_NAME); addMnemonic(help, KeyEvent.VK_H); help.add(getAboutItem()); help.add(getLicenseItem()); help.add(getLocalDocItem()); help.add(getRemoteDocItem()); help.add(getSplashItem()); help.add(getSplashOnStartupItem()); bar.add(help); setJMenuBar(bar); } private JMenuItem getSplashOnStartupItem() { splashOnStartup = new JCheckBoxMenuItem("Splash on Startup"); splashOnStartup.setToolTipText("Enables/Disables splash screen on startup"); splashOnStartup.addActionListener(e -> enableSplashOnStartup(splashOnStartup.isSelected())); return splashOnStartup; } private void enableSplashOnStartup(boolean selected) { fireReceiverChangeEvent(selected, MediatorConstants.SPLASH_ON_STARTUP); } private JMenuItem getRemoteDocItem() { JMenuItem item = new JMenuItem("Web Documentation"); addMnemonic(item, KeyEvent.VK_W); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { openBrowser("http://entrainer.sourceforge.net"); } }); return item; } private JMenuItem getLocalDocItem() { JMenuItem item = new JMenuItem("Local Documentation"); addMnemonic(item, KeyEvent.VK_D); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { openBrowser(getLocalDocAddress()); } }); return item; } private URI getLocalDocAddress() { return new File(EFX_DOC_DIR + "/index.html").toURI(); } private JMenuItem getLicenseItem() { JMenuItem aboutItem = new JMenuItem("License"); addMnemonic(aboutItem, KeyEvent.VK_I); aboutItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { License.showLicenseDialog(); } }); return aboutItem; } private JMenuItem getEditXmlItem() { JMenuItem item = new JMenuItem("Edit Entrainer Program"); addMnemonic(item, KeyEvent.VK_E); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { editXml(); } }); return item; } private JMenuItem getNewXmlItem() { JMenuItem item = new JMenuItem(NEW_XML_PROGRAM_MENU_NAME); addMnemonic(item, KeyEvent.VK_N); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { newXml(); } }); return item; } private JMenuItem getLoadXmlItem() { JMenuItem item = new JMenuItem("Load Entrainer Program"); addMnemonic(item, KeyEvent.VK_L); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { loadXml(); } }); return item; } private JMenuItem getClearXmlItem() { JMenuItem item = new JMenuItem("Clear Entrainer Program"); addMnemonic(item, KeyEvent.VK_C); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { clearXmlFile(); } }); return item; } private void addMnemonic(JMenuItem item, int charKey) { item.setMnemonic(charKey); if (!(item instanceof JMenu)) { item.setAccelerator(KeyStroke.getKeyStroke(charKey, InputEvent.CTRL_DOWN_MASK)); } } private JMenuItem getAboutItem() { JMenuItem aboutItem = new JMenuItem(ABOUT_ENTRAINER_MENU_NAME); addMnemonic(aboutItem, KeyEvent.VK_B); aboutItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { About.showAboutDialog(); } }); return aboutItem; } private JMenuItem getSplashItem() { JMenuItem splashItem = new JMenuItem("Show Splash Screen"); addMnemonic(splashItem, KeyEvent.VK_P); splashItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { new EntrainerFXSplash(true); } }); return splashItem; } private JMenuItem getExitItem() { JMenuItem exitItem = new JMenuItem("Exit"); addMnemonic(exitItem, KeyEvent.VK_X); exitItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { exitPressed(); } }); return exitItem; } private JMenuItem getStopItem() { JMenuItem stopItem = new JMenuItem("Stop"); addMnemonic(stopItem, KeyEvent.VK_T); stopItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { soundControlPane.getStop().fire(); } }); return stopItem; } private JMenuItem getStartItem() { JMenuItem startItem = new JMenuItem("Start"); addMnemonic(startItem, KeyEvent.VK_S); startItem.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { soundControlPane.getPlay().fire(); } }); return startItem; } private JMenu getLookAndFeels() { JMenu menu = new JMenu("Look and Feel"); addMnemonic(menu, KeyEvent.VK_K); LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels(); Map<String, List<LookAndFeelInfo>> sortedInfos = sortInfos(infos); for (String key : sortedInfos.keySet()) { List<LookAndFeelInfo> sorted = sortedInfos.get(key); JMenu lafMenu = new JMenu(key); for (LookAndFeelInfo info : sorted) { JMenuItem item = addLookAndFeel(info); lafMenu.add(item); } if (!sorted.isEmpty()) { menu.add(lafMenu); } } return menu; } private Map<String, List<LookAndFeelInfo>> sortInfos(LookAndFeelInfo[] infos) { Map<String, List<LookAndFeelInfo>> sorted = new LinkedHashMap<String, List<LookAndFeelInfo>>(); String[] known = { "substance", "jgoodies", "jtattoo" }; sorted.put("Substance", getLAFInfo("substance", infos)); sorted.put("JGoodies", getLAFInfo("jgoodies", infos)); sorted.put("JTattoo", getLAFInfo("jtattoo", infos)); sorted.put("Other", getOtherLAFInfo(known, infos)); return sorted; } private List<LookAndFeelInfo> getOtherLAFInfo(String[] known, LookAndFeelInfo[] infos) { List<LookAndFeelInfo> list = new ArrayList<LookAndFeelInfo>(); for (LookAndFeelInfo info : infos) { if (!isKnownLAFInfo(info, known)) { list.add(info); } } return list; } private boolean isKnownLAFInfo(LookAndFeelInfo info, String[] known) { for (String s : known) { if (info.getClassName().contains(s)) { return true; } } return false; } private List<LookAndFeelInfo> getLAFInfo(String name, LookAndFeelInfo[] infos) { List<LookAndFeelInfo> list = new ArrayList<LookAndFeelInfo>(); for (LookAndFeelInfo info : infos) { if (info.getClassName().contains(name)) { list.add(info); } } return list; } private JMenuItem addLookAndFeel(final LookAndFeelInfo info) { if (info.getClassName().equals(SKINNABLE_LAF_CLASS_NAME)) { return getSkinnableLookAndFeelMenu(info); } JMenuItem item = new JMenuItem(info.getName()); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { changeLookAndFeel(info.getClassName(), null); } }); return item; } private JMenu getSkinnableLookAndFeelMenu(LookAndFeelInfo info) { JMenu menu = new JMenu(info.getName()); File themePacks = new File("lafs/skins"); File[] packs = themePacks.listFiles(new java.io.FileFilter() { public boolean accept(File pathname) { return pathname.isFile() && pathname.getName().toLowerCase().endsWith(".zip"); } }); for (int i = 0; i < packs.length; i++) { menu.add(getThemePackItem(info, packs[i])); } return menu; } private JMenuItem getThemePackItem(final LookAndFeelInfo info, final File pack) { JMenuItem item = new JMenuItem(getPackName(pack)); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { changeLookAndFeel(info.getClassName(), pack.getAbsolutePath()); } }); return item; } private void changeLookAndFeel(String className, String themePack) { GuiUtil.changeLookAndFeel(className, themePack, this); fireReceiverChangeEvent(className, MediatorConstants.LOOK_AND_FEEL); fireReceiverChangeEvent(themePack, MediatorConstants.THEME_PACK); } private void fireReceiverChangeEvent(String value, MediatorConstants parm) { sender.fireReceiverChangeEvent(new ReceiverChangeEvent(this, value, parm)); } private JMenu getSocketMenu() { JMenu menu = new JMenu("External Sockets"); addMnemonic(menu, KeyEvent.VK_R); menu.add(getChangePortItem()); menu.add(getConnectorGuiItem()); menu.add(getConnectSocketItem()); return menu; } private JMenu getEspMenu() { JMenu menu = new JMenu("EEG Signal Processing"); addMnemonic(menu, KeyEvent.VK_G); menu.add(getEspDeviceMenu()); menu.add(new JSeparator()); menu.add(chooseChannelMenu()); menu.add(new JSeparator()); menu.add(getStartEspMenu()); menu.add(getStopEspMenu()); menu.add(new JSeparator()); menu.add(loadLabMenu()); menu.add(saveLabMenu()); menu.add(new JSeparator()); menu.add(showEspLabMenu()); setEspMenuItemsEnabled(); return menu; } private JMenuItem chooseChannelMenu() { chooseChannel = new JMenuItem("Choose Channel"); chooseChannel.addActionListener(e -> JFXUtils.runLater(() -> chooseChannel())); return chooseChannel; } private void chooseChannel() { //@formatter:off EspChannel channel = Dialogs.create().title("Choose Channel").message("Choose the channel for processing") .showChoices(lab.getConnection().getChannels()); //@formatter:on if (channel != null) lab.setChannel(channel.getChannelNumber()); } private JMenuItem loadLabMenu() { loadEspLab = new JMenuItem("Load ESP Lab Settings"); loadEspLab.addActionListener(e -> JFXUtils.runLater(() -> loadLabSettings())); return loadEspLab; } private void loadLabSettings() { FileChooser chooser = getLabFileChooser("Load"); File labfile = chooser.showOpenDialog(null); if (labfile != null) loadLab(labfile); } private void loadLab(File labfile) { try { DefaultLabValues values = jsonMapper.readValue(labfile, DefaultLabValues.class); lab.setLabValues(values); } catch (Exception e) { GuiUtil.handleProblem(e); } } private JMenuItem saveLabMenu() { saveEspLab = new JMenuItem("Save ESP Lab Settings"); saveEspLab.addActionListener(e -> JFXUtils.runLater(() -> showSaveLabSettings())); return saveEspLab; } private void showSaveLabSettings() { FileChooser chooser = getLabFileChooser("Save"); File labfile = chooser.showSaveDialog(null); if (labfile != null) saveLab(labfile); } private FileChooser getLabFileChooser(String operation) { File labdir = getLabDir(); FileChooser chooser = new FileChooser(); chooser.setInitialDirectory(labdir); chooser.setTitle(operation + " ESP Lab Settings"); chooser.getExtensionFilters().add(new ExtensionFilter("ESP Lab Files", "*.lab")); return chooser; } private File getLabDir() { File labdir = new File("esp.lab"); if (!labdir.exists()) labdir.mkdir(); return labdir; } private void saveLab(File labfile) { try { jsonMapper.writeValue(labfile, lab.getLabValues()); } catch (Exception e) { GuiUtil.handleProblem(e); } } private void setEspMenuItemsEnabled() { boolean enable = lab != null && lab.getConnection() != null; startEspDevice.setEnabled(enable); stopEspDevice.setEnabled(false); showEspLab.setEnabled(enable); saveEspLab.setEnabled(enable); loadEspLab.setEnabled(enable); chooseChannel.setEnabled(enable ? lab.getNumChannels() > 1 : false); } private JMenuItem showEspLabMenu() { showEspLab = new JMenuItem("Show ESP Lab"); showEspLab.addActionListener(e -> JFXUtils.runLater(() -> showEspLab())); return showEspLab; } private void showEspLab() { if (!connectionCheck()) return; EspPowerLabWindow espLab = new EspPowerLabWindow(lab); espLab.addEventFilter(javafx.stage.WindowEvent.WINDOW_CLOSE_REQUEST, e -> setEspLabShowingEnabled(true)); setEspLabShowingEnabled(false); espLab.show(); } private void setEspLabShowingEnabled(boolean enabled) { espDevices.setEnabled(enabled); showEspLab.setEnabled(enabled); } private JMenuItem getStopEspMenu() { stopEspDevice = new JMenuItem("Stop ESP Device"); stopEspDevice.addActionListener(e -> stopEspDevice()); return stopEspDevice; } private void stopEspDevice() { if (!connectionCheck()) return; if (!lab.getConnection().isConnected()) return; JFXUtils.runLater(() -> lab.getConnection().stop()); } private JMenuItem getStartEspMenu() { startEspDevice = new JMenuItem("Start ESP Device"); startEspDevice.addActionListener(e -> startEspDevice()); return startEspDevice; } private void startEspDevice() { if (!connectionCheck()) return; if (lab.getConnection().isConnected()) return; JFXUtils.runLater(() -> { try { lab.getConnection().start(); } catch (MultiConnectionSocketException e) { GuiUtil.handleProblem(e); } }); } private boolean connectionCheck() { if (lab.getConnection() == null) { if (Platform.isFxApplicationThread()) { Dialogs.create().title("No ESP Device Selected").message("Choose an ESP device first") .showWarning(); } else { JOptionPane.showMessageDialog(this, "Choose an ESP device first", "No ESP Device Selected", JOptionPane.ERROR_MESSAGE); } return false; } return true; } private JMenu getEspDeviceMenu() { espDevices = new JMenu("Choose ESP Device"); EspConnectionRegister.getEspConnections(); return espDevices; } private void addEspDevices(JMenu menu) { List<RawEspConnection> connections = EspConnectionRegister.getEspConnections(); ButtonGroup bg = new ButtonGroup(); connections.forEach(connection -> addEspDevice(menu, connection, bg)); } private void addEspDevice(JMenu menu, RawEspConnection connection, ButtonGroup bg) { JRadioButtonMenuItem item = new JRadioButtonMenuItem(connection.getName()); bg.add(item); menu.add(item); item.addActionListener(e -> setConnection(connection)); } private void setConnection(RawEspConnection connection) { EspConnectionRegister.getEspConnection(connection.getName()); setLabFrom(connection); lab.setConnection(connection); setEspMenuItemsEnabled(); setEspMenuItemText(connection.getName()); connection.addConnectionEventListener(espConnectionListener); } private void setLabFrom(RawEspConnection connection) { Lab connectionLab = connection.getDefaultLab(); connectionLab.setNumBands(41); lab = connectionLab; neuralizer.setLab(lab); } private void setEspMenuItemText(String name) { startEspDevice.setText("Start " + name); stopEspDevice.setText("Stop " + name); } private JMenuItem getConnectorGuiItem() { JMenuItem item = new JMenuItem("Show Connector GUI"); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { showConnectorGui(); } }); item.setToolTipText("Show the Connector GUI, allowing sending and receiving of Entrainer state messages"); return item; } private void showConnectorGui() { try { EntrainerSocketConnector esc = new EntrainerSocketConnector(settings.getSocketIPAddress(), settings.getSocketPort()); esc.pack(); GuiUtil.centerOnScreen(esc); GuiUtil.addFadeOutInvisibleListener(esc, 500); GuiUtil.fadeIn(esc, 500); } catch (UnknownHostException e) { GuiUtil.handleProblem(e); } } private JMenuItem getChangePortItem() { JMenuItem item = new JMenuItem("Choose Socket Host & Port"); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { showSocketPortDialog(); } }); item.setToolTipText("Choose the host and port on which Entrainer sends and receives state messages"); return item; } private JMenuItem getConnectSocketItem() { connect = new JCheckBoxMenuItem("Accept Connections"); connect.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent arg0) { if (connect.isSelected()) { bindSocket(); } else { unbindSocket(); } } }); connect.setToolTipText("Bind / unbind the socket on which Entrainer sends and receives state messages"); return connect; } private boolean isSocketBound() { return socket.isBound(); } private void unbindSocket() { int portNum = socket.getPortNumber(); String hostName = socket.getHostName(); socket.unbind(); new NotificationWindow("Entrainer socket unbound from host " + hostName + " and port " + portNum, this); settings.setSocketConnected(false); } private void bindSocket() { if (Settings.getInstance().getSocketPort() <= 0) showSocketPortDialog(); try { socket.bind(); new NotificationWindow("Entrainer socket bound to host " + socket.getHostName() + " and port " + socket.getPortNumber(), this); settings.setSocketConnected(true); } catch (IOException e) { GuiUtil.handleProblem(e); connect.setSelected(false); } catch (InvalidPortNumberException e) { JOptionPane.showMessageDialog(EntrainerFX.this, "The port number " + e.getPort() + " is not valid", "Invalid Port Number", JOptionPane.ERROR_MESSAGE); connect.setSelected(false); } } private void showSocketPortDialog() { try { GuiUtil.showDialog(new SocketPortDialog()); } catch (UnknownHostException e) { GuiUtil.handleProblem(e); } } private String getPackName(File pack) { String name = pack.getName(); name = name.substring(0, name.indexOf("themepack.zip")); return name; } private void editXml() { JFileChooser xmlChooser = XmlEditor.getXmlFileChooser(); int val = xmlChooser.showOpenDialog(this); if (val == JFileChooser.APPROVE_OPTION) { showXmlEditor(xmlChooser.getSelectedFile()); } } private void showXmlEditor(File f) { intervalCache = intervalMenu.removeAllIntervals(); final XmlEditor editor = new XmlEditor(this, f); editor.addXmlFileSaveListener(new XmlFileSaveListener() { public void xmlFileSaveEventPerformed(XmlFileSaveEvent e) { xmlFileSaved(e.getXmlFile()); } }); editor.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { intervalMenu.loadIntervals(intervalCache); intervalMenu.loadCustomIntervals(); } }); GuiUtil.showDialog(editor); } private void xmlFileSaved(File xmlFile) { if (sleeperManager != null && !soundControlPane.getPlay().isDisabled() && sleeperManager.getXml().getFile().equals(xmlFile.getAbsoluteFile())) { readXmlFile(xmlFile.getName()); } } private void newXml() { showXmlEditor(null); } private void wireButtons() { soundControlPane.getPlay().addEventHandler(javafx.event.ActionEvent.ACTION, new EventHandler<javafx.event.ActionEvent>() { @Override public void handle(javafx.event.ActionEvent arg0) { playPressed(); } }); soundControlPane.getStop().addEventHandler(javafx.event.ActionEvent.ACTION, new EventHandler<javafx.event.ActionEvent>() { @Override public void handle(javafx.event.ActionEvent arg0) { stopPressed(); } }); soundControlPane.getPause().addEventHandler(javafx.event.ActionEvent.ACTION, new EventHandler<javafx.event.ActionEvent>() { @Override public void handle(javafx.event.ActionEvent arg0) { pausePressed(); } }); soundControlPane.getRecord().addEventHandler(javafx.event.ActionEvent.ACTION, new EventHandler<javafx.event.ActionEvent>() { @Override public void handle(javafx.event.ActionEvent arg0) { recordClicked(); } }); } private void recordClicked() { if (soundControlPane.getRecord().isSelected()) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { boolean recording = showWavFileChooser(); JFXUtils.runLater(() -> recordClicked(recording)); } }); } else { control.setWavFile(null); setMessage("Recording cancelled"); soundControlPane.setRecordingEntrainerProgram(false); control.setRecord(false); } } private void recordClicked(boolean recording) { soundControlPane.getRecord().setSelected(recording); control.setRecord(recording); if (control.getWavFile() != null) { String msg = "Prepared to record to " + control.getWavFile().getName(); setMessage(msg); soundControlPane.setRecordingEntrainerProgram(true); } } private boolean showWavFileChooser() { JFileChooser wavChooser = getWavFileChooser(); int val = wavChooser.showDialog(this, "Ok"); if (val == JFileChooser.APPROVE_OPTION) { File wavFile = processFile(wavChooser.getSelectedFile()); if (!isValidFile(wavFile)) { JOptionPane.showMessageDialog(this, wavFile.getName() + " is not a valid WAV file name\n(it must end with a '.wav' extension)", "Invalid WAV File Name", JOptionPane.WARNING_MESSAGE); return false; } control.setWavFile(wavFile); return true; } control.setWavFile(null); return false; } private File processFile(File selected) { if (isUntypedFile(selected)) { return new File(selected.getAbsolutePath() + ".wav"); } return selected; } private boolean isUntypedFile(File f) { return f.getName().indexOf(".") == -1; } private boolean isValidFile(File f) { return f.getName().indexOf(".wav") == f.getName().length() - 4; } private JFileChooser getWavFileChooser() { JFileChooser wavChooser = new JFileChooser(new File(System.getProperty("user.dir") + "/wav")); wavChooser.setDialogTitle("WAV File Chooser, select or enter WAV file for recording"); wavChooser.setFileFilter(new FileFilter() { @Override public boolean accept(File f) { return f.isDirectory() || f.getName().indexOf(".wav") > 0; } @Override public String getDescription() { return "WAV files"; } }); return wavChooser; } private void stopPressed() { if (isPaused()) return; SwingUtilities.invokeLater(new Runnable() { @Override public void run() { setMessage(control.isRecord() ? "Recording Stopped" : "Stopped"); if (animationWindow != null) { animationWindow.setVisible(false); } stop(); } }); } private boolean isPaused() { return soundControlPane.getPause().isSelected(); } private void playPressed() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (animations.getAnimation().isSelected()) { startAnimation(); } start(); if (!animations.getAnimation().isSelected()) { setMessage(control.isRecord() ? "Recording Started" : "Entrainment Started"); } } }); } private void startAnimation() { if (animationWindow.isVisible()) { return; } if (animationWindow.getEntrainerAnimation() == null) { animationWindow.setEntrainerAnimation(JFXAnimationRegister.getEntrainerAnimations().get(0)); } animationWindow.setVisible(true); toFront(); } private void pausePressed() { pauseEventPerformed(); if (soundControlPane.getPause().isSelected()) { pause(); setMessage("Paused"); } else { resume(); setMessage("Resumed"); } } private void pauseEventPerformed() { Thread pauseThread = new Thread() { public void run() { PauseEvent e = new PauseEvent(this, soundControlPane.getPause().isSelected() ? PauseEvent.PAUSE : PauseEvent.RESUME); sleeperManagerThread.pauseEventPerformed(e); } }; pauseThread.start(); } private void initializeControls() { if (!entrainerProgramInitialized) { sleeperManager.initGlobalSettings(); entrainerProgramInitialized = true; soundControlPane.setPlayingEntrainerProgram(true); } } private void setAutoAmplitude(double value) { if (sliderControlPane.getAmplitude().getValue() == value) return; sliderControlPane.setAmplitudeValue(value); } private void setAutoPinkNoise(double value) { if (sliderControlPane.getPinkNoise().getValue() == value) return; sliderControlPane.setPinkNoiseValue(value); } private void setAutoFrequency(double value) { if (sliderControlPane.getFrequency().getValue() == value) return; sliderControlPane.setFrequencyValue(value); } private void setAutoEntrainment(double value) { sliderControlPane.setEntrainmentFrequencyValue(value); } private void enableControls(final boolean enabled) { JFXUtils.runLater(() -> sliderControlPane.setControlsDisabled(!enabled)); getFileMenuItem("New Entrainer Program").setEnabled(enabled); getFileMenuItem("Edit Entrainer Program").setEnabled(enabled); getFileMenuItem("Clear Entrainer Program").setEnabled(!enabled); getFileMenuItem("Load Entrainer Program").setEnabled(enabled); } private JMenuItem getFileMenuItem(String text) { JMenu file = getJMenuBar().getMenu(0); Component[] comps = file.getMenuComponents(); JMenuItem jmi; for (Component c : comps) { if (c instanceof JMenuItem) { jmi = (JMenuItem) c; if (jmi.getText().equals(text)) { return jmi; } } } return null; } private void exitPressed() { stopPressed(); int option = JOptionPane.showConfirmDialog(this, "Exiting: Confirm?", "Exit Entrainer", JOptionPane.YES_NO_OPTION); if (option == JOptionPane.YES_OPTION) { new NotificationWindow("Exiting Entrainer", this); control.exit(); exitApplication(); } } private void exitApplication() { TimelineCallbackAdapter tca = new TimelineCallbackAdapter() { public void onTimelineStateChanged(TimelineState oldState, TimelineState newState, float durationFraction, float timelinePosition) { if (TimelineState.DONE == newState || TimelineState.CANCELLED == newState) { System.exit(0); } } }; GuiUtil.fadeOut(this, 5000, tca); } private void start() { if (isXmlProgram()) { initializeControls(); } if (isXmlProgram()) { sleeperManagerThread = sleeperManager.start(); } } private void resume() { control.resume(); } private boolean isXmlProgram() { return sleeperManager != null; } private void createPanner() { Panner.getInstance(); } private void stop() { if (isXmlProgram()) { sleeperManager.stop(); } entrainerProgramInitialized = false; } private void pause() { control.pause(); } private void layoutComponents() { mainPanel = new JFXPanel(); int v = 0; GridPane.setConstraints(sliderControlPane, 0, v++); GridPane.setMargin(sliderControlPane, new Insets(20, 0, 0, 0)); GridPane.setConstraints(pictures, 0, v++); GridPane.setConstraints(animations, 0, v++); GridPane.setConstraints(shimmerOptions, 0, v++); GridPane.setConstraints(neuralizer, 0, v++); gp.setPadding(new Insets(5, 13, 5, 5)); gp.getChildren().addAll(sliderControlPane, animations, shimmerOptions, pictures, neuralizer); hiddenSidesPane = new HiddenSidesPane(); hiddenSidesPane.setContent(gp); hiddenSidesPane.setTop(soundControlPane); hiddenSidesPane.setTriggerDistance(25); final URI css = JFXUtils.getEntrainerCSS(); JFXUtils.runLater(new Runnable() { @Override public void run() { group = new Group(); shimmer.setInUse(true); group.getChildren().addAll(background.getPane(), shimmer, hiddenSidesPane); Scene scene = new Scene(group); if (css != null) scene.getStylesheets().add(css.toString()); mainPanel.setScene(scene); } }); getContentPane().add(mainPanel); } private void setMessage(String s) { sender.fireReceiverChangeEvent(new ReceiverChangeEvent(this, s, MESSAGE)); } private void loadXml() { intervalMenu.removeAllIntervals(); JFileChooser chooser = XmlEditor.getXmlFileChooser(); int val = chooser.showOpenDialog(this); if (val == JFileChooser.APPROVE_OPTION) { File f = chooser.getSelectedFile(); if (f != null) { try { clearXmlFile(); readXmlFile(f.getName()); soundControlPane.setPlayingEntrainerProgram(true); } catch (Exception e) { GuiUtil.handleProblem(e); soundControlPane.setPlayingEntrainerProgram(false); } } } } private void readXmlFile(String fileName) { sleeperManager = new SleeperManager(fileName); sleeperManager.addSleeperManagerListener(new SleeperManagerListener() { public void sleeperManagerEventPerformed(SleeperManagerEvent e) { if (e.isStopped()) { fireReceiverChangeEvent(false, START_ENTRAINMENT); stopPressed(); soundControlPane.setPlaying(false); } } }); enableControls(false); initializeControls(); setMessage("Loaded " + fileName); settings.setXmlProgram(fileName); } private void clearXmlFile() { if (sleeperManager != null) { sleeperManager.clearMediatorObjects(); sleeperManager = null; sleeperManagerThread = null; setMessage("Cleared Entrainer Program"); entrainerProgramInitialized = false; settings.setXmlProgram(""); enableControls(true); soundControlPane.getPause().setDisable(true); soundControlPane.setPlayingEntrainerProgram(false); } } private class EspConnectionListener implements ConnectionEventListener { @Override public void connectionEventPerformed(ConnectionEvent e) { switch (e.getState()) { case STARTED: fireReceiverChangeEvent(true, MediatorConstants.ESP_START); setEspMenuItemsEnabled(true); break; case ERROR_UNBOUND: case ERROR_STOPPED: case STOPPED: fireReceiverChangeEvent(false, MediatorConstants.ESP_START); setEspMenuItemsEnabled(false); break; default: break; } } } }