JAL-2933 gui and applet Finder hold a FinderI per viewport
[jalview.git] / src / jalview / appletgui / Finder.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.appletgui;
22
23 import jalview.api.AlignViewportI;
24 import jalview.api.FinderI;
25 import jalview.datamodel.SearchResultMatchI;
26 import jalview.datamodel.SearchResultsI;
27 import jalview.datamodel.SequenceFeature;
28 import jalview.datamodel.SequenceGroup;
29 import jalview.datamodel.SequenceI;
30 import jalview.util.MessageManager;
31
32 import java.awt.Button;
33 import java.awt.Checkbox;
34 import java.awt.Font;
35 import java.awt.Frame;
36 import java.awt.GridLayout;
37 import java.awt.Label;
38 import java.awt.Panel;
39 import java.awt.Rectangle;
40 import java.awt.TextField;
41 import java.awt.event.ActionEvent;
42 import java.awt.event.ActionListener;
43 import java.awt.event.KeyEvent;
44 import java.awt.event.WindowAdapter;
45 import java.awt.event.WindowEvent;
46 import java.util.ArrayList;
47 import java.util.HashMap;
48 import java.util.List;
49 import java.util.Map;
50
51 public class Finder extends Panel implements ActionListener
52 {
53   private AlignViewportI av;
54
55   private AlignmentPanel ap;
56
57   private TextField textfield = new TextField();
58
59   private Button findAll = new Button();
60
61   private Button findNext = new Button();
62
63   private Button createFeatures = new Button();
64
65   private Checkbox caseSensitive = new Checkbox();
66
67   private Checkbox searchDescription = new Checkbox();
68
69   private SearchResultsI searchResults;
70
71   /*
72    * Finder agent per viewport searched
73    */
74   Map<AlignViewportI, FinderI> finders;
75
76   public Finder(final AlignmentPanel ap)
77   {
78     finders = new HashMap<>();
79
80     try
81     {
82       jbInit();
83
84     } catch (Exception e)
85     {
86       e.printStackTrace();
87     }
88
89     this.av = ap.av;
90     this.ap = ap;
91     Frame frame = new Frame();
92     frame.add(this);
93     jalview.bin.JalviewLite.addFrame(frame,
94             MessageManager.getString("action.find"), 340, 120);
95     frame.repaint();
96     frame.addWindowListener(new WindowAdapter()
97     {
98       @Override
99       public void windowClosing(WindowEvent evt)
100       {
101         ap.highlightSearchResults(null);
102       }
103     });
104     textfield.requestFocus();
105   }
106
107   @Override
108   public void actionPerformed(ActionEvent evt)
109   {
110     if (evt.getSource() == textfield)
111     {
112       doSearch(false);
113     }
114
115     else if (evt.getSource() == findNext)
116     {
117       doSearch(false);
118     }
119
120     else if (evt.getSource() == findAll)
121     {
122       doSearch(true);
123     }
124     else if (evt.getSource() == createFeatures)
125     {
126       createFeatures_actionPerformed();
127     }
128   }
129
130   public void createFeatures_actionPerformed()
131   {
132     List<SequenceI> seqs = new ArrayList<>();
133     List<SequenceFeature> features = new ArrayList<>();
134     String searchString = textfield.getText().trim();
135
136     for (SearchResultMatchI match : searchResults.getResults())
137     {
138       seqs.add(match.getSequence().getDatasetSequence());
139       features.add(new SequenceFeature(searchString, "Search Results",
140               match.getStart(), match.getEnd(), "Search Results"));
141     }
142
143     if (ap.seqPanel.seqCanvas.getFeatureRenderer().amendFeatures(seqs,
144             features, true, ap))
145     {
146       ap.alignFrame.sequenceFeatures.setState(true);
147       av.setShowSequenceFeatures(true);
148       ap.highlightSearchResults(null);
149     }
150   }
151
152   void doSearch(boolean doFindAll)
153   {
154     if (ap.av.applet.currentAlignFrame != null)
155     {
156       ap = ap.av.applet.currentAlignFrame.alignPanel;
157       av = ap.av;
158     }
159     createFeatures.setEnabled(false);
160     FinderI finder = finders.get(av);
161     if (finder == null)
162     {
163       /*
164        * first time we searched this viewport
165        */
166       finder = new jalview.analysis.Finder(av.getAlignment());
167       finders.put(av, finder);
168     }
169
170     String searchString = textfield.getText();
171     SequenceGroup selectionGroup = av.getSelectionGroup();
172     boolean isCaseSensitive = caseSensitive.getState();
173     boolean doSearchDescription = searchDescription.getState();
174     if (doFindAll)
175     {
176       finder.findAll(searchString, selectionGroup, isCaseSensitive,
177               doSearchDescription);
178     }
179     else
180     {
181       finder.findNext(searchString, selectionGroup, isCaseSensitive,
182               doSearchDescription);
183     }
184
185     searchResults = finder.getSearchResults();
186
187     List<SequenceI> idMatches = finder.getIdMatches();
188     ap.idPanel.highlightSearchResults(idMatches);
189
190     if (searchResults.isEmpty())
191     {
192       searchResults = null;
193     }
194     else
195     {
196       createFeatures.setEnabled(true);
197     }
198
199     // if allResults is null, this effectively switches displaySearch flag in
200     // seqCanvas
201     ap.highlightSearchResults(searchResults);
202     // TODO: add enablers for 'SelectSequences' or 'SelectColumns' or
203     // 'SelectRegion' selection
204     if (idMatches.isEmpty() && searchResults == null)
205     {
206       ap.alignFrame.statusBar.setText(
207               MessageManager.getString("label.finished_searching"));
208     }
209     else
210     {
211       if (doFindAll)
212       {
213         String message = (idMatches.size() > 0) ? "" + idMatches.size() + " IDs"
214                 : "";
215         if (idMatches.size() > 0 && searchResults != null
216                 && searchResults.getSize() > 0)
217         {
218           message += " and ";
219         }
220         if (searchResults != null)
221         {
222           message += searchResults.getSize() + " subsequence matches.";
223         }
224         ap.alignFrame.statusBar.setText(MessageManager
225                 .formatMessage("label.search_results", new String[]
226                 { searchString, message }));
227
228       }
229       else
230       {
231         // TODO: indicate sequence and matching position in status bar
232         ap.alignFrame.statusBar.setText(MessageManager
233                 .formatMessage("label.found_match_for", new String[]
234                 { searchString }));
235       }
236     }
237   }
238
239   private void jbInit() throws Exception
240   {
241     Label jLabel1 = new Label(MessageManager.getString("action.find"));
242     jLabel1.setFont(new java.awt.Font("Verdana", 0, 12));
243     jLabel1.setBounds(new Rectangle(3, 30, 34, 15));
244     this.setLayout(null);
245     textfield.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
246     textfield.setText("");
247     textfield.setBounds(new Rectangle(40, 17, 133, 21));
248     textfield.addKeyListener(new java.awt.event.KeyAdapter()
249     {
250       @Override
251       public void keyTyped(KeyEvent e)
252       {
253         textfield_keyTyped();
254       }
255     });
256     textfield.addActionListener(this);
257     findAll.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
258     findAll.setLabel(MessageManager.getString("action.find_all"));
259     findAll.addActionListener(this);
260     findNext.setEnabled(false);
261     findNext.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
262     findNext.setLabel(MessageManager.getString("action.find_next"));
263     findNext.addActionListener(this);
264
265     Panel actionsPanel = new Panel();
266     actionsPanel.setBounds(new Rectangle(195, 5, 141, 64));
267     GridLayout gridLayout1 = new GridLayout();
268     actionsPanel.setLayout(gridLayout1);
269     gridLayout1.setHgap(0);
270     gridLayout1.setRows(3);
271     gridLayout1.setVgap(2);
272     createFeatures.setEnabled(false);
273     createFeatures.setFont(new java.awt.Font("Verdana", Font.PLAIN, 10));
274     createFeatures.setLabel(MessageManager.getString("label.new_feature"));
275     createFeatures.addActionListener(this);
276     caseSensitive.setLabel(MessageManager.getString("label.match_case"));
277     caseSensitive.setBounds(new Rectangle(30, 39, 126, 23));
278
279     searchDescription.setLabel(
280             MessageManager.getString("label.include_description"));
281     searchDescription.setBounds(new Rectangle(30, 59, 170, 23));
282     actionsPanel.add(findNext, null);
283     actionsPanel.add(findAll, null);
284     actionsPanel.add(createFeatures, null);
285     this.add(caseSensitive);
286     this.add(textfield, null);
287     this.add(jLabel1, null);
288     this.add(actionsPanel, null);
289     this.add(searchDescription);
290   }
291
292   void textfield_keyTyped()
293   {
294     findNext.setEnabled(true);
295   }
296
297 }