Merge branch 'develop' into features/JAL-250_hideredundantseqs
[jalview.git] / src / jalview / gui / RedundancyPanel.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.gui;
22
23 import jalview.analysis.AlignSeq;
24 import jalview.commands.CommandI;
25 import jalview.commands.EditCommand;
26 import jalview.commands.EditCommand.Action;
27 import jalview.datamodel.SequenceGroup;
28 import jalview.datamodel.SequenceI;
29 import jalview.jbgui.GSliderPanel;
30 import jalview.util.MessageManager;
31
32 import java.awt.Color;
33 import java.awt.event.ActionEvent;
34 import java.util.ArrayList;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Stack;
38 import java.util.Vector;
39
40 import javax.swing.JInternalFrame;
41 import javax.swing.JProgressBar;
42 import javax.swing.event.ChangeEvent;
43 import javax.swing.event.ChangeListener;
44 import javax.swing.event.InternalFrameAdapter;
45 import javax.swing.event.InternalFrameEvent;
46
47 /**
48  * DOCUMENT ME!
49  * 
50  * @author $author$
51  * @version $Revision$
52  */
53 public class RedundancyPanel extends GSliderPanel implements Runnable
54 {
55   AlignFrame af;
56
57   AlignmentPanel ap;
58
59   Stack<CommandI> historyList = new Stack<CommandI>();
60
61   // simpler than synching with alignFrame.
62
63   float[] redundancy;
64
65   SequenceI[] originalSequences;
66
67   JInternalFrame frame;
68
69   Vector redundantSeqs;
70
71   private SequenceI[] redreps;
72
73   /**
74    * Creates a new RedundancyPanel object.
75    * 
76    * @param ap
77    *          DOCUMENT ME!
78    * @param af
79    *          DOCUMENT ME!
80    */
81   public RedundancyPanel(final AlignmentPanel ap, AlignFrame af)
82   {
83     this.ap = ap;
84     this.af = af;
85     redundantSeqs = new Vector();
86
87     slider.addChangeListener(new ChangeListener()
88     {
89       @Override
90       public void stateChanged(ChangeEvent evt)
91       {
92         valueField.setText(slider.getValue() + "");
93         sliderValueChanged();
94       }
95     });
96
97     applyButton.setText(MessageManager.getString("action.hide"));
98     allGroupsCheck.setVisible(false);
99     slider.setMinimum(0);
100     slider.setMaximum(100);
101     slider.setValue(100);
102
103     Thread worker = new Thread(this);
104     worker.start();
105
106     frame = new JInternalFrame();
107     frame.setContentPane(this);
108     Desktop.addInternalFrame(frame,
109             MessageManager
110                     .getString("label.redundancy_threshold_selection"),
111             400, 100, false);
112     frame.addInternalFrameListener(new InternalFrameAdapter()
113     {
114       @Override
115       public void internalFrameClosing(InternalFrameEvent evt)
116       {
117         ap.getIdPanel().getIdCanvas().setHighlighted(null);
118       }
119     });
120
121   }
122
123   /**
124    * This is a copy of remove redundancy in jalivew.datamodel.Alignment except
125    * we dont want to remove redundancy, just calculate once so we can use the
126    * slider to dynamically hide redundant sequences
127    * 
128    * @param threshold
129    *          DOCUMENT ME!
130    * @param sel
131    *          DOCUMENT ME!
132    * 
133    * @return DOCUMENT ME!
134    */
135   @Override
136   public void run()
137   {
138     JProgressBar progress = new JProgressBar();
139     progress.setIndeterminate(true);
140     southPanel.add(progress, java.awt.BorderLayout.SOUTH);
141
142     label.setText(MessageManager.getString("label.calculating"));
143
144     slider.setVisible(false);
145     applyButton.setEnabled(false);
146     valueField.setVisible(false);
147
148     validate();
149
150     String[] omitHidden = null;
151
152     SequenceGroup sg = ap.av.getSelectionGroup();
153     int height;
154
155     int start, end;
156
157     if ((sg != null) && (sg.getSize() >= 1))
158     {
159       originalSequences = sg.getSequencesInOrder(ap.av.getAlignment());
160       start = sg.getStartRes();
161       end = sg.getEndRes();
162     }
163     else
164     {
165       originalSequences = ap.av.getAlignment().getSequencesArray();
166       start = 0;
167       end = ap.av.getAlignment().getWidth();
168     }
169
170     height = originalSequences.length;
171     if (ap.av.hasHiddenColumns())
172     {
173       omitHidden = ap.av.getViewAsString(sg != null);
174     }
175     Object rr[] = AlignSeq.computeRedundancyMatrixWithRep(
176             originalSequences,
177             omitHidden, start, end, false);
178
179     redundancy = (float[]) rr[0];
180     redreps = (SequenceI[]) rr[1];
181
182     progress.setIndeterminate(false);
183     progress.setVisible(false);
184     progress = null;
185
186     label.setText(
187             MessageManager.getString("label.enter_redundancy_threshold"));
188     slider.setVisible(true);
189     applyButton.setEnabled(true);
190     valueField.setVisible(true);
191
192     validate();
193     sliderValueChanged();
194     // System.out.println((System.currentTimeMillis()-start));
195   }
196
197   void sliderValueChanged()
198   {
199     if (redundancy == null)
200     {
201       return;
202     }
203
204     float value = slider.getValue();
205     List<SequenceI> redundantSequences = new ArrayList<SequenceI>();
206     for (int i = 0; i < redundancy.length; i++)
207     {
208       if (value <= redundancy[i])
209       {
210         redundantSequences.add(originalSequences[i]);
211       }
212     }
213     ap.getIdPanel().getIdCanvas().setHighlighted(redundantSequences);
214   }
215
216   /**
217    * DOCUMENT ME!
218    * 
219    * @param e
220    *          DOCUMENT ME!
221    */
222   @Override
223   public void applyButton_actionPerformed(ActionEvent e)
224   {
225
226     undoButton.setEnabled(true);
227
228     float value = slider.getValue();
229     SequenceGroup sg = ap.av.getSelectionGroup();
230     // delete_seqs(value, sg);
231     hide_seqs(value, sg);
232   }
233   private void hide_seqs(float value, SequenceGroup sg)
234   {
235     /**
236      * hash to look up the representative for a sequence
237      */
238     HashMap<SequenceI, SequenceI> rep = new HashMap<SequenceI, SequenceI>();
239     /**
240      * hash to collect lists of sequences represented by a sequence
241      */
242     HashMap<SequenceI, SequenceGroup> reps = new HashMap<SequenceI, SequenceGroup>();
243     for (int i = 0; i < redundancy.length; i++)
244     {
245       if (value <= redundancy[i])
246       {
247         // does this sequence represent other sequences ?
248         SequenceGroup repset;
249         // is the representative also redundant ?
250         SequenceI repForI = rep.get(redreps[i]);
251         if (repForI==null) {
252           // the representative is still in the alignment. 
253           // is it representing anything already ? 
254           repset = reps.get(redreps[i]);
255           if (repset==null)
256           {
257             repset = new SequenceGroup();
258           }
259           repset.addSequence(originalSequences[i], false);
260           rep.put(originalSequences[i], redreps[i]);
261           reps.put(redreps[i], repset);
262           // and save the representative sequence for originalSeq
263           repForI = redreps[i];
264         } else {
265           // already hidden the representative for this sequence, so look up its redundant peers
266           repset = reps.get(repForI);
267           if (repset==null)
268           {
269             throw new Error("Implementation failure for redundancy set creation");
270           }
271           // add the sequence to the peerset, and mark sequence's representative in hash
272           repset.addSequence(originalSequences[i], false);
273           rep.put(originalSequences[i], repForI);
274         }
275         // merge any sequences represented by this with its new containing group
276         SequenceGroup existingreps = reps.remove(originalSequences[i]);
277         if (existingreps!=null)
278         {
279           for (SequenceI sq:existingreps.getSequences())
280           {
281             rep.put(sq, repForI);
282             repset.addSequence(sq, false);
283           }
284         }
285       }
286     }
287     int s = 0, e = ap.av.getAlignment().getWidth();
288     if (sg != null)
289     {
290       s = sg.getStartRes();
291       e = sg.getEndRes();
292     }
293     List<SequenceGroup> sgs = new ArrayList<SequenceGroup>();
294     for (SequenceI repseq: reps.keySet())
295     {
296       sg = reps.get(repseq);
297       sg.addSequence(repseq, false);
298       sg.setSeqrep(repseq);
299       sg.setStartRes(s);
300       sg.setEndRes(e);
301       sgs.add(sg);
302     }
303     ap.alignFrame.avc.showRandomColoursForGroups(sgs);
304     for (SequenceI repseq : reps.keySet())
305     {
306       sg = reps.get(repseq);
307       ap.av.hideRepSequences(repseq, sg);
308     }
309   }
310
311   private void delete_seqs(float value, SequenceGroup sg)
312   {
313     ArrayList<SequenceI> del = new ArrayList<SequenceI>();
314
315     for (int i = 0; i < redundancy.length; i++)
316     {
317       if (value <= redundancy[i])
318       {
319         del.add(originalSequences[i]);
320       }
321     }
322     // This has to be done before the restoreHistoryItem method of alignFrame
323     // will
324     // actually restore these sequences.
325     if (del.size() > 0)
326     {
327       SequenceI[] deleted = new SequenceI[del.size()];
328
329       int width = 0;
330       for (int i = 0; i < del.size(); i++)
331       {
332         deleted[i] = del.get(i);
333         if (deleted[i].getLength() > width)
334         {
335           width = deleted[i].getLength();
336         }
337       }
338
339       EditCommand cut = new EditCommand(
340               MessageManager.getString("action.remove_redundancy"),
341               Action.CUT, deleted, 0, width, ap.av.getAlignment());
342
343       for (int i = 0; i < del.size(); i++)
344       {
345         ap.av.getAlignment().deleteSequence(deleted[i]);
346         if (sg != null)
347         {
348           sg.deleteSequence(deleted[i], false);
349         }
350       }
351
352       historyList.push(cut);
353
354       ap.alignFrame.addHistoryItem(cut);
355
356       PaintRefresher.Refresh(this, ap.av.getSequenceSetId(), true, true);
357       ap.av.firePropertyChange("alignment", null,
358               ap.av.getAlignment().getSequences());
359     }
360
361   }
362
363   /**
364    * DOCUMENT ME!
365    * 
366    * @param e
367    *          DOCUMENT ME!
368    */
369   @Override
370   public void undoButton_actionPerformed(ActionEvent e)
371   {
372     if (historyList == null || historyList.isEmpty())
373     {
374       undoButton.setEnabled(false);
375       return;
376     }
377
378     CommandI command = historyList.pop();
379     if (ap.av.getHistoryList().contains(command))
380     {
381       command.undoCommand(af.getViewAlignments());
382       ap.av.getHistoryList().remove(command);
383       ap.av.firePropertyChange("alignment", null,
384               ap.av.getAlignment().getSequences());
385       af.updateEditMenuBar();
386     }
387
388     ap.paintAlignment(true);
389
390     if (historyList.size() == 0)
391     {
392       undoButton.setEnabled(false);
393     }
394   }
395
396   /**
397    * DOCUMENT ME!
398    * 
399    * @param e
400    *          DOCUMENT ME!
401    */
402   @Override
403   public void valueField_actionPerformed(ActionEvent e)
404   {
405     try
406     {
407       int i = Integer.parseInt(valueField.getText());
408       slider.setValue(i);
409     } catch (Exception ex)
410     {
411       valueField.setText(slider.getValue() + "");
412     }
413   }
414
415 }