/* GanttProject is an opensource project management tool. License: GPL3 Copyright (C) 2011 Dmitry Barashev, GanttProject Team This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package net.sourceforge.ganttproject.search; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.ListModel; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import net.sourceforge.ganttproject.IGanttProject; import net.sourceforge.ganttproject.action.CancelAction; import net.sourceforge.ganttproject.action.GPAction; import net.sourceforge.ganttproject.gui.UIFacade; import net.sourceforge.ganttproject.language.GanttLanguage; import net.sourceforge.ganttproject.plugins.PluginManager; public class SearchDialog { public interface SearchCallback { void accept(List<SearchResult<?>> results); } private final UIFacade myUiFacade; private DefaultListModel myResultViewDataModel; private final IGanttProject myProject; private JList myResultView; private UIFacade.Dialog myDialog; private SearchCallback mySearchCallback; public SearchDialog(IGanttProject project, UIFacade uiFacade) { myProject = project; myUiFacade = uiFacade; myResultViewDataModel = new DefaultListModel(); myDialog = myUiFacade.createDialog(getComponent(), new Action[] { new CancelAction("search.dialog.goto") { @Override public void actionPerformed(ActionEvent e) { gotoSelection(); } }, new CancelAction("close") }, GanttLanguage.getInstance().getText("search.dialog.title")); mySearchCallback = new SearchCallback() { @Override public void accept(List<SearchResult<?>> results) { processResults(results); } }; } public void show() { myDialog.show(); } protected void gotoSelection() { SearchResult selectedValue = (SearchResult) myResultView.getSelectedValue(); if (selectedValue != null) { selectedValue.getSearchService().select(Collections.singletonList(selectedValue)); } } public JTextField createSearchBox(final SearchCallback callback) { final JTextField inputField = new JTextField(30); inputField.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { runSearch(inputField.getText(), callback); } } }); return inputField; } private JComponent getComponent() { JPanel result = new JPanel(new BorderLayout()); JPanel inputPanel = new JPanel(new BorderLayout()); final JTextField inputField = new JTextField(30); final GPAction searchAction = new GPAction("search.dialog.search") { @Override public void actionPerformed(ActionEvent arg0) { runSearch(inputField.getText(), mySearchCallback); } }; JButton searchButton = new JButton(searchAction); // TODO Add keyboard shortcut to searchAction inputField.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { searchAction.actionPerformed(null); } } }); inputPanel.add(inputField, BorderLayout.CENTER); inputPanel.add(searchButton, BorderLayout.EAST); inputPanel.setBorder(BorderFactory.createEmptyBorder(0, 0, 5, 0)); result.add(inputPanel, BorderLayout.NORTH); myResultView = new JList(getResultViewDataModel()); myResultView.addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { gotoSelection(); myDialog.hide(); } } }); result.add(new JScrollPane(myResultView), BorderLayout.CENTER); result.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); return result; } private ListModel getResultViewDataModel() { return myResultViewDataModel; } void runSearch(final String text, final SearchCallback callback) { myResultViewDataModel.clear(); List<SearchService> services = PluginManager.getExtensions(SearchService.EXTENSION_POINT_ID, SearchService.class); final List<Future<List<SearchResult<?>>>> tasks = new ArrayList<Future<List<SearchResult<?>>>>(); ExecutorService executor = Executors.newFixedThreadPool(services.size()); for (final SearchService<SearchResult<?>, ?> service : services) { service.init(myProject, myUiFacade); tasks.add(executor.submit(new Callable<List<SearchResult<?>>>() { @Override public List<SearchResult<?>> call() throws Exception { List<SearchResult<?>> search = service.search(text); return search; } })); } SwingWorker<List<SearchResult<?>>, Object> worker = new SwingWorker<List<SearchResult<?>>, Object>() { @Override protected List<SearchResult<?>> doInBackground() throws Exception { List<SearchResult<?>> totalResult = new ArrayList<SearchResult<?>>(); for (Future<List<SearchResult<?>>> f : tasks) { totalResult.addAll(f.get()); } return totalResult; } @Override protected void done() { try { callback.accept(get()); processResults(get()); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }; worker.execute(); } protected void processResults(final List<SearchResult<?>> results) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { for (SearchResult<?> r : results) { myResultViewDataModel.addElement(r); } } }); } }