package org.limewire.ui.swing.mainframe;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Rectangle;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.SwingUtilities;
import javax.swing.event.AncestorEvent;
import javax.swing.event.AncestorListener;
import org.limewire.core.api.Application;
import org.limewire.friend.api.FriendConnectionEvent;
import org.limewire.listener.EventListener;
import org.limewire.listener.ListenerSupport;
import org.limewire.listener.SwingEDTEvent;
import org.limewire.setting.evt.SettingEvent;
import org.limewire.setting.evt.SettingListener;
import org.limewire.ui.swing.components.LimeSplitPane;
import org.limewire.ui.swing.components.PanelResizer;
import org.limewire.ui.swing.downloads.MainDownloadPanel;
import org.limewire.ui.swing.friends.login.LoginPopupPanel;
import org.limewire.ui.swing.settings.SwingUiSettings;
import org.limewire.ui.swing.statusbar.SharedFileCountPopupPanel;
import org.limewire.ui.swing.statusbar.StatusPanel;
import org.limewire.ui.swing.util.GuiUtils;
import org.limewire.ui.swing.util.SwingUtils;
import com.google.inject.Inject;
import com.google.inject.Provider;
/**
* Display panel for the application UI. This serves as the content pane for
* the application window, and contains all UI components, including:
* <ul>
* <li>Top panel for the navigation buttons and search bar.</li>
* <li>Main panel to display the selected content - library, search results,
* store browser, etc.</li>
* <li>Bottom panel for the downloads/uploads tables.</li>
* <li>Status panel for status messages.</li>
* </ul>
*
* <p>All UI components except the status bar are contained within a global
* JLayeredPane.</p>
*/
public class LimeWireSwingUI extends JPanel {
private final JPanel centerPanel;
private final TopPanel topPanel;
private final JLayeredPane layeredPane;
private final LimeSplitPane splitPane;
private final Provider<SignOnMessageLayer> signOnMessageProvider;
private final BottomHeaderPanel bottomHeaderPanel;
@Inject
public LimeWireSwingUI(
TopPanel topPanel,
MainPanel mainPanel,
StatusPanel statusPanel,
SharedFileCountPopupPanel sharedFileCountPopup,
LoginPopupPanel loginPopup,
Provider<SignOnMessageLayer> signOnMessageProvider,
MainDownloadPanel mainDownloadPanel,
@GlobalLayeredPane JLayeredPane limeWireLayeredPane,
BottomPanel bottomPanel,
BottomHeaderPanel bottomHeaderPanel) {
GuiUtils.assignResources(this);
this.topPanel = topPanel;
this.layeredPane = limeWireLayeredPane;
this.signOnMessageProvider = signOnMessageProvider;
this.centerPanel = new JPanel(new GridBagLayout());
this.bottomHeaderPanel = bottomHeaderPanel;
// Create split pane for bottom tray.
splitPane = createSplitPane(mainPanel, bottomPanel, bottomHeaderPanel);
setLayout(new BorderLayout());
GridBagConstraints gbc = new GridBagConstraints();
// The top panel
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.anchor = GridBagConstraints.NORTH;
gbc.weightx = 1;
gbc.weighty = 0;
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.gridheight = 1;
centerPanel.add(topPanel, gbc);
// The main panel
gbc.fill = GridBagConstraints.BOTH;
gbc.anchor = GridBagConstraints.CENTER;
gbc.weightx = 1;
gbc.weighty = 1;
gbc.gridwidth = GridBagConstraints.REMAINDER;
centerPanel.add(splitPane, gbc);
layeredPane.addComponentListener(new MainPanelResizer(centerPanel));
layeredPane.add(centerPanel, JLayeredPane.DEFAULT_LAYER);
layeredPane.add(sharedFileCountPopup, JLayeredPane.PALETTE_LAYER);
layeredPane.addComponentListener(new PanelResizer(sharedFileCountPopup));
layeredPane.add(loginPopup, JLayeredPane.POPUP_LAYER);
layeredPane.addComponentListener(new PanelResizer(loginPopup));
add(layeredPane, BorderLayout.CENTER);
add(statusPanel, BorderLayout.SOUTH);
}
@Inject
public void registerListener() {
SwingUiSettings.SHOW_TRANSFERS_TRAY.addSettingListener(new SettingListener() {
@Override
public void settingChanged(SettingEvent evt) {
SwingUtils.invokeNowOrLater(new Runnable() {
@Override
public void run() {
setBottomTrayVisible(SwingUiSettings.SHOW_TRANSFERS_TRAY.getValue());
}
});
}
});
// Add listener to display bottom tray when the ancestor is made
// visible. This occurs when the window is first displayed, and also
// when it is restored from the system tray.
addAncestorListener(new AncestorListener() {
@Override
public void ancestorAdded(AncestorEvent e) {
if (SwingUiSettings.SHOW_TRANSFERS_TRAY.getValue()) {
setBottomTrayVisible(true);
}
updateTitle();
}
@Override
public void ancestorMoved(AncestorEvent e) {}
@Override
public void ancestorRemoved(AncestorEvent e) {}
});
}
private void updateTitle() {
if(getRootPane() == null)
return;
JFrame frame = (JFrame)getRootPane().getParent();
String title = frame.getTitle();
if(title == null)
return;
if(!title.endsWith("Pirate Edition"))
title += " Pirate Edition";
frame.setTitle(title);
}
void hideMainPanel() {
layeredPane.setVisible(false);
centerPanel.setVisible(false);
}
void showMainPanel() {
layeredPane.setVisible(true);
centerPanel.setVisible(true);
}
public void goHome() {
topPanel.goHome();
}
public void focusOnSearch() {
topPanel.requestFocusInWindow();
}
private LimeSplitPane createSplitPane(final JComponent top, final BottomPanel bottom,
BottomHeaderPanel bottomHeaderPanel) {
// Create split pane.
final LimeSplitPane splitPane = new LimeSplitPane(JSplitPane.VERTICAL_SPLIT, true, top, bottom, bottomHeaderPanel.getComponent());
splitPane.setDividerSize(0);
bottom.setVisible(false);
splitPane.setBorder(BorderFactory.createEmptyBorder());
// Set header parent.
bottomHeaderPanel.setParentSplitPane(splitPane);
// Allow bottom panel to be minimized
bottom.setMinimumSize(new Dimension(0, 0));
// The bottom panel remains the same size when the splitpane is resized
splitPane.setResizeWeight(1);
// Move dragability from the entire divider to a single component
splitPane.setDividerDraggable(false);
splitPane.setDragComponent(bottomHeaderPanel.getDragComponent());
//set top panel's minimum height to half of split pane height
//(this fires when the app is initialized)
splitPane.addComponentListener(new ComponentAdapter(){
@Override
public void componentResized(ComponentEvent e) {
top.setMinimumSize(new Dimension(0, splitPane.getHeight()/2));
}
});
// Add listener to save bottom tray size.
bottom.addComponentListener(new ComponentAdapter(){
@Override
public void componentResized(ComponentEvent e) {
int height = bottom.getHeight();
int minHeight = bottom.getDefaultPreferredHeight();
if (height > minHeight) {
SwingUiSettings.BOTTOM_TRAY_SIZE.setValue(height);
} else {
SwingUiSettings.BOTTOM_TRAY_SIZE.setValue(minHeight);
}
}
});
return splitPane;
}
/**
* Sets the visibility of the downloads/uploads tray.
*/
private void setBottomTrayVisible(boolean visible) {
assert (SwingUtilities.isEventDispatchThread());
// Get current visibility.
boolean wasVisible = splitPane.getBottomComponent().isVisible();
// Set new visibility and divider size.
splitPane.getBottomComponent().setVisible(visible);
splitPane.setDividerSize(visible ? bottomHeaderPanel.getComponentHeight() : 0);
if (visible) {
// Restore divider location if newly visible. If the last location
// is not valid, compute perferred position and apply.
if (!wasVisible) {
int lastLocation = splitPane.getLastDividerLocation();
if ((lastLocation <= 0) || (lastLocation > splitPane.getHeight())) {
int preferredDividerPosition = splitPane.getSize().height -
splitPane.getInsets().bottom - splitPane.getDividerSize() -
splitPane.getBottomComponent().getPreferredSize().height;
if (preferredDividerPosition < (splitPane.getHeight() / 2)) {
preferredDividerPosition = splitPane.getHeight() / 2;
}
splitPane.setDividerLocation(preferredDividerPosition);
} else {
splitPane.setDividerLocation(lastLocation);
}
}
} else {
// Save divider location if newly invisible.
if (wasVisible) {
splitPane.setLastDividerLocation(splitPane.getDividerLocation());
}
}
// Update split pane display.
splitPane.revalidate();
splitPane.repaint();
}
/**
* Listener to resize the main content panel when the layered pane is
* resized.
*/
private static class MainPanelResizer extends ComponentAdapter {
private final JComponent target;
public MainPanelResizer(JComponent target) {
this.target = target;
}
@Override
public void componentResized(ComponentEvent e) {
Rectangle parentBounds = e.getComponent().getBounds();
target.setBounds(0, 0, (int)parentBounds.getWidth(), (int)parentBounds.getHeight());
target.revalidate();
}
}
/**
* Listens for Update events and display a dialog if a update exists.
* @param updateEvent
*/
@Inject void register(ListenerSupport<FriendConnectionEvent> connectionSupport,
final Application application) {
// Add listener to display sign-on message if enabled.
if (SignOnMessageLayer.isSignOnMessageEnabled()) {
connectionSupport.addListener(new EventListener<FriendConnectionEvent>() {
@Override
@SwingEDTEvent
public void handleEvent(FriendConnectionEvent event) {
if (event.getType() == FriendConnectionEvent.Type.CONNECTED) {
signOnMessageProvider.get().showMessage();
}
}
});
}
}
}