package de.onyxbits.raccoon.gui; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.HashMap; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JEditorPane; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JSpinner; import javax.swing.JTextField; import javax.swing.SpinnerNumberModel; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; import de.onyxbits.raccoon.App; import de.onyxbits.raccoon.BrowseUtil; import de.onyxbits.raccoon.Messages; import de.onyxbits.raccoon.io.Archive; import de.onyxbits.raccoon.io.DownloadLogger; import de.onyxbits.raccoon.io.FileNode; import de.onyxbits.raccoon.rss.Loader; import de.onyxbits.raccoon.rss.Parser; /** * A container for putting search results in. * * @author patrick * */ public class SearchView extends JPanel implements ActionListener, ChangeListener, Runnable, HyperlinkListener { /** * */ private static final long serialVersionUID = 1L; protected Archive archive; private static final String CARDRESULTS = "results"; //$NON-NLS-1$ private static final String CARDPROGRESS = "progress"; //$NON-NLS-1$ private static final String CARDMESSAGE = "message"; //$NON-NLS-1$ private JTextField query; private JSpinner page; private JScrollPane results; private JProgressBar progress; private JEditorPane message; private JButton cancel; private JPanel main; private CardLayout cardLayout; private MainActivity mainActivity; private SearchWorker searcher; private SearchView(MainActivity mainActivity, Archive archive) { this.archive = archive; this.mainActivity = mainActivity; setLayout(new BorderLayout()); query = new JTextField(); query.setToolTipText(Messages.getString("SearchView.3")); //$NON-NLS-1$ query.setMargin(new Insets(3,3,3,3)); page = new JSpinner(new SpinnerNumberModel(1, 1, 10000, 1)); page.setToolTipText(Messages.getString("SearchView.4")); //$NON-NLS-1$ results = new JScrollPane(); cardLayout = new CardLayout(); message = new HypertextPane(""); //$NON-NLS-1$ message.setMargin(new Insets(10, 10, 10, 10)); progress = new JProgressBar(); progress.setIndeterminate(true); progress.setString(Messages.getString("SearchView.5")); //$NON-NLS-1$ progress.setStringPainted(true); cancel = new JButton(Messages.getString("SearchView.0")); //$NON-NLS-1$ GridBagConstraints center = new GridBagConstraints(); center.anchor = GridBagConstraints.CENTER; center.fill = GridBagConstraints.NONE; main = new JPanel(); main.setLayout(cardLayout); main.add(new JScrollPane(message), CARDMESSAGE); main.add(results, CARDRESULTS); JPanel container = new JPanel(); container.setOpaque(false); container.setLayout(new GridBagLayout()); JPanel inner = new JPanel(); inner.add(progress); inner.add(cancel); container.add(inner, center); main.add(new JScrollPane(container), CARDPROGRESS); container = new JPanel(); container.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); container.setLayout(new BoxLayout(container, BoxLayout.X_AXIS)); container.add(query); container.add(page); add(container, BorderLayout.NORTH); add(main, BorderLayout.CENTER); } public static SearchView create(MainActivity mainActivity, Archive archive) { SearchView ret = new SearchView(mainActivity, archive); ret.query.addActionListener(ret); ret.page.addChangeListener(ret); ret.cancel.addActionListener(ret); ret.message.addHyperlinkListener(new BrowseUtil()); ret.doMessage(""); //$NON-NLS-1$ ret.message.addHyperlinkListener(ret); return ret; } public void actionPerformed(ActionEvent event) { if (event.getSource() == query) { page.setValue(1); doSearch(); } if (event.getSource() == cancel) { if (searcher != null) { searcher.cancel(true); } run(); } } /** * Display a message. * * @param status * message to show. */ protected void doMessage(String status) { HashMap<String, Object> model = new HashMap<String, Object>(); model.put("message", status); //$NON-NLS-1$ model.put("app_version", App.VERSIONSTRING); //$NON-NLS-1$ model.put("i18n_latestnews", Messages.getString("SearchView.2")); //$NON-NLS-1$ //$NON-NLS-2$ model.put("i18n_lastsession", Messages.getString("SearchView.10")); //$NON-NLS-1$ //$NON-NLS-2$ model.put("i18n_archive_appcount", Messages.getString("SearchView.1")); //$NON-NLS-1$ //$NON-NLS-2$ model.put("i18n_archive_folder", Messages.getString("SearchView.11")); //$NON-NLS-1$ //$NON-NLS-2$ model.put("i18n_none", Messages.getString("SearchView.12")); //$NON-NLS-1$ //$NON-NLS-2$ model.put("archive_count", archive.countApps()); //$NON-NLS-1$ model.put("archive_folder", new FileNode(archive.getRoot())); //$NON-NLS-1$ model.put("archive_account",archive.getUserId()); //$NON-NLS-1$ model.put("i18n_archive_account",Messages.getString("SearchView.15")); //$NON-NLS-1$ //$NON-NLS-2$ try { Parser parser = new Parser(Loader.getFeedCache().toURI().toString()); model.put("newsfeed", parser.readFeed().getMessages()); //$NON-NLS-1$ } catch (Exception e) { // We can do without the newsfeed // e.printStackTrace(); } try { DownloadLogger dl = new DownloadLogger(archive); model.put("lastsession", dl.getLastSessionDownloads()); //$NON-NLS-1$ } catch (Exception e) { // We can do without the latest downloads. // e.printStackTrace(); } message.setText(TmplTool.transform("splash.html", model)); //$NON-NLS-1$ cardLayout.show(main, CARDMESSAGE); query.setEnabled(true); page.setEnabled(true); } public void stateChanged(ChangeEvent event) { if (event.getSource() == page) { doSearch(); } // Slightly ugly: a searchview is meant to sit in a JTabbedPane and // registered as it's ChangeListener, so the query can get focus whenever // the user switches to this view. In fact, we consider the query field to // be so important that we always focus it, no matter what. query.requestFocusInWindow(); } /** * After adding this view to a JTabbedPane, post it to the EDT via * SwingUtils.invokeLater() to ensure the query gets the inputfocus. */ public void run() { query.requestFocusInWindow(); } protected void doSearch() { if (query.getText().length() == 0) { doMessage(Messages.getString("SearchView.6")); //$NON-NLS-1$ } else { query.setEnabled(false); page.setEnabled(false); cardLayout.show(main, CARDPROGRESS); int offset = (Integer) page.getValue(); offset = (offset - 1) * 10; searcher = new SearchWorker(archive, query.getText(), this).withOffset(offset).withLimit(10); searcher.execute(); } } protected void doResultList(JPanel listing) { query.setEnabled(true); page.setEnabled(true); cardLayout.show(main, CARDRESULTS); results.setViewportView(listing); } public void doUpdateSearch() { query.setEnabled(false); page.setEnabled(false); cardLayout.show(main, CARDPROGRESS); searcher = new SearchWorker(archive, null, this); searcher.execute(); } protected void doDownload(DownloadView d) { if (d.isDownloaded()) { int result = JOptionPane.showConfirmDialog(getRootPane(), Messages.getString("SearchView.7"), Messages.getString("SearchView.8"), //$NON-NLS-1$ //$NON-NLS-2$ JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE); if (result == JOptionPane.YES_OPTION) { mainActivity.doDownload(d); } } else { mainActivity.doDownload(d); } } public Archive getArchive() { return archive; } @Override public void hyperlinkUpdate(HyperlinkEvent event) { String prefix = "market://details?id="; //$NON-NLS-1$ if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { try { String url = event.getDescription(); if (url.startsWith(prefix) && url.length() > prefix.length()) { // Let's keep it simple. query.setText(url.substring(prefix.length(), url.length())); doSearch(); } } catch (Exception e) { e.printStackTrace(); } } } }