package org.limewire.ui.swing.player;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import org.jdesktop.application.Resource;
import org.limewire.ui.swing.components.LimeJFrame;
import org.limewire.ui.swing.home.HomeMediator;
import org.limewire.ui.swing.library.LibraryMediator;
import org.limewire.ui.swing.mainframe.GlobalLayeredPane;
import org.limewire.ui.swing.nav.NavCategory;
import org.limewire.ui.swing.nav.NavItem;
import org.limewire.ui.swing.nav.NavMediator;
import org.limewire.ui.swing.nav.NavSelectable;
import org.limewire.ui.swing.nav.NavigationListener;
import org.limewire.ui.swing.nav.Navigator;
import org.limewire.ui.swing.util.GuiUtils;
import org.limewire.util.OSUtils;
import com.google.inject.Inject;
import com.google.inject.Provider;
/**
* Class that handles the displaying of video.
*/
class VideoDisplayDirector {
private final JLayeredPane limeWireLayeredPane;
private final Integer videoLayer = new Integer(JLayeredPane.DEFAULT_LAYER + 1);
private VideoPanel videoPanel;
private JFrame fullScreenFrame;
private NavigationListener closeVideoOnNavigation;
@Resource(key="WireframeTop.preferredSize") private Dimension topPanelPreferredSize;
private final VideoPanelFactory videoPanelFactory;
private final Navigator navigator;
private final Provider<PlayerMediatorImpl> playerMediator;
private VideoPanelResizer resizerListener;
@Inject
public VideoDisplayDirector(@GlobalLayeredPane JLayeredPane limeWireLayeredPane, Provider<PlayerMediatorImpl> videoPlayerMediator,
VideoPanelFactory videoPanelFactory, Navigator navigator){
this.limeWireLayeredPane = limeWireLayeredPane;
this.playerMediator = videoPlayerMediator;
this.videoPanelFactory = videoPanelFactory;
this.navigator = navigator;
GuiUtils.assignResources(this);
assert(topPanelPreferredSize != null);
resizerListener = new VideoPanelResizer();
}
/**
* Prepares a new videoRenderer to be displayed.
*
* @param videoRenderer the Container that will be displayed in a PlayerPanel.
* @param isFullScreen true for fullscreen, false to embed in the client frame
*/
public void initialize(Container videoRenderer, boolean isFullScreen){
boolean isReinitializing = false;
if(this.videoPanel != null){
isReinitializing = true;
close();
}
//Recycling the video panel causes problems with native painting. We need a new one each time.
videoPanel = videoPanelFactory.createVideoPanel(videoRenderer);
// If the start screen is open, then open up the library view before showing the video.
// The start screen has a heavy weight component that would cut off the video.
NavItem selectedNavItem = navigator.getSelectedNavItem();
if (selectedNavItem == null || selectedNavItem.getId().equals(HomeMediator.NAME)) {
NavItem item = navigator.getNavItem(NavCategory.LIBRARY, LibraryMediator.NAME);
item.select();
}
if(isFullScreen){
initializeFullScreen();
} else {
initializeInClient(isReinitializing);
}
}
private void initializeInClient(boolean isReinitializing){
limeWireLayeredPane.add(videoPanel.getComponent(), videoLayer);
registerNavigationListener();
limeWireLayeredPane.addComponentListener(resizerListener);
//If we are reinitializing (for example returning from full screen) we want to show the panel immediately
if (isReinitializing) {
showInClient();
} else {
videoPanel.getComponent().setVisible(false);
}
}
private void registerNavigationListener() {
if (closeVideoOnNavigation == null) {
closeVideoOnNavigation = new CloseVideoOnNavigationListener();
}
navigator.addNavigationListener(closeVideoOnNavigation);
}
private void initializeFullScreen(){
//Hide main frame first so that fullScreenFrame gets proper focus.
GuiUtils.getMainFrame().setVisible(false);
fullScreenFrame = new LimeJFrame();
fullScreenFrame.setTitle(GuiUtils.getMainFrame().getTitle());
// fullScreenFrame.setAlwaysOnTop(true) and
// SystemUtils.setWindowTopMost(fullScreenFrame) don't play nicely with
// dialog boxes so we aren't using them here.
fullScreenFrame.setUndecorated(true);
fullScreenFrame.add(videoPanel.getComponent(), BorderLayout.CENTER);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
fullScreenFrame.setBounds(0,0,screenSize.width, screenSize.height);
//addNotify is necessary here so that a null pointer doesn't happen in sun's awt code
//when a canvas is added to videoRenderer
fullScreenFrame.addNotify();
}
/**
* Displays the video as {@link #initialize(Container, boolean) initialized}.
*/
public void show(){
if(isFullScreen()){
showFullScreen();
} else {
showInClient();
}
videoPanel.playerLoaded();
}
private void showInClient() {
videoPanel.getComponent().setVisible(true);
resizeVideoContainer();
//Make sure the flash of native video window doesn't steal focus
GuiUtils.getMainFrame().toFront();
videoPanel.requestFocus();
}
private void showFullScreen() {
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = environment.getDefaultScreenDevice();
if (OSUtils.isMacOSX() && device.isFullScreenSupported()) {
device.setFullScreenWindow(fullScreenFrame);
} else {
fullScreenFrame.setVisible(true);
fullScreenFrame.toFront();
}
}
public boolean isFullScreen() {
return fullScreenFrame != null;
}
public void close() {
if (isFullScreen()) {
closeFullScreen();
videoPanel.dispose();
} else if(videoPanel != null) {
closeInClient();
videoPanel.dispose();
}
videoPanel = null;
//Force a repaint on close - gets rid of artifacts (especially noticable on Mac)
GuiUtils.getMainFrame().repaint();
}
private void closeFullScreen() {
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice device = environment.getDefaultScreenDevice();
if (OSUtils.isMacOSX() && device.isFullScreenSupported()) {
device.setFullScreenWindow(null);
}
fullScreenFrame.setVisible(false);
//manually dispose of the frame so that everything gets garbage collected.
fullScreenFrame.dispose();
fullScreenFrame = null;
GuiUtils.getMainFrame().setVisible(true);
}
private void closeInClient(){
limeWireLayeredPane.removeComponentListener(resizerListener);
limeWireLayeredPane.remove(videoPanel.getComponent());
removeNavigationListener();
}
private void removeNavigationListener(){
navigator.removeNavigationListener(closeVideoOnNavigation);
}
private class VideoPanelResizer extends ComponentAdapter {
@Override
public void componentResized(ComponentEvent e) {
resizeVideoContainer();
}
}
private void resizeVideoContainer() {
if (videoPanel != null) {
Rectangle parentBounds = videoPanel.getComponent().getParent().getBounds();
// TODO: this knows too much about the layered pane's layout
videoPanel.getComponent().setBounds(0, (int)topPanelPreferredSize.getHeight(), (int)parentBounds.getWidth(),
(int)parentBounds.getHeight() - (int)topPanelPreferredSize.getHeight());
videoPanel.getComponent().revalidate();
}
}
private class CloseVideoOnNavigationListener implements NavigationListener {
@Override
public void itemSelected(NavCategory category, NavItem navItem,
NavSelectable selectable, NavMediator navMediator) {
playerMediator.get().stop();
}
@Override public void categoryAdded(NavCategory category) {}
@Override public void categoryRemoved(NavCategory category, boolean wasSelected) {}
@Override public void itemAdded(NavCategory category, NavItem navItem) {}
@Override public void itemRemoved(NavCategory category, NavItem navItem, boolean wasSelected) {}
}
}