Further abstractions and clean up
[jalview.git] / src / jalview / controller / AlignViewController.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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.controller;
22
23 import jalview.analysis.AlignmentSorter;
24 import jalview.api.AlignViewControllerGuiI;
25 import jalview.api.AlignViewControllerI;
26 import jalview.api.AlignViewportI;
27 import jalview.api.AlignmentViewPanel;
28 import jalview.api.FeatureRenderer;
29 import jalview.commands.OrderCommand;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.Annotation;
32 import jalview.datamodel.ColumnSelection;
33 import jalview.datamodel.SequenceCollectionI;
34 import jalview.datamodel.SequenceFeature;
35 import jalview.datamodel.SequenceGroup;
36 import jalview.datamodel.SequenceI;
37 import jalview.util.MessageManager;
38 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter;
39 import jalview.viewmodel.annotationfilter.AnnotationFilterParameter.SearchableAnnotationField;
40
41 import java.awt.Color;
42 import java.util.ArrayList;
43 import java.util.BitSet;
44 import java.util.List;
45
46 public class AlignViewController implements AlignViewControllerI
47 {
48   AlignViewportI viewport = null;
49
50   AlignmentViewPanel alignPanel = null;
51
52   /**
53    * the GUI container that is handling interactions with the user
54    */
55   private AlignViewControllerGuiI avcg;
56
57   @Override
58   protected void finalize() throws Throwable
59   {
60     viewport = null;
61     alignPanel = null;
62     avcg = null;
63   };
64
65   public AlignViewController(AlignViewControllerGuiI alignFrame,
66           AlignViewportI viewport, AlignmentViewPanel alignPanel)
67   {
68     this.avcg = alignFrame;
69     this.viewport = viewport;
70     this.alignPanel = alignPanel;
71   }
72
73   @Override
74   public void setViewportAndAlignmentPanel(AlignViewportI viewport,
75           AlignmentViewPanel alignPanel)
76   {
77     this.alignPanel = alignPanel;
78     this.viewport = viewport;
79
80   }
81
82   @Override
83   public boolean makeGroupsFromSelection()
84   {
85
86     if (viewport.getSelectionGroup() != null)
87     {
88       SequenceGroup[] gps = jalview.analysis.Grouping.makeGroupsFrom(
89               viewport.getSequenceSelection(),
90               viewport.getAlignmentView(true).getSequenceStrings(
91                       viewport.getGapCharacter()), viewport.getAlignment()
92                       .getGroups());
93       viewport.getAlignment().deleteAllGroups();
94       viewport.clearSequenceColours();
95       viewport.setSelectionGroup(null);
96       // set view properties for each group
97       for (int g = 0; g < gps.length; g++)
98       {
99         // gps[g].setShowunconserved(viewport.getShowUnconserved());
100         gps[g].setshowSequenceLogo(viewport.isShowSequenceLogo());
101         viewport.getAlignment().addGroup(gps[g]);
102         Color col = new Color((int) (Math.random() * 255),
103                 (int) (Math.random() * 255), (int) (Math.random() * 255));
104         col = col.brighter();
105         for (SequenceI sq : gps[g].getSequences(null))
106         {
107           viewport.setSequenceColour(sq, col);
108         }
109       }
110       return true;
111     }
112     return false;
113   }
114
115   @Override
116   public boolean createGroup()
117   {
118
119     SequenceGroup sg = viewport.getSelectionGroup();
120     if (sg != null)
121     {
122       viewport.getAlignment().addGroup(sg);
123       return true;
124     }
125     return false;
126   }
127
128   @Override
129   public boolean unGroup()
130   {
131     SequenceGroup sg = viewport.getSelectionGroup();
132     if (sg != null)
133     {
134       viewport.getAlignment().deleteGroup(sg);
135       return true;
136     }
137     return false;
138   }
139
140   @Override
141   public boolean deleteGroups()
142   {
143     if (viewport.getAlignment().getGroups() != null
144             && viewport.getAlignment().getGroups().size() > 0)
145     {
146       viewport.getAlignment().deleteAllGroups();
147       viewport.clearSequenceColours();
148       viewport.setSelectionGroup(null);
149       return true;
150     }
151     return false;
152   }
153
154   @Override
155   public boolean markColumnsContainingFeatures(boolean invert,
156           boolean extendCurrent, boolean toggle, String featureType)
157   {
158     // JBPNote this routine could also mark rows, not just columns.
159     // need a decent query structure to allow all types of feature searches
160     BitSet bs = new BitSet();
161     int alw, alStart;
162     SequenceCollectionI sqcol = (viewport.getSelectionGroup() == null ? viewport
163             .getAlignment() : viewport.getSelectionGroup());
164     alStart = sqcol.getStartRes();
165     alw = sqcol.getEndRes() + 1;
166     List<SequenceI> seqs = sqcol.getSequences();
167     int nseq = 0;
168     for (SequenceI sq : seqs)
169     {
170       int tfeat = 0;
171       if (sq != null)
172       {
173         SequenceI dsq = sq.getDatasetSequence();
174         while (dsq.getDatasetSequence() != null)
175         {
176           dsq = dsq.getDatasetSequence();
177         }
178         ;
179         SequenceFeature[] sf = dsq.getSequenceFeatures();
180         if (sf != null)
181         {
182           int ist = sq.findIndex(sq.getStart());
183           int iend = sq.findIndex(sq.getEnd());
184           if (iend < alStart || ist > alw)
185           {
186             // sequence not in region
187             continue;
188           }
189           for (SequenceFeature sfpos : sf)
190           {
191             // future functionalty - featureType == null means mark columns
192             // containing all displayed features
193             if (sfpos != null && (featureType.equals(sfpos.getType())))
194             {
195               tfeat++;
196               // optimisation - could consider 'spos,apos' like cursor argument
197               // - findIndex wastes time by starting from first character and
198               // counting
199
200               int i = sq.findIndex(sfpos.getBegin());
201               int j = sq.findIndex(sfpos.getEnd());
202               if (j < alStart || i > alw)
203               {
204                 // feature is outside selected region
205                 continue;
206               }
207               if (i < alStart)
208               {
209                 i = alStart;
210               }
211               if (i < ist)
212               {
213                 i = ist;
214               }
215               if (j > alw)
216               {
217                 j = alw;
218               }
219               for (; i <= j; i++)
220               {
221                 bs.set(i - 1);
222               }
223             }
224           }
225         }
226
227         if (tfeat > 0)
228         {
229           nseq++;
230         }
231       }
232     }
233     ColumnSelection cs = viewport.getColumnSelection();
234     if (bs.cardinality() > 0 || invert)
235     {
236       if (cs == null)
237       {
238         cs = new ColumnSelection();
239       }
240       else
241       {
242         if (!extendCurrent)
243         {
244           cs.clear();
245         }
246       }
247       if (invert)
248       {
249         // invert only in the currently selected sequence region
250         for (int i = bs.nextClearBit(alStart), ibs = bs.nextSetBit(alStart); i >= alStart
251                 && i < (alw);)
252         {
253           if (ibs < 0 || i < ibs)
254           {
255             if (toggle && cs.contains(i))
256             {
257               cs.removeElement(i++);
258             }
259             else
260             {
261               cs.addElement(i++);
262             }
263           }
264           else
265           {
266             i = bs.nextClearBit(ibs);
267             ibs = bs.nextSetBit(i);
268           }
269         }
270       }
271       else
272       {
273         for (int i = bs.nextSetBit(alStart); i >= alStart; i = bs
274                 .nextSetBit(i + 1))
275         {
276           if (toggle && cs.contains(i))
277           {
278             cs.removeElement(i);
279           }
280           else
281           {
282             cs.addElement(i);
283           }
284         }
285       }
286       viewport.setColumnSelection(cs);
287       alignPanel.paintAlignment(true);
288       avcg.setStatus(MessageManager.formatMessage("label.view_controller_toggled_marked",
289                   new String[]{
290                                 (toggle ? MessageManager.getString("label.toggled") : MessageManager.getString("label.marked")),
291                                 (invert ? (Integer.valueOf((alw - alStart) - bs.cardinality()).toString()):(Integer.valueOf(bs.cardinality()).toString())),
292                                 featureType, Integer.valueOf(nseq).toString()
293                         }));
294       return true;
295     }
296     else
297     {
298       avcg.setStatus(MessageManager.formatMessage("label.no_feature_of_type_found", new String[]{featureType}));
299       if (!extendCurrent && cs != null)
300       {
301         cs.clear();
302         alignPanel.paintAlignment(true);
303       }
304       return false;
305     }
306   }
307
308   public static boolean filterAnnotations(Annotation[] annotations,
309           AnnotationFilterParameter filterParams, ColumnSelection cs)
310   {
311     cs.revealAllHiddenColumns();
312     cs.clear();
313     int count = 0;
314     do
315     {
316       if (annotations[count] != null)
317       {
318
319         boolean itemMatched = false;
320
321         if (filterParams.getThresholdType() == AnnotationFilterParameter.ThresholdType.ABOVE_THRESHOLD
322                 && annotations[count].value > filterParams
323                         .getThresholdValue())
324         {
325           itemMatched = true;
326         }
327         if (filterParams.getThresholdType() == AnnotationFilterParameter.ThresholdType.BELOW_THRESHOLD
328                 && annotations[count].value < filterParams
329                         .getThresholdValue())
330         {
331           itemMatched = true;
332         }
333
334         if (filterParams.isFilterAlphaHelix()
335                 && annotations[count].secondaryStructure == 'H')
336         {
337           itemMatched = true;
338         }
339
340         if (filterParams.isFilterBetaSheet()
341                 && annotations[count].secondaryStructure == 'E')
342         {
343           itemMatched = true;
344         }
345
346         if (filterParams.isFilterTurn()
347                 && annotations[count].secondaryStructure == 'S')
348         {
349           itemMatched = true;
350         }
351
352         String regexSearchString = filterParams.getRegexString();
353         if (regexSearchString != null
354                 && !filterParams.getRegexSearchFields().isEmpty())
355         {
356           List<SearchableAnnotationField> fields = filterParams
357                   .getRegexSearchFields();
358           try
359           {
360             if (fields.contains(SearchableAnnotationField.DISPLAY_STRING)
361                     && annotations[count].displayCharacter
362                             .matches(regexSearchString))
363             {
364               itemMatched = true;
365             }
366           } catch (java.util.regex.PatternSyntaxException pse)
367           {
368             if (annotations[count].displayCharacter
369                     .equals(regexSearchString))
370             {
371               itemMatched = true;
372             }
373           }
374           if (fields.contains(SearchableAnnotationField.DESCRIPTION)
375                   && annotations[count].description != null
376                   && annotations[count].description
377                           .matches(regexSearchString))
378           {
379             itemMatched = true;
380           }
381         }
382
383         if (itemMatched)
384         {
385           cs.addElement(count);
386         }
387       }
388       count++;
389     } while (count < annotations.length);
390     return false;
391   }
392
393   @Override
394   public void sortAlignmentByFeatureDensity(String[] typ)
395   {
396     sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
397   }
398
399   protected void sortBy(String[] typ, String methodText, final String method)
400   {
401     FeatureRenderer fr = alignPanel.getFeatureRenderer();
402     if (typ == null)
403     {
404       typ = fr==null ? null : fr.getDisplayedFeatureTypes();
405     }
406     String gps[] = null;
407     gps = fr==null ? null : fr.getDisplayedFeatureGroups();
408     if (typ != null)
409     {
410       ArrayList types = new ArrayList();
411       for (int i = 0; i < typ.length; i++)
412       {
413         if (typ[i] != null)
414         {
415           types.add(typ[i]);
416         }
417         typ = new String[types.size()];
418         types.toArray(typ);
419       }
420     }
421     if (gps != null)
422     {
423       ArrayList grps = new ArrayList();
424
425       for (int i = 0; i < gps.length; i++)
426       {
427         if (gps[i] != null)
428         {
429           grps.add(gps[i]);
430         }
431       }
432       gps = new String[grps.size()];
433       grps.toArray(gps);
434     }
435     AlignmentI al = viewport.getAlignment();
436
437     int start, stop;
438     SequenceGroup sg = viewport.getSelectionGroup();
439     if (sg != null)
440     {
441       start = sg.getStartRes();
442       stop = sg.getEndRes();
443     }
444     else
445     {
446       start = 0;
447       stop = al.getWidth();
448     }
449     SequenceI[] oldOrder = al.getSequencesArray();
450     AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
451     avcg.addHistoryItem(new OrderCommand(methodText, oldOrder, viewport
452             .getAlignment()));
453     alignPanel.paintAlignment(true);
454
455   }
456
457   @Override
458   public void sortAlignmentByFeatureScore(String[] typ)
459   {
460     sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
461   }
462 }