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(); } } }); } } }