/* * Jajuk * Copyright (C) The Jajuk Team * http://jajuk.info * * 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 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.jajuk.ui.windows; import static org.jajuk.ui.actions.JajukActions.NEXT_TRACK; import static org.jajuk.ui.actions.JajukActions.PAUSE_RESUME_TRACK; import static org.jajuk.ui.actions.JajukActions.PREVIOUS_TRACK; import static org.jajuk.ui.actions.JajukActions.STOP_TRACK; import java.awt.Cursor; import java.awt.Dimension; import java.awt.GraphicsDevice; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JWindow; import javax.swing.Timer; import net.miginfocom.swing.MigLayout; import org.jajuk.ui.actions.ActionManager; import org.jajuk.ui.actions.JajukActions; import org.jajuk.ui.helpers.JajukMouseAdapter; import org.jajuk.ui.substance.CircleButtonShaper; import org.jajuk.ui.substance.LeftConcaveButtonShaper; import org.jajuk.ui.substance.RightConcaveButtonShaper; import org.jajuk.ui.substance.RoundRectButtonShaper; import org.jajuk.ui.views.AnimationView; import org.jajuk.ui.views.CoverView; import org.jajuk.ui.widgets.JajukButton; import org.jajuk.ui.widgets.TrackPositionSliderToolbar; import org.jajuk.util.IconLoader; import org.jajuk.util.JajukIcons; import org.jajuk.util.UtilGUI; import org.jajuk.util.UtilSystem; import org.jajuk.util.log.Log; import org.pushingpixels.substance.api.SubstanceLookAndFeel; /** * The full screen window Note that not all operating support full screen mode. * If the OS doesn't support it, the user cannot access to it so we have not to * handle any errors. */ public class JajukFullScreenWindow extends JWindow implements IJajukWindow { /** Generated serialVersionUID. */ private static final long serialVersionUID = -2859302706462954993L; private static JajukFullScreenWindow instance = null; private GraphicsDevice graphicsDevice; private JButton jbPrevious; private JButton jbNext; private JButton jbPlayPause; private JButton jbStop; private JajukButton jbFull; private JajukButton jbExit; private CoverView coverView; /** State decorator. */ private WindowStateDecorator decorator; private AnimationView animationView; private JPanel jtbPlay; private Timer hideMouseTimer; /** Owning frame, see bellow for explanations *. */ private static JFrame owner; /** * See http://java.sun.com/docs/books/tutorial/uiswing/misc/focus.html We have * to use a frame owner of this jwindow to allow full screen mode to respond * to key events */ static { owner = new JFrame(); owner.setSize(new Dimension(0, 0)); owner.setUndecorated(true); owner.requestFocus(); // Add escape listening to exit full-screen mode owner.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { try { ActionManager.getAction(JajukActions.FULLSCREEN_JAJUK).perform(null); } catch (Exception e1) { Log.error(e1); } } } }); // Install global keystrokes WindowGlobalKeystrokeManager.getInstance(); } /** * Return whether we should try to switch to fullscreen mode (and lose keystrokes controls). * * @return whether we should try to switch to fullscreen mode */ private static boolean shouldTryNativeFullScreenmode() { return // Check full screen mode is actually supported (instance.graphicsDevice.isFullScreenSupported() && // Use real full screen mode only under OSX to override the task bar, otherwise, // we only maximize the frame to make keystrokes working. UtilSystem.isUnderOSX()); } /** * Gets the single instance of JajukFullScreenWindow. * * @return single instance of JajukFullScreenWindow */ public static synchronized JajukFullScreenWindow getInstance() { if (instance == null) { instance = new JajukFullScreenWindow(); instance.decorator = new WindowStateDecorator(instance) { @Override public void specificBeforeShown() { instance.graphicsDevice = UtilGUI.getGraphicsDeviceOfMainFrame(); if (shouldTryNativeFullScreenmode()) { instance.graphicsDevice.setFullScreenWindow(instance); } } @Override public void specificAfterShown() { if (!shouldTryNativeFullScreenmode()) { Dimension dimension = null; Point point = null; // Probably due to JRE bug #6921661, instance.graphicsDevice.getDisplayMode() sometimes // returns null under some OS like Fedora as a VMWare guest OS. Then, we can enter properly // into fullscreen mode but we we cannot leave it anymore // (java.lang.IllegalArgumentException: Invalid display mode ). // So we try to detect defunct configurations and we use Toolkit (dual screen unaware) // To find single screen size. if (instance.graphicsDevice.getDisplayMode() != null) { // No full screen mode but no JRE bug dimension = new Dimension(instance.graphicsDevice.getDisplayMode().getWidth(), instance.graphicsDevice.getDisplayMode().getHeight()); point = new Point(instance.graphicsDevice.getDefaultConfiguration().getBounds() .getLocation()); } else { // No full screen mode AND JRE bug dimension = Toolkit.getDefaultToolkit().getScreenSize(); point = new Point(0, 0); } instance.setSize(dimension); instance.setLocation(point); owner.setVisible(true); owner.requestFocus(); } } @Override public void specificAfterHidden() { if (shouldTryNativeFullScreenmode()) { instance.graphicsDevice.setFullScreenWindow(null); } owner.setVisible(false); instance.dispose(); } @Override public void specificBeforeHidden() { if (instance.graphicsDevice.isFullScreenSupported()) { // Make sure to reset everything like it was before entering fullscreen mode // (even if it wasn't the case) instance.graphicsDevice.setFullScreenWindow(null); } } }; } return instance; } /** * Hide mouse timer. * */ private void hideMouseTimer() { setCursor(Cursor.getDefaultCursor()); if (hideMouseTimer != null) { hideMouseTimer.restart(); } else { initMouseTimer(); } } /** * Inits the mouse timer. * */ private void initMouseTimer() { hideMouseTimer = new Timer(3000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { setCursor(Toolkit.getDefaultToolkit().createCustomCursor( IconLoader.getIcon(JajukIcons.NONE).getImage(), new Point(0, 0), "invisibleCursor")); } }); hideMouseTimer.start(); } /** * Instantiates a new jajuk full screen window. */ public JajukFullScreenWindow() { super(); setAlwaysOnTop(true); // Add Mouse Listener to disable mouse cursor addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { hideMouseTimer(); } }); addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { hideMouseTimer(); } }); // activate Timer hideMouseTimer(); } /* * (non-Javadoc) * * @see org.jajuk.ui.windows.IJajukWindow#initUI() */ @Override public void initUI() { // Full screen switch button jbFull = new JajukButton(ActionManager.getAction(JajukActions.FULLSCREEN_JAJUK)); // Exit button jbExit = new JajukButton(ActionManager.getAction(JajukActions.EXIT)); // Animation view animationView = new AnimationView(); animationView.initUI(); // Cover view coverView = new CoverView(); coverView.initUI(false); // Player toolbar jtbPlay = getPlayerPanel(); // Information panel TrackPositionSliderToolbar tpst = new TrackPositionSliderToolbar(); // Add items setLayout(new MigLayout("ins 0,gap 0 0", "[grow]", "[][grow][70%!][][]")); add(jbFull, "right,split 2,gapright 5"); add(jbExit, "right,wrap"); add(animationView, "alignx center,aligny top,grow,wrap"); add(coverView, "alignx center, grow,gap bottom 20,wrap"); add(jtbPlay, "alignx center,gap bottom 20,wrap"); add(tpst, "alignx center,width 50%!,aligny bottom,gap bottom 10"); // Add a contextual menu to leave full screen mode or quit, see also // issue #1492 // TODO : For some reasons, the popup doesn't appears over the cover pic, // I have no idea of the reason so far. final JPopupMenu popup = new JPopupMenu(); popup.add(ActionManager.getAction(JajukActions.FULLSCREEN_JAJUK)); popup.add(ActionManager.getAction(JajukActions.EXIT)); addMouseListener(new JajukMouseAdapter() { @Override public void handlePopup(MouseEvent e) { popup.show(e.getComponent(), e.getX(), e.getY()); } @Override public void handleAction(MouseEvent e) { // Void on purpose } }); } /** * Gets the player panel. * * @return the player panel */ private JPanel getPlayerPanel() { JPanel jPanelPlay = new JPanel(); jPanelPlay.setLayout(new MigLayout("insets 5", "[grow][grow][grow]")); // previous jbPrevious = new JajukButton(ActionManager.getAction(PREVIOUS_TRACK)); int concavity = IconLoader.getIcon(JajukIcons.PLAYER_PLAY).getIconHeight(); jbPrevious.putClientProperty(SubstanceLookAndFeel.BUTTON_SHAPER_PROPERTY, new RightConcaveButtonShaper(concavity)); jbPrevious.setBorderPainted(true); jbPrevious.setContentAreaFilled(true); jbPrevious.setFocusPainted(true); jbPrevious.addMouseListener(new JajukMouseAdapter() { @Override public void handlePopup(final MouseEvent me) { // Create an ActionEvent from this MouseEvent with a custom modifier : the right click ActionEvent ae = new ActionEvent(jbPrevious, 0, PREVIOUS_TRACK.name(), 4332424); try { ActionManager.getAction(PREVIOUS_TRACK).perform(ae); } catch (Exception e) { Log.error(e); } } }); // next jbNext = new JajukButton(ActionManager.getAction(NEXT_TRACK)); jbNext.putClientProperty(SubstanceLookAndFeel.BUTTON_SHAPER_PROPERTY, new LeftConcaveButtonShaper(concavity)); // play pause jbPlayPause = new JajukButton(ActionManager.getAction(PAUSE_RESUME_TRACK)); jbPlayPause.putClientProperty(SubstanceLookAndFeel.BUTTON_SHAPER_PROPERTY, new CircleButtonShaper()); // stop jbStop = new JajukButton(ActionManager.getAction(STOP_TRACK)); jbStop.putClientProperty(SubstanceLookAndFeel.BUTTON_SHAPER_PROPERTY, new RoundRectButtonShaper()); jPanelPlay.add(jbStop, "center,split 6,width 40!,height 30,gapright 5!"); jPanelPlay.add(jbPrevious, "center,width 62!,height 30!,gapright 0"); jPanelPlay.add(jbPlayPause, "center,width 45!,height 45!,gapright 0"); jPanelPlay.add(jbNext, "center,width 62!,height 30!,gapright 3"); return jPanelPlay; } /* * (non-Javadoc) * * @see org.jajuk.ui.widgets.JajukWindow#getWindowStateDecorator() */ @Override public WindowStateDecorator getWindowStateDecorator() { return decorator; } }