package org.limewire.ui.swing.search.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.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.EnumMap;
import java.util.Map;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.application.Resource;
import org.jdesktop.swingx.JXButton;
import org.jdesktop.swingx.JXPanel;
import org.limewire.core.api.FilePropertyKey;
import org.limewire.core.api.search.SearchCategory;
import org.limewire.ui.swing.action.AbstractAction;
import org.limewire.ui.swing.advanced.AbstractTabItem;
import org.limewire.ui.swing.advanced.TabButton;
import org.limewire.ui.swing.components.PopupWindow;
import org.limewire.ui.swing.components.decorators.ButtonDecorator;
import org.limewire.ui.swing.options.TabItemListener;
import org.limewire.ui.swing.painter.factories.BarPainterFactory;
import org.limewire.ui.swing.search.KeywordAssistedSearchBuilder;
import org.limewire.ui.swing.search.SearchHandler;
import org.limewire.ui.swing.search.SearchInfo;
import org.limewire.ui.swing.util.GuiUtils;
import org.limewire.ui.swing.util.I18n;
import com.google.inject.Inject;
import com.google.inject.Provider;
/**
* Display container for the Advanced Search popup dialog. This is shown when
* the user selects the Advanced search category.
*/
public class AdvancedPopupPanel extends JXPanel {
/** Defines the tabs for the popup. */
private enum TabId {
AUDIO(I18n.tr("Audio"), SearchCategory.AUDIO),
VIDEO(I18n.tr("Video"), SearchCategory.VIDEO);
private final String name;
private final SearchCategory category;
TabId(String name, SearchCategory category) {
this.name = name;
this.category = category;
}
public SearchCategory getCategory() {
return category;
}
public String getName() {
return name;
}
}
@Resource private Color background;
@Resource private Color borderColor;
@Resource private Color tabTopColor;
@Resource private Color tabBottomColor;
@Resource private Color tabForeground;
@Resource private Font tabFont;
@Resource private Icon audioTabIcon;
@Resource private Icon videoTabIcon;
private final AdvancedPanelFactory advancedPanelFactory;
private final ButtonDecorator buttonDecorator;
private final Provider<KeywordAssistedSearchBuilder> advancedSearchBuilder;
private final SearchHandler searchHandler;
private final Map<TabId, AdvancedSearchTabItem> tabItemMap =
new EnumMap<TabId, AdvancedSearchTabItem>(TabId.class);
private Action nextTabAction = new NextTabAction();
private Action prevTabAction = new PrevTabAction();
private Action searchAction = new SearchAction();
private AdvancedSearchTabItem selectedItem;
private JXPanel headerPanel;
private JPanel cardPanel;
private CardLayout cardLayout;
private JPanel buttonPanel;
private JXButton searchButton;
@Inject
public AdvancedPopupPanel(BarPainterFactory barPainterFactory,
AdvancedPanelFactory advancedPanelFactory,
ButtonDecorator buttonDecorator,
Provider<KeywordAssistedSearchBuilder> advancedSearchBuilder,
SearchHandler searchHandler) {
this.advancedPanelFactory = advancedPanelFactory;
this.buttonDecorator = buttonDecorator;
this.advancedSearchBuilder = advancedSearchBuilder;
this.searchHandler = searchHandler;
// Inject annotated resource values.
GuiUtils.assignResources(this);
// Initialize components.
initComponents(barPainterFactory);
// Add tabs to popup.
addTab(TabId.AUDIO, audioTabIcon);
addTab(TabId.VIDEO, videoTabIcon);
}
/**
* Initializes the components in the container.
*/
private void initComponents(BarPainterFactory barPainterFactory) {
setBorder(BorderFactory.createLineBorder(borderColor));
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(background);
cardPanel.setBorder(BorderFactory.createEmptyBorder(6, 10, 6, 6));
cardPanel.setLayout(cardLayout);
cardPanel.setOpaque(true);
// Create search button.
searchButton = new JXButton(searchAction);
buttonDecorator.decorateGreenFullButton(searchButton);
buttonPanel = new JPanel();
buttonPanel.setBackground(background);
buttonPanel.setLayout(new MigLayout("insets 0 12 12 12, gap 0"));
buttonPanel.setOpaque(true);
buttonPanel.add(searchButton, "pushx, alignx right");
add(headerPanel, BorderLayout.NORTH);
add(cardPanel, BorderLayout.CENTER);
add(buttonPanel, BorderLayout.SOUTH);
}
/**
* Adds a tab to the dialog using the specified tab identifier and icon.
*/
private void addTab(TabId tabId, Icon icon) {
// Create tab item and add to map.
AdvancedSearchTabItem tabItem = new AdvancedSearchTabItem(tabId);
tabItemMap.put(tabId, tabItem);
// Add button to header.
headerPanel.add(createButton(tabItem, icon));
}
/**
* Creates a tab button for the specified tab item and icon.
*/
private JButton createButton(AdvancedSearchTabItem tabItem, Icon icon) {
TabButton button = new TabButton(new TabAction(tabItem, icon));
// Adjust button size.
button.setPreferredSize(new Dimension(54, 60));
button.setBorder(BorderFactory.createEmptyBorder());
// Set gradient colors.
button.setGradients(tabTopColor, tabBottomColor);
button.setForeground(tabForeground);
button.setFont(tabFont);
// Add inputs and action to select previous tab.
button.getActionMap().put(PrevTabAction.KEY, 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, 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;
}
/**
* Creates a new popup window to display the container.
*/
private PopupWindow createPopup(JComponent invoker, int x, int y) {
// Determine popup location on screen.
Point invokerLoc = invoker.getLocationOnScreen();
Point location = new Point(invokerLoc.x + x, invokerLoc.y + y);
// Create popup containing this panel.
return PopupWindow.createPopupWindow(invoker, this, location);
}
/**
* Closes the popup window.
*/
private void disposePopup() {
Container ancestor = getTopLevelAncestor();
if (ancestor instanceof Window) {
((Window) ancestor).setVisible(false);
}
}
/**
* Returns the TabId of the next tab after or before the specified tab.
* The <code>forward</code> argument specifies direction: true for next
* tab, false for previous tab.
*/
private TabId findNextTab(TabId tabId, boolean forward) {
// Get index for tab item.
int index = tabId.ordinal();
// Get array of TabId values.
TabId[] tabIds = TabId.values();
// Get normalized index of next/previous tab.
int nextTab = (index + (forward ? 1 : -1) + tabIds.length) % tabIds.length;
// Return TabId.
return tabIds[nextTab];
}
/**
* Selects the tab item with the specified identifier.
* @param tabId the identifier of the tab
*/
private void select(TabId tabId) {
// De-select current item.
if (selectedItem != null) {
selectedItem.fireSelected(false);
}
// Set new selected item.
selectedItem = tabItemMap.get(tabId);
// Get tab panel, and add to container if necessary.
final AdvancedPanel tabPanel = selectedItem.getTabPanel();
if (!cardPanel.isAncestorOf(tabPanel)) {
tabPanel.setBackground(background);
cardPanel.add(tabPanel, tabId.getName());
}
// Display selected tab panel.
cardLayout.show(cardPanel, tabId.getName());
// Fire event to select tab.
selectedItem.fireSelected(true);
// Post an event to request focus on first input field.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
tabPanel.requestFocusFirstComponent();
}
});
}
/**
* Displays this panel in a popup window at the (x, y) position relative
* to the specified invoker.
*/
public void showPopup(JComponent invoker, int x, int y, int width) {
// Select tab.
if (selectedItem == null) {
select(TabId.AUDIO);
}
// Adjust container to desired width.
setPreferredSize(new Dimension(width, getPreferredSize().height));
// Create popup and display.
createPopup(invoker, x, y).setVisible(true);
}
/**
* A tab item for the Advanced Search popup.
*/
private class AdvancedSearchTabItem extends AbstractTabItem {
private final TabId tabId;
private AdvancedPanel tabPanel;
public AdvancedSearchTabItem(TabId tabId) {
this.tabId = tabId;
}
@Override
public String getId() {
return tabId.getName();
}
@Override
public void select() {
AdvancedPopupPanel.this.select(tabId);
}
public TabId getTabId() {
return tabId;
}
public AdvancedPanel getTabPanel() {
if (tabPanel == null) {
tabPanel = advancedPanelFactory.create(tabId.getCategory(), searchAction);
}
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;
public TabAction(AdvancedSearchTabItem tabItem, Icon icon) {
super(tabItem.getTabId().getName(), icon);
// Store tab identifier and action command.
tabId = tabItem.getTabId();
putValue(ACTION_COMMAND_KEY, tabId.getName());
// Install listener to handle tab item selection.
tabItem.addTabItemListener(new TabItemListener() {
@Override
public void itemSelected(boolean selected) {
putValue(SELECTED_KEY, selected);
}
});
}
@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) {
select(findNextTab(selectedItem.getTabId(), true));
}
}
/**
* 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) {
select(findNextTab(selectedItem.getTabId(), false));
}
}
/**
* An Action that handles events to select the previous tab.
*/
private class SearchAction extends AbstractAction {
public SearchAction() {
super(I18n.tr("Search"));
}
@Override
public void actionPerformed(ActionEvent e) {
// Get advanced search values.
AdvancedPanel visiblePanel = selectedItem.getTabPanel();
Map<FilePropertyKey, String> searchData = visiblePanel.getSearchData();
// Close popup.
disposePopup();
// Start search if any fields filled in.
if (searchData != null) {
SearchInfo info = advancedSearchBuilder.get().createAdvancedSearch(
searchData, visiblePanel.getSearchCategory());
searchHandler.doSearch(info);
}
}
}
}