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