JAL-250 create random coloured groups for representatives
[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, MessageManager
109             .getString("label.redundancy_threshold_selection"), 400, 100,
110             false);
111     frame.addInternalFrameListener(new InternalFrameAdapter()
112     {
113       @Override
114       public void internalFrameClosing(InternalFrameEvent evt)
115       {
116         ap.getIdPanel().getIdCanvas().setHighlighted(null);
117       }
118     });
119
120   }
121
122   /**
123    * This is a copy of remove redundancy in jalivew.datamodel.Alignment except
124    * we dont want to remove redundancy, just calculate once so we can use the
125    * slider to dynamically hide redundant sequences
126    * 
127    * @param threshold
128    *          DOCUMENT ME!
129    * @param sel
130    *          DOCUMENT ME!
131    * 
132    * @return DOCUMENT ME!
133    */
134   @Override
135   public void run()
136   {
137     JProgressBar progress = new JProgressBar();
138     progress.setIndeterminate(true);
139     southPanel.add(progress, java.awt.BorderLayout.SOUTH);
140
141     label.setText(MessageManager.getString("label.calculating"));
142
143     slider.setVisible(false);
144     applyButton.setEnabled(false);
145     valueField.setVisible(false);
146
147     validate();
148
149     String[] omitHidden = null;
150
151     SequenceGroup sg = ap.av.getSelectionGroup();
152     int height;
153
154     int start, end;
155
156     if ((sg != null) && (sg.getSize() >= 1))
157     {
158       originalSequences = sg.getSequencesInOrder(ap.av.getAlignment());
159       start = sg.getStartRes();
160       end = sg.getEndRes();
161     }
162     else
163     {
164       originalSequences = ap.av.getAlignment().getSequencesArray();
165       start = 0;
166       end = ap.av.getAlignment().getWidth();
167     }
168
169     height = originalSequences.length;
170     if (ap.av.hasHiddenColumns())
171     {
172       omitHidden = ap.av.getViewAsString(sg != null);
173     }
174     Object rr[] = AlignSeq.computeRedundancyMatrixWithRep(
175             originalSequences,
176             omitHidden, start, end, false);
177
178     redundancy = (float[]) rr[0];
179     redreps = (SequenceI[]) rr[1];
180
181     progress.setIndeterminate(false);
182     progress.setVisible(false);
183     progress = null;
184
185     label.setText(MessageManager
186             .getString("label.enter_redundancy_thereshold"));
187     slider.setVisible(true);
188     applyButton.setEnabled(true);
189     valueField.setVisible(true);
190
191     validate();
192     sliderValueChanged();
193     // System.out.println((System.currentTimeMillis()-start));
194   }
195
196   void sliderValueChanged()
197   {
198     if (redundancy == null)
199     {
200       return;
201     }
202
203     float value = slider.getValue();
204     List<SequenceI> redundantSequences = new ArrayList<SequenceI>();
205     for (int i = 0; i < redundancy.length; i++)
206     {
207       if (value <= redundancy[i])
208       {
209         redundantSequences.add(originalSequences[i]);
210       }
211     }
212     ap.getIdPanel().getIdCanvas().setHighlighted(redundantSequences);
213   }
214
215   /**
216    * DOCUMENT ME!
217    * 
218    * @param e
219    *          DOCUMENT ME!
220    */
221   @Override
222   public void applyButton_actionPerformed(ActionEvent e)
223   {
224
225     undoButton.setEnabled(true);
226
227     float value = slider.getValue();
228     SequenceGroup sg = ap.av.getSelectionGroup();
229     // delete_seqs(value, sg);
230     hide_seqs(value, sg);
231   }
232   private void hide_seqs(float value, SequenceGroup sg)
233   {
234     /**
235      * hash to look up the representative for a sequence
236      */
237     HashMap<SequenceI, SequenceI> rep = new HashMap<SequenceI, SequenceI>();
238     /**
239      * hash to collect lists of sequences represented by a sequence
240      */
241     HashMap<SequenceI, SequenceGroup> reps = new HashMap<SequenceI, SequenceGroup>();
242     for (int i = 0; i < redundancy.length; i++)
243     {
244       if (value <= redundancy[i])
245       {
246         // does this sequence represent other sequences ?
247         SequenceGroup repset;
248         // is the representative also redundant ?
249         SequenceI repForI = rep.get(redreps[i]);
250         if (repForI==null) {
251           // the representative is still in the alignment. 
252           // is it representing anything already ? 
253           repset = reps.get(redreps[i]);
254           if (repset==null)
255           {
256             repset = new SequenceGroup();
257           }
258           repset.addSequence(originalSequences[i], false);
259           rep.put(originalSequences[i], redreps[i]);
260           reps.put(redreps[i], repset);
261           // and save the representative sequence for originalSeq
262           repForI = redreps[i];
263         } else {
264           // already hidden the representative for this sequence, so look up its redundant peers
265           repset = reps.get(repForI);
266           if (repset==null)
267           {
268             throw new Error("Implementation failure for redundancy set creation");
269           }
270           // add the sequence to the peerset, and mark sequence's representative in hash
271           repset.addSequence(originalSequences[i], false);
272           rep.put(originalSequences[i], repForI);
273         }
274         // merge any sequences represented by this with its new containing group
275         SequenceGroup existingreps = reps.remove(originalSequences[i]);
276         if (existingreps!=null)
277         {
278           for (SequenceI sq:existingreps.getSequences())
279           {
280             rep.put(sq, repForI);
281             repset.addSequence(sq, false);
282           }
283         }
284       }
285     }
286     int s = 0, e = ap.av.getAlignment().getWidth();
287     if (sg != null)
288     {
289       s = sg.getStartRes();
290       e = sg.getEndRes();
291     }
292     List<SequenceGroup> sgs = new ArrayList<SequenceGroup>();
293     for (SequenceI repseq: reps.keySet())
294     {
295       sg = reps.get(repseq);
296       sg.addSequence(repseq, false);
297       sg.setSeqrep(repseq);
298       sg.setStartRes(s);
299       sg.setEndRes(e);
300       sgs.add(sg);
301     }
302     ap.alignFrame.avc.showRandomColoursForGroups(sgs);
303     for (SequenceI repseq : reps.keySet())
304     {
305       sg = reps.get(repseq);
306       ap.av.hideRepSequences(repseq, sg);
307     }
308   }
309
310   private void delete_seqs(float value, SequenceGroup sg)
311   {
312     ArrayList<SequenceI> del = new ArrayList<SequenceI>();
313
314     for (int i = 0; i < redundancy.length; i++)
315     {
316       if (value <= redundancy[i])
317       {
318         del.add(originalSequences[i]);
319       }
320     }
321     // This has to be done before the restoreHistoryItem method of alignFrame
322     // will
323     // actually restore these sequences.
324     if (del.size() > 0)
325     {
326       SequenceI[] deleted = new SequenceI[del.size()];
327
328       int width = 0;
329       for (int i = 0; i < del.size(); i++)
330       {
331         deleted[i] = del.get(i);
332         if (deleted[i].getLength() > width)
333         {
334           width = deleted[i].getLength();
335         }
336       }
337
338       EditCommand cut = new EditCommand(
339               MessageManager.getString("action.remove_redundancy"),
340               Action.CUT, deleted, 0, width, ap.av.getAlignment());
341
342       for (int i = 0; i < del.size(); i++)
343       {
344         ap.av.getAlignment().deleteSequence(deleted[i]);
345         if (sg != null)
346         {
347           sg.deleteSequence(deleted[i], false);
348         }
349       }
350
351       historyList.push(cut);
352
353       ap.alignFrame.addHistoryItem(cut);
354
355       PaintRefresher.Refresh(this, ap.av.getSequenceSetId(), true, true);
356       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
357               .getSequences());
358     }
359
360   }
361
362   /**
363    * DOCUMENT ME!
364    * 
365    * @param e
366    *          DOCUMENT ME!
367    */
368   @Override
369   public void undoButton_actionPerformed(ActionEvent e)
370   {
371     if (historyList == null || historyList.isEmpty())
372     {
373       undoButton.setEnabled(false);
374       return;
375     }
376
377     CommandI command = historyList.pop();
378     if (ap.av.getHistoryList().contains(command))
379     {
380       command.undoCommand(af.getViewAlignments());
381       ap.av.getHistoryList().remove(command);
382       ap.av.firePropertyChange("alignment", null, ap.av.getAlignment()
383               .getSequences());
384       af.updateEditMenuBar();
385     }
386
387     ap.paintAlignment(true);
388
389     if (historyList.size() == 0)
390     {
391       undoButton.setEnabled(false);
392     }
393   }
394
395   /**
396    * DOCUMENT ME!
397    * 
398    * @param e
399    *          DOCUMENT ME!
400    */
401   @Override
402   public void valueField_actionPerformed(ActionEvent e)
403   {
404     try
405     {
406       int i = Integer.parseInt(valueField.getText());
407       slider.setValue(i);
408     } catch (Exception ex)
409     {
410       valueField.setText(slider.getValue() + "");
411     }
412   }
413
414 }