package org.limewire.ui.swing.advanced; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.Frame; import java.awt.event.ActionEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.EnumMap; import java.util.Map; import javax.swing.Action; import javax.swing.Icon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.KeyStroke; import net.miginfocom.swing.MigLayout; import org.jdesktop.application.Resource; import org.jdesktop.swingx.JXPanel; import org.limewire.ui.swing.action.AbstractAction; import org.limewire.ui.swing.components.LimeJFrame; import org.limewire.ui.swing.options.TabItemListener; import org.limewire.ui.swing.painter.factories.BarPainterFactory; import org.limewire.ui.swing.util.GuiUtils; import org.limewire.ui.swing.util.I18n; import org.limewire.ui.swing.util.EnabledType; import org.limewire.listener.EventListener; import com.google.inject.Inject; import com.google.inject.Provider; /** * Main content panel for the Advanced Tools window. The AdvancedToolsPanel * instance is displayed by calling the <code>display(WindowListener)</code> * method. */ public class AdvancedToolsPanel extends JPanel { /** Defines the tab identifiers for the window. */ public enum TabId { CONNECTIONS(I18n.tr("Connections"), I18n.tr("View connections to other P2P clients")), CONSOLE(I18n.tr("Console"), I18n.tr("View console messages")), MOJITO(I18n.tr("Mojito"), I18n.tr("View incoming and outgoing DHT messages")); private final String name; private final String tooltip; TabId(String name, String tooltip) { this.name = name; this.tooltip = tooltip; } public String tooltip() { return tooltip; } @Override public String toString() { return name; } } /** Identifier for Advanced Tools window. */ private static final String WINDOW_TITLE = I18n.tr("Advanced Tools"); @Resource private Color backgroundColor; @Resource private Icon connectionsIcon; @Resource private Icon consoleIcon; @Resource private Icon mojitoIcon; @Resource private Color tabTopColor; @Resource private Color tabBottomColor; @Resource private Color tabFontColor; @Resource private Font tabFont; /** Action to select next tab. */ private Action nextTabAction = new NextTabAction(); /** Action to select previous tab. */ private Action prevTabAction = new PrevTabAction(); /** Window listener for main frame. */ private WindowListener mainFrameListener = new MainFrameListener(); /** Currently selected tab item. */ private AdvancedTabItem selectedItem; /** Map containing tab items. */ private Map<TabId, AdvancedTabItem> tabItemMap = new EnumMap<TabId, AdvancedTabItem>(TabId.class); /** Map containing tab panels created. */ private Map<TabId, TabPanel> tabPanelMap = new EnumMap<TabId, TabPanel>(TabId.class); private JXPanel headerPanel; private JPanel cardPanel; private CardLayout cardLayout; /** * Constructs a window panel for the Advanced Tools window that uses the * injected Provider instances to create the tab content panels. */ @Inject public AdvancedToolsPanel(BarPainterFactory barPainterFactory, Provider<ConnectionsPanel> connectionsPanel, Provider<ConsolePanel> consolePanel, Provider<MojitoPanel> mojitoPanel) { // Inject annotated resource values. GuiUtils.assignResources(this); // Initialize components. initComponents(barPainterFactory); // Add tabs to dialog. addTab(TabId.CONNECTIONS, connectionsIcon, connectionsPanel); addTab(TabId.CONSOLE, consoleIcon, consolePanel); addTab(TabId.MOJITO, mojitoIcon, mojitoPanel); } /** * Initializes the components in the container. */ private void initComponents(BarPainterFactory barPainterFactory) { setLayout(new BorderLayout()); // Create header panel to hold tab buttons. headerPanel = new JXPanel(); headerPanel.setBackgroundPainter(barPainterFactory.createTopBarPainter()); headerPanel.setLayout(new MigLayout("insets 0 0 0 0, gap 0 0", "", // col constraints "align top,fill")); // row constraints // Create panel to hold tab content panels. cardLayout = new CardLayout(); cardPanel = new JPanel(); cardPanel.setBackground(backgroundColor); cardPanel.setLayout(cardLayout); add(headerPanel, BorderLayout.NORTH); add(cardPanel, BorderLayout.CENTER); } /** * Adds a tab to the dialog using the specified tab identifier, icon, and * panel provider. */ private void addTab(TabId tabId, Icon icon, Provider<? extends TabPanel> provider) { // Create tab item and add to map. AdvancedTabItem tabItem = new AdvancedTabItem(tabId, icon, provider); tabItemMap.put(tabId, tabItem); // Add button to header. headerPanel.add(createButton(tabItem)); } /** * Creates a tab button for the specified tab item. */ private JButton createButton(AdvancedTabItem tabItem) { // Create the tab button. TabButton button = new TabButton(new TabAction(tabItem, tabItem.getIcon())); // Set gradient colors. button.setGradients(tabTopColor, tabBottomColor); button.setForeground(tabFontColor); button.setFont(tabFont); // Add inputs and action to select previous tab. button.getActionMap().put(PrevTabAction.KEY, this.prevTabAction); button.getInputMap(JComponent.WHEN_FOCUSED).put( KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0), PrevTabAction.KEY); button.getInputMap(JComponent.WHEN_FOCUSED).put( KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), PrevTabAction.KEY); // Add inputs and action to select next tab. button.getActionMap().put(NextTabAction.KEY, this.nextTabAction); button.getInputMap(JComponent.WHEN_FOCUSED).put( KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0), NextTabAction.KEY); button.getInputMap(JComponent.WHEN_FOCUSED).put( KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), NextTabAction.KEY); return button; } /** * Returns the window frame that contains this panel. */ private JFrame getFrame() { Container ancestor = getTopLevelAncestor(); return (ancestor instanceof JFrame) ? (JFrame) ancestor : null; } /** * Displays this panel in a modeless window. If the panel is not contained * in a window frame, then a new window is created and the specified window * listener is installed. Otherwise, the existing window is made visible. */ public void display(WindowListener windowListener) { JFrame frame = getFrame(); if (frame == null) { // Create modeless window. frame = new LimeJFrame(WINDOW_TITLE); // Set window properties. frame.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE); frame.setResizable(true); // Set preferred size based on main frame. Dimension mainSize = GuiUtils.getMainFrame().getSize(); Dimension prefSize = new Dimension( Math.max(mainSize.width - 60, 800), Math.max(mainSize.height - 60, 600)); frame.setPreferredSize(prefSize); frame.getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE,0), "escapeAction"); frame.getRootPane().getActionMap().put("escapeAction", new CloseFrameAction(frame)); // Add listeners to handle system menu close action. frame.addWindowListener(windowListener); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { disposeWindow(); } }); // Add listener to handle parent window events. GuiUtils.getMainFrame().addWindowListener(this.mainFrameListener); // Add to content pane and validate. frame.getContentPane().add(this, BorderLayout.CENTER); frame.pack(); // Set window position centered on main frame. frame.setLocationRelativeTo(GuiUtils.getMainFrame()); // Select first enabled tab. select(findNextEnabledTab(-1, true)); // Start tab panels. for (TabId tabId : TabId.values()) { TabPanel tabPanel = tabItemMap.get(tabId).getTabPanel(); if (tabPanel != null) { tabPanel.start(); } } } // Display window. frame.setVisible(true); frame.toFront(); } /** * Closes the window that is displaying this panel. */ public void disposeWindow() { JFrame frame = getFrame(); if (frame != null) { // Stop tab panels. for (TabId tabId : TabId.values()) { TabPanel tabPanel = tabItemMap.get(tabId).getTabPanel(); if (tabPanel != null) { tabPanel.stop(); } } // Remove window listener from main GUI frame. GuiUtils.getMainFrame().removeWindowListener(this.mainFrameListener); // Dispose window. frame.getContentPane().remove(this); frame.dispose(); } } /** * Minimizes the window that is displaying this panel. * @param visible true if minimized window remains visible in taskbar */ public void minimizeWindow(boolean visible) { JFrame frame = getFrame(); if (frame != null) { frame.setExtendedState(Frame.ICONIFIED); frame.setVisible(visible); } } /** * Restores the window that is displaying this panel. */ public void restoreWindow() { JFrame frame = getFrame(); if (frame != null) { frame.setExtendedState(Frame.NORMAL); frame.setVisible(true); } } /** * Returns the TabId of the next enabled tab after the specified tab index. * The <code>forward</code> argument specifies direction: true for next * tab, false for previous tab. If no other enabled tabs are found, then * null is returned. */ private TabId findNextEnabledTab(int startIndex, boolean forward) { // Get array of TabId values. TabId[] tabIds = TabId.values(); // Normalize start index. startIndex = (startIndex + tabIds.length) % tabIds.length; // Get index of next/previous tab. int nextTab = (startIndex + (forward ? 1 : -1) + tabIds.length) % tabIds.length; boolean enabled = tabItemMap.get(tabIds[nextTab]).isEnabled(); // Find next/previous enabled tab. If no enabled tabs are found, // then return the start index. while (!enabled && (nextTab != startIndex)) { nextTab = (nextTab + (forward ? 1 : -1) + tabIds.length) % tabIds.length; enabled = tabItemMap.get(tabIds[nextTab]).isEnabled(); } // Return enabled tab, or null if none found. return (nextTab != startIndex) ? tabIds[nextTab] : null; } /** * Selects the tab item with the specified identifier. * @param tabId the identifier of the tab */ private void select(TabId tabId) { // Skip if identifier is null. if (tabId == null) { return; } // De-select current item. if (selectedItem != null) { selectedItem.fireSelected(false); } // Get new selected item. selectedItem = tabItemMap.get(tabId); // Get tab panel - lazily create if necessary. TabPanel tabPanel = tabPanelMap.get(tabId); if (tabPanel == null) { tabPanel = selectedItem.getTabPanel(); tabPanel.setBackground(backgroundColor); tabPanelMap.put(tabId, tabPanel); cardPanel.add(tabPanel, tabId.toString()); } // Fire event to select tab. selectedItem.fireSelected(true); // Display selected tab panel. cardLayout.show(cardPanel, tabId.toString()); } /** * Selects the next enabled tab item. */ private void selectNext() { // Get index for selected tab item. int index = selectedItem.getTabId().ordinal(); // Select next enabled tab. select(findNextEnabledTab(index, true)); } /** * Selects the previous enabled tab item. */ private void selectPrev() { // Get index for selected tab item. int index = selectedItem.getTabId().ordinal(); // Select previous enabled tab. select(findNextEnabledTab(index, false)); } /** * A TabItem for the Advanced Tools dialog. */ private class AdvancedTabItem extends AbstractTabItem { private final TabId tabId; private final Icon icon; private final Provider<? extends TabPanel> provider; private TabPanel tabPanel; /** * Constructs a tab item using the specified tab identifier, icon, and * panel provider. */ public AdvancedTabItem(TabId tabId, Icon icon, Provider<? extends TabPanel> provider) { this.tabId = tabId; this.icon = icon; this.provider = provider; } @Override public String getId() { return tabId.toString(); } @Override public void select() { AdvancedToolsPanel.this.select(tabId); } public boolean isEnabled() { return getTabPanel().isTabEnabled(); } public Icon getIcon() { return icon; } public TabId getTabId() { return tabId; } public TabPanel getTabPanel() { if (tabPanel == null) { tabPanel = this.provider.get(); } return tabPanel; } } /** * An Action associated with a tab button. This updates the "selected" * value when its associated tab item is selected or de-selected. */ private class TabAction extends AbstractAction { private final TabId tabId; /** * Constructs a TabAction with the specified <code>AdvancedTabItem * </code> and icon. The Action.NAME value is set to the translated * string for the tab. */ public TabAction(AdvancedTabItem tabItem, Icon icon) { super(tabItem.getTabId().toString(), icon); // Store tab identifier and action command. tabId = tabItem.getTabId(); putValue(ACTION_COMMAND_KEY, tabId.toString()); putValue(SHORT_DESCRIPTION, tabId.tooltip()); // Install listener to handle tab item selection. tabItem.addTabItemListener(new TabItemListener() { @Override public void itemSelected(boolean selected) { putValue(SELECTED_KEY, selected); } }); // Install listener to handle tab panel enabled. TabPanel tabPanel = tabItem.getTabPanel(); tabPanel.addEnabledListener(new EventListener<EnabledType>() { @Override public void handleEvent(EnabledType enabledType) { setEnabled(enabledType.isEnabled()); } }); // Initialize enabled state. setEnabled(tabPanel.isTabEnabled()); } @Override public void actionPerformed(ActionEvent e) { select(tabId); } } /** * An Action that handles events to select the next tab. */ private class NextTabAction extends AbstractAction { final static String KEY = "NEXT"; @Override public void actionPerformed(ActionEvent e) { selectNext(); } } /** * An Action that handles events to select the previous tab. */ private class PrevTabAction extends AbstractAction { final static String KEY = "PREV"; @Override public void actionPerformed(ActionEvent e) { selectPrev(); } } /** * A listener that handles window events on the main GUI frame. This is * used to minimize or restore the Advanced Tools window when the main GUI * frame is minimized or restored. */ private class MainFrameListener extends WindowAdapter { @Override public void windowClosed(WindowEvent e) { disposeWindow(); } @Override public void windowDeiconified(WindowEvent e) { restoreWindow(); } @Override public void windowIconified(WindowEvent e) { minimizeWindow(e.getWindow().isVisible()); } } private static class CloseFrameAction extends AbstractAction { private JFrame frame; public CloseFrameAction(JFrame frame){ this.frame = frame; } @Override public void actionPerformed(ActionEvent e) { frame.setVisible(false); } } }