package com.limegroup.gnutella.gui.search; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.awt.Graphics; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.HashMap; import java.util.HashSet; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JViewport; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.LineBorder; import com.limegroup.gnutella.MediaType; import com.limegroup.gnutella.RouterService; import com.limegroup.gnutella.gui.AutoCompleteTextField; import com.limegroup.gnutella.gui.BoxPanel; import com.limegroup.gnutella.gui.ClearableAutoCompleteTextField; import com.limegroup.gnutella.gui.GUIMediator; import com.limegroup.gnutella.gui.GUIUtils; import com.limegroup.gnutella.gui.KeyProcessingTextField; import com.limegroup.gnutella.gui.themes.ThemeFileHandler; import com.limegroup.gnutella.gui.themes.ThemeSettings; import com.limegroup.gnutella.gui.xml.InputPanel; import com.limegroup.gnutella.settings.FilterSettings; import com.limegroup.gnutella.util.NetworkUtils; import com.limegroup.gnutella.xml.LimeXMLSchema; /** * Inner panel that switches between the various kinds of * searching. */ class SearchInputPanel extends JPanel { /** * The current search label in what's new. */ private final JLabel WHATSNEW_SEARCH_LABEL = new JLabel(); /** * The current search label in normal search. */ private final JLabel SEARCH_TYPE_LABEL = new JLabel(); /** * The sole input text field that is at the top of all searches. */ private final SearchField SEARCH_FIELD = new SearchField(14); /** * The text area that contains the information about direct-connecting * to this host. */ private final JTextArea IP_TEXT = new JTextArea(); /** * The input field for browse-host searches. */ private final AutoCompleteTextField BROWSE_HOST_FIELD = new ClearableAutoCompleteTextField(); /** * The What's New search button. */ private final JButton WHATSNEW = new JButton( GUIMediator.getStringResource("SEARCH_TYPE_WHATSNEW")); /** * The JTabbedPane that switches between types of searches. */ private final JTabbedPane PANE = new JTabbedPane(JTabbedPane.BOTTOM); /** * The CardLayout that switches between the detailed * search input information for each meta-type. */ private final CardLayout META_CARDS = new CardLayout(); /** * The panel that the META_CARDS layout uses to layout * the detailed search input fields. */ private final JPanel META_PANEL = new JPanel(META_CARDS); /** * The name to use for the default panel that has no meta-data. */ private static final String DEFAULT_PANEL_KEY = "defaultPanel"; /** * The box that holds the schemas for searching. */ private final SchemaBox SCHEMA_BOX = new SchemaBox(); /** * The ditherer to use for the tab backgrounds. */ private final Ditherer DITHERER = new Ditherer(62, ThemeFileHandler.SEARCH_PANEL_BG_1.getValue(), ThemeFileHandler.SEARCH_PANEL_BG_2.getValue() ); private JPanel searchEntry; /** * The listener for new searches. */ private final ActionListener SEARCH_LISTENER = new SearchListener(); /** * Holds the keys of the already created input panels. */ private HashSet inputPanelKeys = null; /** * A HashMap for each input panel's preferred dimension * where the key is the <tt>NameMediaType</tt> of the panel */ private HashMap inputPanelDimensions = new HashMap(); SearchInputPanel() { super(new BorderLayout(0, 5)); final ActionListener schemaListener = new SchemaListener(); SCHEMA_BOX.addSelectionListener(schemaListener); add(SCHEMA_BOX, BorderLayout.NORTH); searchEntry = createSearchEntryPanel(); JPanel whatsnew = createWhatIsNewPanel(); JPanel browseHost = createBrowseHostPanel(); panelize(searchEntry); panelize(whatsnew); panelize(browseHost); PANE.add(GUIMediator.getStringResource("SEARCH_TYPE_KEYWORD"), searchEntry); PANE.add(GUIMediator.getStringResource("SEARCH_TYPE_WHATSNEW"), whatsnew); PANE.add(GUIMediator.getStringResource("SEARCH_TYPE_BROWSEHOST"), browseHost); PANE.setRequestFocusEnabled(false); PANE.addMouseListener(new MouseListener() { public void mouseClicked(MouseEvent e) { requestSearchFocusImmediately(); } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} }); if(!ThemeSettings.isNativeTheme()) { PANE.setBorder( new LineBorder(ThemeFileHandler.SEARCH_GRID_COLOR.getValue()) { public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { try { Component sel = PANE.getSelectedComponent(); if(sel != null) height = sel.getBounds().height + 4; } catch(ArrayIndexOutOfBoundsException aioobe) {} super.paintBorder(c, g, x, y, width, height); } }); } add(PANE, BorderLayout.CENTER); add(Box.createVerticalStrut(20), BorderLayout.SOUTH); WHATSNEW_SEARCH_LABEL.setFont(UIManager.getFont("Table.font.bold")); Font bold = UIManager.getFont("Table.font.bold"); Font bolder = new Font(bold.getName(), bold.getStyle(), bold.getSize() + 5); SEARCH_TYPE_LABEL.setFont(bolder); SEARCH_TYPE_LABEL.setPreferredSize(new Dimension(100, 20)); schemaListener.actionPerformed(null); } /** * Gets the KeyProcessingTextField that key events can be forwarded to. */ KeyProcessingTextField getForwardingSearchField() { if(isNormalSearchType()) { if(SCHEMA_BOX.getSelectedSchema() != null) { return getInputPanel().getFirstTextField(); } return SEARCH_FIELD; } if(isBrowseHostSearchType()) return BROWSE_HOST_FIELD; return null; } /** * Determines if a key event can be forwarded to the search. */ boolean isKeyEventForwardable() { return isNormalSearchType() || isBrowseHostSearchType(); } /** * Determines if browse-host is selected. */ boolean isBrowseHostSearchType() { return PANE.getSelectedIndex() == 2; } /** * Determines if what is new is selected. */ boolean isWhatIsNewSearchType() { return PANE.getSelectedIndex() == 1; } /** * Determines if keyword is selected. */ boolean isNormalSearchType() { return PANE.getSelectedIndex() == 0; } /** * Notification that the addr has changed. */ void addressChanged() { updateIpText(); invalidate(); revalidate(); } void requestSearchFocusImmediately() { if(isNormalSearchType()) { if(SCHEMA_BOX.getSelectedSchema() != null) { getInputPanel().requestFirstFocus(); } else { SEARCH_FIELD.requestFocus(); } } else if(isWhatIsNewSearchType()) { WHATSNEW.requestFocus(); } else if(isBrowseHostSearchType()) { BROWSE_HOST_FIELD.requestFocus(); } } void requestSearchFocus() { // Workaround for bug manifested on Java 1.3 where FocusEvents // are improperly posted, causing BasicTabbedPaneUI to throw an // ArrayIndexOutOfBoundsException. // See: // http://developer.java.sun.com/developer/bugParade/bugs/4523606.html // http://developer.java.sun.com/developer/bugParade/bugs/4379600.html // http://developer.java.sun.com/developer/bugParade/bugs/4128120.html // for related problems. SwingUtilities.invokeLater(new Runnable() { public void run() { requestSearchFocusImmediately(); } }); } /** * Sets all components in this component to be not opaque * and sets the correct background panel. */ private void panelize(JComponent c) { GUIUtils.setOpaque(false, c); if(!ThemeSettings.isNativeTheme()) c.setOpaque(true); c.setBackground(ThemeFileHandler.SEARCH_PANEL_BG_2.getValue()); c.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 0)); } /** * Creates the 'Direct Connect' browse host input panel. */ private JPanel createBrowseHostPanel() { BROWSE_HOST_FIELD.addActionListener(SEARCH_LISTENER); JPanel panel = new DitherPanel(DITHERER); panel.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.gridwidth = GridBagConstraints.REMAINDER; JLabel label = new JLabel(GUIMediator.getStringResource("SEARCH_BROWSEHOST_TITLE")); label.setFont(UIManager.getFont("Table.font.bold")); panel.add(label, c); JTextArea text1 = new JTextArea(GUIMediator.getStringResource("SEARCH_BROWSEHOST_DESC")); text1.setLineWrap(true); text1.setWrapStyleWord(true); text1.setColumns(15); text1.setEditable(false); text1.setFont(UIManager.getFont("Table.font")); text1.setForeground(label.getForeground()); c.insets = new Insets(15, 0, 0, 0); panel.add(text1, c); updateIpText(); IP_TEXT.setLineWrap(true); IP_TEXT.setWrapStyleWord(true); IP_TEXT.setColumns(15); IP_TEXT.setEditable(false); IP_TEXT.setFont(UIManager.getFont("Table.font")); IP_TEXT.setForeground(label.getForeground()); panel.add(IP_TEXT, c); c.fill = GridBagConstraints.HORIZONTAL; panel.add(BROWSE_HOST_FIELD, c); JButton search = new JButton(GUIMediator.getStringResource("SEARCH_BROWSEHOST_BUTTON")); search.addActionListener(SEARCH_LISTENER); c.fill = GridBagConstraints.NONE; panel.add(search, c); c.weighty = 1; panel.add(Box.createVerticalGlue(), c); return panel; } private void updateIpText() { if(RouterService.acceptedIncomingConnection() && !NetworkUtils.isPrivate()) { IP_TEXT.setText( GUIMediator.getStringResource("SEARCH_BROWSEHOST_MYIP")+" \""+ NetworkUtils.ip2string(RouterService.getAddress())+":" +RouterService.getPort()+"\"" ); } else { IP_TEXT.setText(GUIMediator.getStringResource("SEARCH_BROWSEHOST_FIREWALLED")); } } /** * Creates the What's New input panel. */ private JPanel createWhatIsNewPanel() { JPanel panel = new DitherPanel(DITHERER); panel.setLayout(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.gridwidth = GridBagConstraints.REMAINDER; JLabel label = new JLabel(GUIMediator.getStringResource("SEARCH_WHATSNEW_TITLE_ONE")); label.setFont(UIManager.getFont("Table.font.bold")); panel.add(label, c); JLabel label2 = new JLabel(GUIMediator.getStringResource("SEARCH_WHATSNEW_TITLE_TWO")); label2.setFont(UIManager.getFont("Table.font.bold")); panel.add(label2, c); JTextArea text = new JTextArea(GUIMediator.getStringResource("SEARCH_WHATSNEW_DESC")); text.setLineWrap(true); text.setWrapStyleWord(true); text.setColumns(15); text.setEditable(false); text.setFont(UIManager.getFont("Table.font")); text.setForeground(label.getForeground()); c.insets = new Insets(15, 0, 30, 0); panel.add(text, c); JTextArea type = new JTextArea( GUIMediator.getStringResource("SEARCH_CURRENT_SEARCH") + " "); type.setFont(UIManager.getFont("Table.font")); type.setEditable(false); type.setForeground(label.getForeground()); c.anchor = GridBagConstraints.EAST; c.gridwidth = GridBagConstraints.RELATIVE; c.insets = new Insets(0, 0, 0, 0); panel.add(type, c); c.anchor = GridBagConstraints.WEST; c.gridwidth = GridBagConstraints.REMAINDER; panel.add(WHATSNEW_SEARCH_LABEL, c); WHATSNEW.addActionListener(SEARCH_LISTENER); c.insets = new Insets(5, 0, 30, 0); c.anchor = GridBagConstraints.CENTER; panel.add(WHATSNEW, c); final JCheckBox hideAdult = new JCheckBox( GUIMediator.getStringResource("SEARCH_WHATSNEW_HIDE_ADULT"), FilterSettings.FILTER_WHATS_NEW_ADULT.getValue()); hideAdult.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { FilterSettings.FILTER_WHATS_NEW_ADULT. setValue(hideAdult.isSelected()); } }); c.insets = new Insets(0, 0, 0, 0); panel.add(hideAdult, c); hideAdult.setVisible(!FilterSettings.FILTER_ADULT.getValue()); // Add a blank item with a weighty that'll push the rest to the top. c.weighty = 1; panel.add(Box.createVerticalGlue(), c); return panel; } private JPanel createSearchEntryPanel() { SEARCH_FIELD.addActionListener(SEARCH_LISTENER); // add the default search input panel to the meta cards META_PANEL.add(createDefaultSearchPanel(), DEFAULT_PANEL_KEY); // other mediatype panels are added lazily on demand JPanel search = new DitherPanel(DITHERER); search.setLayout(new BoxLayout(search, BoxLayout.Y_AXIS)); search.add(GUIUtils.left(SEARCH_TYPE_LABEL)); search.add(Box.createVerticalStrut(5)); search.add(META_PANEL); return search; } private void createInputPanelForNamedMediaType(NamedMediaType nmt) { String name = nmt.getName(); LimeXMLSchema schema = nmt.getSchema(); // If a schema exists, add it as a possible type. if(schema == null) { throw new NullPointerException("named mediatype has no schema"); } InputPanel panel = new InputPanel(schema, SEARCH_LISTENER, SEARCH_FIELD.getDocument(), SEARCH_FIELD.getUndoManager()); panel.addMoreOptionsListener(new MoreOptionsListener()); JScrollPane pane = new JScrollPane(panel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); pane.setOpaque(false); pane.setBorder(BorderFactory.createEmptyBorder()); pane.getViewport().setBorder(null); JPanel outerPanel = new JPanel(new BorderLayout()); outerPanel.add(pane,"Center"); outerPanel.add(createSearchButtonPanel(),"South"); int paneWidth = (int)pane.getPreferredSize().getWidth(); int paneHeight= (int)pane.getPreferredSize().getHeight(); Dimension dim = new Dimension(paneWidth+70,paneHeight+30); outerPanel.setMaximumSize(dim); inputPanelDimensions.put(nmt,dim); JPanel holdingPanel = new JPanel(); holdingPanel.setLayout(new BoxLayout(holdingPanel,BoxLayout.Y_AXIS)); holdingPanel.add(outerPanel); getInputPanelKeys().add(name); META_PANEL.add(holdingPanel, name); panelize(searchEntry); } private HashSet getInputPanelKeys() { if (inputPanelKeys == null) { inputPanelKeys = new HashSet(); } return inputPanelKeys; } /** * Creates the default search input of: * Filename * [ input box ] */ private JPanel createDefaultSearchPanel() { JPanel label = createLabel( GUIMediator.getStringResource("SEARCH_DEFAULT_FIELD_TITLE")); JPanel fullPanel = new BoxPanel(BoxPanel.Y_AXIS); fullPanel.add(label); fullPanel.add(Box.createVerticalStrut(3)); fullPanel.add(GUIUtils.left(SEARCH_FIELD)); fullPanel.add(Box.createVerticalStrut(5)); fullPanel.add(createSearchButtonPanel()); return GUIUtils.left(fullPanel); } private JPanel createLabel(String text) { JPanel labelPanel = new BoxPanel(BoxPanel.X_AXIS); labelPanel.setOpaque(false); labelPanel.add(new JLabel(text)); labelPanel.add(Box.createHorizontalGlue()); return labelPanel; } /** * Creates the search button & inserts it in a panel. */ private JPanel createSearchButtonPanel() { JPanel b = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); JButton searchButton = new JButton( GUIMediator.getStringResource("SEARCH_SEARCH_BUTTON_LABEL")); searchButton.setToolTipText( GUIMediator.getStringResource("SEARCH_SEARCH_BUTTON_TIP")); searchButton.addActionListener(SEARCH_LISTENER); b.add(Box.createHorizontalGlue()); b.add(searchButton); return b; } /** * Gets the visible component in META_PANEL. */ private JComponent getVisibleComponent() { for(int i = 0; i < META_PANEL.getComponentCount(); i++) { Component current = META_PANEL.getComponent(i); if(current.isVisible()) return (JComponent)current; } return null; } /** * Gets the visible scrollpane. */ private JScrollPane getVisibleScrollPane() { JComponent parent = (JComponent)getVisibleComponent().getComponent(0); for(int i = 0; i < parent.getComponentCount(); i++) { Component current = parent.getComponent(i); if(current.isVisible() && current instanceof JScrollPane) return (JScrollPane)current; } return null; } /** * Retrieves the InputPanel that is currently visible. */ private InputPanel getInputPanel() { JScrollPane pane = getVisibleScrollPane(); if(pane == null) return null; else return (InputPanel)pane.getViewport().getView(); } /** * Listener for selecting a new schema. */ private class SchemaListener implements ActionListener { public void actionPerformed(ActionEvent event) { if(SCHEMA_BOX.getSelectedSchema() != null) { String key = SCHEMA_BOX.getSelectedItem(); if (!getInputPanelKeys().contains(key)) { createInputPanelForNamedMediaType (SCHEMA_BOX.getSelectedMedia()); } META_CARDS.show(META_PANEL, key); } else { META_CARDS.show(META_PANEL, DEFAULT_PANEL_KEY); } WHATSNEW_SEARCH_LABEL.setText(SCHEMA_BOX.getSelectedItem()); WHATSNEW_SEARCH_LABEL.setPreferredSize( new Dimension(GUIUtils.width(WHATSNEW_SEARCH_LABEL), 20)); SEARCH_TYPE_LABEL.setText(SCHEMA_BOX.getSelectedItem()); requestSearchFocus(); } } /** * Listener for starting a new search. */ private class SearchListener implements ActionListener { public void actionPerformed(ActionEvent e) { String query = null; String xml = null; MediaType mt = SCHEMA_BOX.getSelectedMediaType(); InputPanel panel = null; SearchInformation info = null; if(isWhatIsNewSearchType()) { info = SearchInformation.createWhatsNewSearch( SCHEMA_BOX.getSelectedItem(), mt); } else if(isNormalSearchType()) { if(SCHEMA_BOX.getSelectedSchema() != null) { panel = getInputPanel(); query = panel.getStandardQuery(); xml = panel.getInput(true); } else { query = SEARCH_FIELD.getText(); } info = SearchInformation.createKeywordSearch(query, xml, mt); } else if(isBrowseHostSearchType()) { String user = BROWSE_HOST_FIELD.getText(); info = SearchInformation.createBrowseHostSearch(user); mt = MediaType.getAnyTypeMediaType(); // always any } // If the search worked, store & clear it. if(SearchMediator.triggerSearch(info) != null) { if(info.isKeywordSearch()) { // Add the necessary stuff for autocompletion. if(panel != null) { panel.storeInput(); panel.clear(); } else { SEARCH_FIELD.addToDictionary(); } // Clear the existing search. SEARCH_FIELD.setText(""); } else if(info.isBrowseHostSearch()) { BROWSE_HOST_FIELD.addToDictionary(); BROWSE_HOST_FIELD.setText(""); } } } } /** * Listener for 'more options'. */ private class MoreOptionsListener implements ActionListener { public void actionPerformed(ActionEvent e) { JCheckBox box = (JCheckBox)e.getSource(); JComponent c = (JComponent)((JComponent)getVisibleComponent()).getComponent(0); JComponent pane = (JComponent)((JComponent)c.getComponent(0)).getComponent(0); if(c instanceof JPanel && pane instanceof JViewport) { if(box.isSelected()) { c.setMaximumSize(null); } else { Dimension dim = (Dimension)inputPanelDimensions.get(SCHEMA_BOX.getSelectedMedia()); if(dim != null) c.setMaximumSize(dim); } } invalidate(); revalidate(); repaint(); } } }