/******************************************************************************* * GenPlay, Einstein Genome Analyzer * Copyright (C) 2009, 2014 Albert Einstein College of Medicine * * 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/>. * Authors: Julien Lajugie <julien.lajugie@einstein.yu.edu> * Nicolas Fourel <nicolas.fourel@einstein.yu.edu> * Eric Bouhassira <eric.bouhassira@einstein.yu.edu> * * Website: <http://genplay.einstein.yu.edu> ******************************************************************************/ package edu.yu.einstein.genplay.gui.mainFrame; import java.awt.Dimension; import java.awt.Frame; import java.awt.GraphicsEnvironment; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JOptionPane; import javax.swing.WindowConstants; import edu.yu.einstein.genplay.core.manager.application.ConfigurationManager; import edu.yu.einstein.genplay.core.manager.project.ProjectChromosomes; import edu.yu.einstein.genplay.core.manager.project.ProjectManager; import edu.yu.einstein.genplay.core.manager.project.ProjectWindow; import edu.yu.einstein.genplay.core.manager.recording.RecordingManager; import edu.yu.einstein.genplay.gui.MGDisplaySettings.MGDisplaySettings; import edu.yu.einstein.genplay.gui.OSIntegration.OSXIntegrator; import edu.yu.einstein.genplay.gui.action.multiGenome.properties.MGARefresh; import edu.yu.einstein.genplay.gui.action.project.PAAbout; import edu.yu.einstein.genplay.gui.action.project.PABookmarkCurrentPosition; import edu.yu.einstein.genplay.gui.action.project.PACheckForUpdates; import edu.yu.einstein.genplay.gui.action.project.PAContactUs; import edu.yu.einstein.genplay.gui.action.project.PACopyCurrentPosition; import edu.yu.einstein.genplay.gui.action.project.PAExit; import edu.yu.einstein.genplay.gui.action.project.PAFullScreen; import edu.yu.einstein.genplay.gui.action.project.PAHelp; import edu.yu.einstein.genplay.gui.action.project.PALoadProject; import edu.yu.einstein.genplay.gui.action.project.PAMoveFarLeft; import edu.yu.einstein.genplay.gui.action.project.PAMoveFarRight; import edu.yu.einstein.genplay.gui.action.project.PAMoveLeft; import edu.yu.einstein.genplay.gui.action.project.PAMoveRight; import edu.yu.einstein.genplay.gui.action.project.PANewProject; import edu.yu.einstein.genplay.gui.action.project.PAOption; import edu.yu.einstein.genplay.gui.action.project.PARNAPosToDNAPos; import edu.yu.einstein.genplay.gui.action.project.PAReportBug; import edu.yu.einstein.genplay.gui.action.project.PASaveProject; import edu.yu.einstein.genplay.gui.action.project.PASaveProjectAs; import edu.yu.einstein.genplay.gui.action.project.PAShowErrorReport; import edu.yu.einstein.genplay.gui.action.project.PAShowWarningReport; import edu.yu.einstein.genplay.gui.action.project.PASortFile; import edu.yu.einstein.genplay.gui.action.project.PAZoomIn; import edu.yu.einstein.genplay.gui.action.project.PAZoomOut; import edu.yu.einstein.genplay.gui.action.track.TAAddLayer; import edu.yu.einstein.genplay.gui.action.track.TAAddLayerFromDAS; import edu.yu.einstein.genplay.gui.action.track.TAAddVariantLayer; import edu.yu.einstein.genplay.gui.action.track.TACopy; import edu.yu.einstein.genplay.gui.action.track.TACut; import edu.yu.einstein.genplay.gui.action.track.TADelete; import edu.yu.einstein.genplay.gui.action.track.TAInsert; import edu.yu.einstein.genplay.gui.action.track.TAPasteOrDrop; import edu.yu.einstein.genplay.gui.action.track.TASaveAsImage; import edu.yu.einstein.genplay.gui.action.track.TASaveTrack; import edu.yu.einstein.genplay.gui.action.track.TATrackSettings; import edu.yu.einstein.genplay.gui.controlPanel.ControlPanel; import edu.yu.einstein.genplay.gui.dialog.optionDialog.OptionDialog; import edu.yu.einstein.genplay.gui.event.genomeWindowEvent.GenomeWindowEvent; import edu.yu.einstein.genplay.gui.event.genomeWindowEvent.GenomeWindowListener; import edu.yu.einstein.genplay.gui.menu.MainMenu; import edu.yu.einstein.genplay.gui.menu.MenuBar; import edu.yu.einstein.genplay.gui.statusBar.StatusBar; import edu.yu.einstein.genplay.gui.track.ruler.Ruler; import edu.yu.einstein.genplay.gui.trackList.TrackListModel; import edu.yu.einstein.genplay.gui.trackList.TrackListPanel; import edu.yu.einstein.genplay.util.Images; import edu.yu.einstein.genplay.util.LookAndFeels; import edu.yu.einstein.genplay.util.Utils; import edu.yu.einstein.genplay.util.colors.Colors; /** * Main Frame of the application. * @author Julien Lajugie * @author Nicolas Fourel */ public final class MainFrame extends JFrame implements GenomeWindowListener, ActionListener { /** Generated ID */ private static final long serialVersionUID = -4637394760647080396L; /** Major version of GenPlay */ private static final int VERSION_MAJOR = 1; /** Minor version of GenPlay */ private static final int VERSION_MINOR = 1; /** Build version of GenPlay */ private static final int VERSION_BUILD = 0; /** GenPlay version */ public static final String GENPLAY_VERSION = VERSION_MAJOR + "." + VERSION_MINOR + "." + VERSION_BUILD; /** Title of the application */ public static final String APPLICATION_TITLE = "GenPlay, Einstein Genome Analyzer (v" + GENPLAY_VERSION + ")"; /** Default size of the application */ private final static Dimension WINDOW_DEFAULT_SIZE = new Dimension(800, 600); /** Minimum size of the application */ private final static Dimension WINDOW_MINIMUM_SIZE = new Dimension(200, 150); /** Instance of the singleton MainFrame */ private static MainFrame instance = null; /** * @return the instance of the singleton MainFrame */ public static MainFrame getInstance() { if (instance == null) { synchronized (MainFrame.class) { if (instance == null) { instance = new MainFrame(); } } } return instance; } /** * @return true if the main frame has been initialized, false otherwise */ public static boolean isInitialized() { return instance != null; } /** * Reinit the {@link ProjectChromosomes} and the chromosome panel of the {@link ControlPanel} if needed */ public static void reinit() { // if instance is null the mainframe has never been initialized // so there is no need to do a reinit if (instance != null) { ProjectManager.getInstance().updateChromosomeList(); instance.getControlPanel().reinitChromosomePanel(); // creates a new model and register the tracks to the project window manager TrackListModel trackListModel = new TrackListModel(); instance.getTrackListPanel().setModel(trackListModel); ProjectManager.getInstance().getProjectWindow().removeAllListeners(); instance.setTitle(); instance.getStatusBar().reinit(); } } private final Ruler ruler; // Ruler component private final TrackListPanel trackListPanel; // TrackList component private final ControlPanel controlPanel; // ControlPanel component private final StatusBar statusBar; // Status bar component private Rectangle screenBounds; // position and dimension of this frame private boolean isLocked; // true if the main frame is locked /** * Private constructor. Creates an instance of singleton {@link MainFrame} */ private MainFrame() { super(GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration()); setIconImages(Images.getApplicationImages()); setTitle(); ruler = new Ruler(); ruler.getOptionButton().addActionListener(this); TrackListModel trackListModel = new TrackListModel(); trackListPanel = new TrackListPanel(trackListModel); controlPanel = new ControlPanel(); statusBar = new StatusBar(); setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.BOTH; gbc.weightx = 1; gbc.weighty = 0; add(ruler.getRulerPanel(), gbc); gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.BOTH; gbc.gridy = 1; gbc.weightx = 1; gbc.weighty = 1; add(trackListPanel, gbc); gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.BOTH; gbc.gridy = 2; gbc.weightx = 1; gbc.weighty = 0; add(controlPanel, gbc); gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.BOTH; gbc.insets = new Insets(10, 0, 0, 0); gbc.gridy = 3; gbc.weightx = 1; gbc.weighty = 0; add(statusBar, gbc); // register to the genome window manager so it can be notified when the project window changes ProjectWindow projectWindow = ProjectManager.getInstance().getProjectWindow(); projectWindow.addGenomeWindowListener(this); // create actions setActionMap(); // add shortcuts setInputMap(); // add menu bar setJMenuBar(new MenuBar(getRootPane().getActionMap())); if (Utils.isMacOS()) { OSXIntegrator.integrateMainFrame(this); } showMenuBar(); // customise the look and feel LookAndFeels.customizeLookAndFeel(); // set the look and feel LookAndFeels.setLookAndFeel(getRootPane()); if ((trackListPanel != null) && (trackListPanel.getTrackMenu() != null)) { LookAndFeels.setLookAndFeel(getTrackListPanel().getTrackMenu()); } // set the application behavior when closed setDefaultCloseOperation(); getContentPane().setBackground(Colors.MAIN_GUI_BACKGROUND); setMinimumSize(WINDOW_MINIMUM_SIZE); setPreferredSize(WINDOW_DEFAULT_SIZE); pack(); setExtendedState(Frame.MAXIMIZED_BOTH); setLocationByPlatform(true); if (RecordingManager.getInstance().getProjectRecording().isLoadingEvent()) { PALoadProject loader = new PALoadProject(); loader.setSkipFileSelection(true); loader.actionPerformed(null); } // set the jump button as the default button pressed when enter is pressed addWindowListener(new WindowAdapter() { @Override public void windowActivated(WindowEvent e) { getRootPane().setDefaultButton(controlPanel.getJumpButton()); super.windowActivated(e); } @Override public void windowDeactivated(WindowEvent e) { if ((trackListPanel != null) && (trackListPanel.getTrackMenu() != null) && trackListPanel.getTrackMenu().isVisible()) { trackListPanel.getTrackMenu().setVisible(false); } super.windowDeactivated(e); } @Override public void windowLostFocus(WindowEvent e) { if ((trackListPanel != null) && (trackListPanel.getTrackMenu() != null) && trackListPanel.getTrackMenu().isVisible()) { trackListPanel.getTrackMenu().setVisible(false); } super.windowLostFocus(e); } }); } /** * Shows the main menu when the button in the ruler is clicked */ @Override public void actionPerformed(ActionEvent e) { new MainMenu(getRootPane().getActionMap()).show(this, getMousePosition().x, getMousePosition().y); } @Override public void genomeWindowChanged(GenomeWindowEvent evt) { // if the chromosome changed we reinitialize the multigenome data if (evt.chromosomeChanged() && ProjectManager.getInstance().isMultiGenomeProject()) { ProjectManager.getInstance().getMultiGenomeProject().getFileContentManager().updateCurrentVariants(); MGARefresh tracksUpdate = new MGARefresh(); tracksUpdate.actionPerformed(null); } } /** * @return the controlPanel */ public final ControlPanel getControlPanel() { return controlPanel; } /** * @return the ruler */ public final Ruler getRuler() { return ruler; } /** * @return the statusBar */ public final StatusBar getStatusBar() { return statusBar; } /** * @return the track list panel */ public final TrackListPanel getTrackListPanel(){ return trackListPanel; } /** * Initializes the status bar when starting a new project */ public void initStatusBarForFirstUse () { statusBar.initDescriptionForFirstUse(); } /** * @return true if the main frame is locked */ public boolean isLocked() { synchronized (MainFrame.class) { return isLocked; } } /** * Locks the main frame: * - the action button (top left button) * - the track handles * - the chromosome selection box * - the chromosome position text field */ public void lock() { synchronized (MainFrame.class) { ruler.lock(); trackListPanel.lockTrackHandles(); controlPanel.setEnabled(false); if (getJMenuBar() != null) { getJMenuBar().setEnabled(false); } isLocked = true; } } /** * Sets the action map of the main frame. This actions are associated with * the main menu. */ private void setActionMap() { // Add project actions to action map getRootPane().getActionMap().put(PASortFile.ACTION_KEY, new PASortFile()); getRootPane().getActionMap().put(PACheckForUpdates.ACTION_KEY, new PACheckForUpdates()); getRootPane().getActionMap().put(PAAbout.ACTION_KEY, new PAAbout(this)); getRootPane().getActionMap().put(PAContactUs.ACTION_KEY, new PAContactUs(this)); getRootPane().getActionMap().put(PAReportBug.ACTION_KEY, new PAReportBug(this)); getRootPane().getActionMap().put(PAAbout.ACTION_KEY, new PAAbout(this)); getRootPane().getActionMap().put(PAExit.ACTION_KEY, new PAExit(this)); getRootPane().getActionMap().put(PAFullScreen.ACTION_KEY, new PAFullScreen(this)); getRootPane().getActionMap().put(PAHelp.ACTION_KEY, new PAHelp(this)); getRootPane().getActionMap().put(PALoadProject.ACTION_KEY, new PALoadProject()); getRootPane().getActionMap().put(PANewProject.ACTION_KEY, new PANewProject()); getRootPane().getActionMap().put(PAOption.ACTION_KEY, new PAOption(this)); getRootPane().getActionMap().put(PASaveProject.ACTION_KEY, new PASaveProject()); getRootPane().getActionMap().put(PASaveProjectAs.ACTION_KEY, new PASaveProjectAs()); getRootPane().getActionMap().put(PAMoveLeft.ACTION_KEY, new PAMoveLeft()); getRootPane().getActionMap().put(PAMoveFarLeft.ACTION_KEY, new PAMoveFarLeft()); getRootPane().getActionMap().put(PAMoveRight.ACTION_KEY, new PAMoveRight()); getRootPane().getActionMap().put(PAMoveFarRight.ACTION_KEY, new PAMoveFarRight()); getRootPane().getActionMap().put(PAZoomIn.ACTION_KEY, new PAZoomIn()); getRootPane().getActionMap().put(PAZoomOut.ACTION_KEY, new PAZoomOut()); getRootPane().getActionMap().put(PARNAPosToDNAPos.ACTION_KEY, new PARNAPosToDNAPos(this)); getRootPane().getActionMap().put(PAShowWarningReport.ACTION_KEY, new PAShowWarningReport()); getRootPane().getActionMap().put(PAShowErrorReport.ACTION_KEY, new PAShowErrorReport()); getRootPane().getActionMap().put(PACopyCurrentPosition.ACTION_KEY, new PACopyCurrentPosition()); getRootPane().getActionMap().put(PABookmarkCurrentPosition.ACTION_KEY, new PACopyCurrentPosition()); // Add track actions to action map getRootPane().getActionMap().put(TAAddLayer.ACTION_KEY, new TAAddLayer()); getRootPane().getActionMap().put(TAAddVariantLayer.ACTION_KEY, new TAAddVariantLayer()); getRootPane().getActionMap().put(TAAddLayerFromDAS.ACTION_KEY, new TAAddLayerFromDAS()); getRootPane().getActionMap().put(TACopy.ACTION_KEY, new TACopy()); getRootPane().getActionMap().put(TACut.ACTION_KEY, new TACut()); getRootPane().getActionMap().put(TAPasteOrDrop.ACTION_KEY, new TAPasteOrDrop()); getRootPane().getActionMap().put(TAInsert.ACTION_KEY, new TAInsert()); getRootPane().getActionMap().put(TADelete.ACTION_KEY, new TADelete()); getRootPane().getActionMap().put(TASaveTrack.ACTION_KEY, new TASaveTrack()); getRootPane().getActionMap().put(TASaveAsImage.ACTION_KEY, new TASaveAsImage()); getRootPane().getActionMap().put(TATrackSettings.ACTION_KEY, new TATrackSettings()); } /** * Asks the user to confirm that he wants to close the application before * exiting */ private void setDefaultCloseOperation() { setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { int res = JOptionPane.showConfirmDialog(getRootPane(), "Exit GenPlay?", "Confirm Exit", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null); if (res == JOptionPane.OK_OPTION) { System.exit(0); } } }); } /** * Sets the input map. This map contain the short cuts of the applications. */ private void setInputMap() { getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PASortFile.ACCELERATOR,PASortFile.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PACheckForUpdates.ACCELERATOR,PACheckForUpdates.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAExit.ACCELERATOR, PAExit.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAFullScreen.ACCELERATOR, PAFullScreen.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAHelp.ACCELERATOR, PAHelp.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PASaveProject.ACCELERATOR, PASaveProject.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PASaveProjectAs.ACCELERATOR, PASaveProjectAs.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PALoadProject.ACCELERATOR, PALoadProject.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PANewProject.ACCELERATOR, PANewProject.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAMoveLeft.ACCELERATOR, PAMoveLeft.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAMoveFarLeft.ACCELERATOR, PAMoveFarLeft.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAMoveRight.ACCELERATOR, PAMoveRight.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAMoveFarRight.ACCELERATOR, PAMoveFarRight.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAZoomIn.ACCELERATOR, PAZoomIn.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PAZoomOut.ACCELERATOR, PAZoomOut.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PACopyCurrentPosition.ACCELERATOR, PACopyCurrentPosition.ACTION_KEY); getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(PABookmarkCurrentPosition.ACCELERATOR, PABookmarkCurrentPosition.ACTION_KEY); } /** * Set the current selected genome as well as repaint the frame * @param genomeName the new selected genome name to use for the coordinate system */ public void setNewGenomeCoordinate (String genomeName) { MGDisplaySettings.SELECTED_GENOME = genomeName; controlPanel.setSelectedGenomeName(genomeName); repaint(); } /** * Sets the main frame title. * Application title - Project name - Genome name - Assembly name. */ public void setTitle () { setTitle( MainFrame.APPLICATION_TITLE + " - " + ProjectManager.getInstance().getProjectName() + " - (" + ProjectManager.getInstance().getGenomeName() + ", " + ProjectManager.getInstance().getAssembly().getName() + ")"); } /** * Sets the main menu bar */ private void showMenuBar() { ConfigurationManager cm = ConfigurationManager.getInstance(); if (cm.isMenuBarShown()) { getJMenuBar().setVisible(true); } else { getJMenuBar().setVisible(false); } validate(); } /** * Shows the option screen */ public void showOption() { OptionDialog optionDialog = new OptionDialog(); if (optionDialog.showConfigurationDialog(getRootPane()) == OptionDialog.APPROVE_OPTION) { if (optionDialog.lookAndFeelChanged()) { LookAndFeels.setLookAndFeel(getRootPane()); LookAndFeels.setLookAndFeel(getTrackListPanel().getTrackMenu()); } if (optionDialog.showMenuBarChanged()) { showMenuBar(); } if (optionDialog.trackHeightChanged()) { trackListPanel.trackHeightChanged(); } if (optionDialog.trackCountChanged()) { trackListPanel.trackCountChanged(); } if (optionDialog.undoCountChanged()) { trackListPanel.undoCountChanged(); } if (optionDialog.resetTrackChanged()) { trackListPanel.resetLayerChanged(); } if (optionDialog.legendChanged()) { trackListPanel.legendChanged(); } } optionDialog.dispose(); } /** * Toggles the full screen mode */ public void toggleFullScreenMode() { if (Utils.isMacOS()) { // full screen for integration in OSX OSXIntegrator.toggleMainFrameFullScreen(); } else { // full screen on linux and windows if (!isUndecorated()) { setVisible(false); dispose(); setUndecorated(true); controlPanel.setVisible(false); statusBar.setVisible(false); screenBounds = getBounds(); setExtendedState(Frame.MAXIMIZED_BOTH); setVisible(true); } else { setVisible(false); dispose(); setUndecorated(false); controlPanel.setVisible(true); statusBar.setVisible(true); setVisible(true); setBounds(screenBounds); } } } /** * Unlocks the main frame */ public void unlock() { synchronized (MainFrame.class) { ruler.unlock(); trackListPanel.unlockTrackHandles(); controlPanel.setEnabled(true); if (getJMenuBar() != null) { getJMenuBar().setEnabled(true); } isLocked = false; } } }