experimental buttons for sort on score/density and popup menu to sort by score/densit...
[jalview.git] / src / jalview / gui / FeatureSettings.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.io.*;
22 import java.util.*;
23
24 import java.awt.*;
25 import java.awt.event.*;
26 import java.beans.PropertyChangeEvent;
27 import java.beans.PropertyChangeListener;
28
29 import javax.swing.*;
30 import javax.swing.event.*;
31 import javax.swing.table.*;
32
33 import jalview.analysis.AlignmentSorter;
34 import jalview.commands.OrderCommand;
35 import jalview.datamodel.*;
36 import jalview.io.*;
37
38 public class FeatureSettings extends JPanel
39 {
40   DasSourceBrowser dassourceBrowser;
41
42   jalview.ws.DasSequenceFeatureFetcher dasFeatureFetcher;
43
44   JPanel settingsPane = new JPanel();
45
46   JPanel dasSettingsPane = new JPanel();
47
48   final FeatureRenderer fr;
49
50   public final AlignFrame af;
51
52   Object[][] originalData;
53
54   final JInternalFrame frame;
55
56   JScrollPane scrollPane = new JScrollPane();
57
58   JTable table;
59
60   JPanel groupPanel;
61
62   JSlider transparency = new JSlider();
63
64   JPanel transPanel = new JPanel(new FlowLayout());
65
66   public FeatureSettings(AlignFrame af)
67   {
68     this.af = af;
69     fr = af.getFeatureRenderer();
70
71     transparency.setMaximum(100 - (int) (fr.transparency * 100));
72
73     try
74     {
75       jbInit();
76     } catch (Exception ex)
77     {
78       ex.printStackTrace();
79     }
80
81     table = new JTable();
82     table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
83     table.setFont(new Font("Verdana", Font.PLAIN, 12));
84     table.setDefaultRenderer(Color.class, new ColorRenderer());
85
86     table.setDefaultEditor(Color.class, new ColorEditor());
87
88     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
89
90     table.addMouseListener(new MouseAdapter()
91     {
92       public void mousePressed(MouseEvent evt)
93       {
94         selectedRow = table.rowAtPoint(evt.getPoint());
95         if (javax.swing.SwingUtilities.isRightMouseButton(evt))
96         {
97           popupSort((String)table.getValueAt(selectedRow, 0), evt.getX(), evt.getY());
98         }
99       }
100     });
101
102     table.addMouseMotionListener(new MouseMotionAdapter()
103     {
104       public void mouseDragged(MouseEvent evt)
105       {
106         int newRow = table.rowAtPoint(evt.getPoint());
107         if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
108         {
109           Object[] temp = new Object[3];
110           temp[0] = table.getValueAt(selectedRow, 0);
111           temp[1] = table.getValueAt(selectedRow, 1);
112           temp[2] = table.getValueAt(selectedRow, 2);
113
114           table.setValueAt(table.getValueAt(newRow, 0), selectedRow, 0);
115           table.setValueAt(table.getValueAt(newRow, 1), selectedRow, 1);
116           table.setValueAt(table.getValueAt(newRow, 2), selectedRow, 2);
117
118           table.setValueAt(temp[0], newRow, 0);
119           table.setValueAt(temp[1], newRow, 1);
120           table.setValueAt(temp[2], newRow, 2);
121
122           selectedRow = newRow;
123         }
124       }
125     });
126
127     scrollPane.setViewportView(table);
128
129     dassourceBrowser = new DasSourceBrowser();
130     dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
131
132     if (af.getViewport().featuresDisplayed == null
133             || fr.renderOrder == null)
134     {
135       fr.findAllFeatures(true); // display everything!
136     }
137
138     setTableData();
139     final PropertyChangeListener change;
140     final FeatureSettings fs = this;
141     fr.addPropertyChangeListener(change = new PropertyChangeListener()
142     {
143       public void propertyChange(PropertyChangeEvent evt)
144       {
145         if (!fs.resettingTable && !fs.handlingUpdate)
146         {
147           fs.handlingUpdate = true;
148           fs.resetTable(null); // new groups may be added with new seuqence
149                                 // feature types only
150           fs.handlingUpdate = false;
151         }
152       }
153
154     });
155
156     frame = new JInternalFrame();
157     frame.setContentPane(this);
158     Desktop.addInternalFrame(frame, "Sequence Feature Settings", 400, 450);
159     frame
160             .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
161             {
162               public void internalFrameClosed(
163                       javax.swing.event.InternalFrameEvent evt)
164               {
165                 fr.removePropertyChangeListener(change);
166               };
167             });
168     frame.setLayer(JLayeredPane.PALETTE_LAYER);
169   }
170
171   protected void popupSort(final String type, int x, int y)
172   {
173     JPopupMenu men = new JPopupMenu();
174     men.setName("Sort by "+type);
175     JMenuItem scr = new JMenuItem("Score");
176     men.add(scr);
177     final FeatureSettings me=this;
178     scr.addActionListener(new ActionListener() {
179
180       public void actionPerformed(ActionEvent e)
181       {
182         me.sortByScore(new String[] {type});
183       }
184       
185     });
186     JMenuItem dens = new JMenuItem("Density");
187     dens.addActionListener(new ActionListener() {
188
189       public void actionPerformed(ActionEvent e)
190       {
191         me.sortByDens(new String[] { type });
192       }
193       
194     });
195     men.add(dens);
196     men.show(table, x, y);
197   }
198
199   /**
200    * true when Feature Settings are updating from feature renderer
201    */
202   private boolean handlingUpdate = false;
203
204   /**
205    * contains a float[3] for each feature type string. created by setTableData
206    */
207   Hashtable typeWidth = null;
208
209   synchronized public void setTableData()
210   {
211     if (fr.featureGroups == null)
212     {
213       fr.featureGroups = new Hashtable();
214     }
215     Vector allFeatures = new Vector();
216     Vector allGroups = new Vector();
217     SequenceFeature[] tmpfeatures;
218     String group;
219     for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
220     {
221       if (af.getViewport().alignment.getSequenceAt(i).getDatasetSequence()
222               .getSequenceFeatures() == null)
223       {
224         continue;
225       }
226
227       tmpfeatures = af.getViewport().alignment.getSequenceAt(i)
228               .getDatasetSequence().getSequenceFeatures();
229
230       int index = 0;
231       while (index < tmpfeatures.length)
232       {
233         if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
234         {
235           index++;
236           continue;
237         }
238
239         if (tmpfeatures[index].getFeatureGroup() != null)
240         {
241           group = tmpfeatures[index].featureGroup;
242           if (!allGroups.contains(group))
243           {
244             allGroups.addElement(group);
245             if (group != null)
246             {
247               checkGroupState(group);
248             }
249           }
250         }
251
252         if (!allFeatures.contains(tmpfeatures[index].getType()))
253         {
254           allFeatures.addElement(tmpfeatures[index].getType());
255         }
256         index++;
257       }
258     }
259
260     resetTable(null);
261
262     validate();
263   }
264
265   /**
266    * 
267    * @param group
268    * @return true if group has been seen before and is already added to set.
269    */
270   private boolean checkGroupState(String group)
271   {
272     boolean visible;
273     if (fr.featureGroups.containsKey(group))
274     {
275       visible = ((Boolean) fr.featureGroups.get(group)).booleanValue();
276     }
277     else
278     {
279       visible = true; // new group is always made visible
280     }
281
282     if (groupPanel == null)
283     {
284       groupPanel = new JPanel();
285     }
286
287     boolean alreadyAdded = false;
288     for (int g = 0; g < groupPanel.getComponentCount(); g++)
289     {
290       if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
291       {
292         alreadyAdded = true;
293         ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
294         break;
295       }
296     }
297
298     if (alreadyAdded)
299     {
300
301       return true;
302     }
303
304     fr.featureGroups.put(group, new Boolean(visible));
305     final String grp = group;
306     final JCheckBox check = new JCheckBox(group, visible);
307     check.setFont(new Font("Serif", Font.BOLD, 12));
308     check.addItemListener(new ItemListener()
309     {
310       public void itemStateChanged(ItemEvent evt)
311       {
312         fr.featureGroups.put(check.getText(), new Boolean(check
313                 .isSelected()));
314         af.alignPanel.seqPanel.seqCanvas.repaint();
315         if (af.alignPanel.overviewPanel != null)
316         {
317           af.alignPanel.overviewPanel.updateOverviewImage();
318         }
319
320         resetTable(new String[]
321         { grp });
322       }
323     });
324     groupPanel.add(check);
325     return false;
326   }
327
328   boolean resettingTable = false;
329
330   synchronized void resetTable(String[] groupChanged)
331   {
332     if (resettingTable == true)
333     {
334       return;
335     }
336     resettingTable = true;
337     typeWidth = new Hashtable();
338     // TODO: change avWidth calculation to 'per-sequence' average and use long
339     // rather than float
340     float[] avWidth = null;
341     SequenceFeature[] tmpfeatures;
342     String group = null, type;
343     Vector visibleChecks = new Vector();
344
345     // Find out which features should be visible depending on which groups
346     // are selected / deselected
347     // and recompute average width ordering
348     for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
349     {
350
351       tmpfeatures = af.getViewport().alignment.getSequenceAt(i)
352               .getDatasetSequence().getSequenceFeatures();
353       if (tmpfeatures == null)
354       {
355         continue;
356       }
357
358       int index = 0;
359       while (index < tmpfeatures.length)
360       {
361         group = tmpfeatures[index].featureGroup;
362
363         if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
364         {
365           index++;
366           continue;
367         }
368
369         if (group == null || fr.featureGroups.get(group) == null
370                 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
371         {
372           if (group != null)
373             checkGroupState(group);
374           type = tmpfeatures[index].getType();
375           if (!visibleChecks.contains(type))
376           {
377             visibleChecks.addElement(type);
378           }
379         }
380         if (!typeWidth.containsKey(tmpfeatures[index].getType()))
381         {
382           typeWidth.put(tmpfeatures[index].getType(),
383                   avWidth = new float[3]);
384         }
385         else
386         {
387           avWidth = (float[]) typeWidth.get(tmpfeatures[index].getType());
388         }
389         avWidth[0]++;
390         if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
391         {
392           avWidth[1] += 1 + tmpfeatures[index].getBegin()
393                   - tmpfeatures[index].getEnd();
394         }
395         else
396         {
397           avWidth[1] += 1 + tmpfeatures[index].getEnd()
398                   - tmpfeatures[index].getBegin();
399         }
400         index++;
401       }
402     }
403
404     int fSize = visibleChecks.size();
405     Object[][] data = new Object[fSize][3];
406     int dataIndex = 0;
407
408     if (fr.renderOrder != null)
409     {
410       if (!handlingUpdate)
411         fr.findAllFeatures(groupChanged != null); // prod to update
412                                                   // colourschemes. but don't
413                                                   // affect display
414       // First add the checks in the previous render order,
415       // in case the window has been closed and reopened
416       for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
417       {
418         type = fr.renderOrder[ro];
419
420         if (!visibleChecks.contains(type))
421         {
422           continue;
423         }
424
425         data[dataIndex][0] = type;
426         data[dataIndex][1] = fr.getColour(type);
427         data[dataIndex][2] = new Boolean(af.getViewport().featuresDisplayed
428                 .containsKey(type));
429         dataIndex++;
430         visibleChecks.removeElement(type);
431       }
432     }
433
434     fSize = visibleChecks.size();
435     for (int i = 0; i < fSize; i++)
436     {
437       // These must be extra features belonging to the group
438       // which was just selected
439       type = visibleChecks.elementAt(i).toString();
440       data[dataIndex][0] = type;
441
442       data[dataIndex][1] = fr.getColour(type);
443       if (data[dataIndex][1] == null)
444       {
445         // "Colour has been updated in another view!!"
446         fr.renderOrder = null;
447         return;
448       }
449
450       data[dataIndex][2] = new Boolean(true);
451       dataIndex++;
452     }
453
454     if (originalData == null)
455     {
456       originalData = new Object[data.length][3];
457       System.arraycopy(data, 0, originalData, 0, data.length);
458     }
459
460     table.setModel(new FeatureTableModel(data));
461     table.getColumnModel().getColumn(0).setPreferredWidth(200);
462
463     if (groupPanel != null)
464     {
465       groupPanel.setLayout(new GridLayout(fr.featureGroups.size() / 4 + 1,
466               4));
467
468       groupPanel.validate();
469       bigPanel.add(groupPanel, BorderLayout.NORTH);
470     }
471
472     updateFeatureRenderer(data, groupChanged != null);
473     resettingTable = false;
474   }
475
476   /**
477    * reorder data based on the featureRenderers global priority list.
478    * 
479    * @param data
480    */
481   private void ensureOrder(Object[][] data)
482   {
483     boolean sort = false;
484     float[] order = new float[data.length];
485     for (int i = 0; i < order.length; i++)
486     {
487       order[i] = fr.getOrder(data[i][0].toString());
488       if (order[i] < 0)
489         order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
490       if (i > 1)
491         sort = sort || order[i - 1] > order[i];
492     }
493     if (sort)
494       jalview.util.QuickSort.sort(order, data);
495   }
496
497   void load()
498   {
499     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
500             .getProperty("LAST_DIRECTORY"), new String[]
501     { "fc" }, new String[]
502     { "Sequence Feature Colours" }, "Sequence Feature Colours");
503     chooser.setFileView(new jalview.io.JalviewFileView());
504     chooser.setDialogTitle("Load Feature Colours");
505     chooser.setToolTipText("Load");
506
507     int value = chooser.showOpenDialog(this);
508
509     if (value == JalviewFileChooser.APPROVE_OPTION)
510     {
511       File file = chooser.getSelectedFile();
512
513       try
514       {
515         InputStreamReader in = new InputStreamReader(new FileInputStream(
516                 file), "UTF-8");
517
518         jalview.binding.JalviewUserColours jucs = new jalview.binding.JalviewUserColours();
519         jucs = (jalview.binding.JalviewUserColours) jucs.unmarshal(in);
520
521         for (int i = jucs.getColourCount() - 1; i >= 0; i--)
522         {
523           String name;
524           fr.setColour(name = jucs.getColour(i).getName(), new Color(
525                   Integer.parseInt(jucs.getColour(i).getRGB(), 16)));
526           fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
527         }
528         if (table != null)
529         {
530           resetTable(null);
531           Object[][] data = ((FeatureTableModel) table.getModel())
532                   .getData();
533           ensureOrder(data);
534           updateFeatureRenderer(data, false);
535           table.repaint();
536         }
537       } catch (Exception ex)
538       {
539         System.out.println("Error loading User Colour File\n" + ex);
540       }
541     }
542   }
543
544   void save()
545   {
546     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
547             .getProperty("LAST_DIRECTORY"), new String[]
548     { "fc" }, new String[]
549     { "Sequence Feature Colours" }, "Sequence Feature Colours");
550     chooser.setFileView(new jalview.io.JalviewFileView());
551     chooser.setDialogTitle("Save Feature Colour Scheme");
552     chooser.setToolTipText("Save");
553
554     int value = chooser.showSaveDialog(this);
555
556     if (value == JalviewFileChooser.APPROVE_OPTION)
557     {
558       String choice = chooser.getSelectedFile().getPath();
559       jalview.binding.JalviewUserColours ucs = new jalview.binding.JalviewUserColours();
560       ucs.setSchemeName("Sequence Features");
561       try
562       {
563         PrintWriter out = new PrintWriter(new OutputStreamWriter(
564                 new FileOutputStream(choice), "UTF-8"));
565
566         Enumeration e = fr.featureColours.keys();
567         float[] sortOrder = new float[fr.featureColours.size()];
568         String[] sortTypes = new String[fr.featureColours.size()];
569         int i = 0;
570         while (e.hasMoreElements())
571         {
572           sortTypes[i] = e.nextElement().toString();
573           sortOrder[i] = fr.getOrder(sortTypes[i]);
574           i++;
575         }
576         jalview.util.QuickSort.sort(sortOrder, sortTypes);
577         sortOrder = null;
578         for (i = 0; i < sortTypes.length; i++)
579         {
580           jalview.binding.Colour col = new jalview.binding.Colour();
581           col.setName(sortTypes[i]);
582           col.setRGB(jalview.util.Format.getHexString(fr.getColour(col
583                   .getName())));
584           ucs.addColour(col);
585         }
586         ucs.marshal(out);
587         out.close();
588       } catch (Exception ex)
589       {
590         ex.printStackTrace();
591       }
592     }
593   }
594
595   public void invertSelection()
596   {
597     for (int i = 0; i < table.getRowCount(); i++)
598     {
599       Boolean value = (Boolean) table.getValueAt(i, 2);
600
601       table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
602     }
603   }
604
605   public void orderByAvWidth()
606   {
607     if (table == null || table.getModel() == null)
608       return;
609     Object[][] data = ((FeatureTableModel) table.getModel()).getData();
610     float[] width = new float[data.length];
611     float[] awidth;
612     float max = 0;
613     int num = 0;
614     for (int i = 0; i < data.length; i++)
615     {
616       awidth = (float[]) typeWidth.get(data[i][0]);
617       if (awidth[0] > 0)
618       {
619         width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
620                                           // weight - but have to make per
621                                           // sequence, too (awidth[2])
622         // if (width[i]==1) // hack to distinguish single width sequences.
623         num++;
624       }
625       else
626       {
627         width[i] = 0;
628       }
629       if (max < width[i])
630         max = width[i];
631     }
632     boolean sort = false;
633     for (int i = 0; i < width.length; i++)
634     {
635       // awidth = (float[]) typeWidth.get(data[i][0]);
636       if (width[i] == 0)
637       {
638         width[i] = fr.getOrder(data[i][0].toString());
639         if (width[i] < 0)
640         {
641           width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
642         }
643       }
644       else
645       {
646         width[i] /= max; // normalize
647         fr.setOrder(data[i][0].toString(), width[i]); // store for later
648       }
649       if (i > 0)
650         sort = sort || width[i - 1] > width[i];
651     }
652     if (sort)
653       jalview.util.QuickSort.sort(width, data);
654     // update global priority order
655
656     updateFeatureRenderer(data, false);
657     table.repaint();
658   }
659
660   public void close()
661   {
662     try
663     {
664       frame.setClosed(true);
665     } catch (Exception exe)
666     {
667     }
668
669   }
670
671   public void updateFeatureRenderer(Object[][] data)
672   {
673     updateFeatureRenderer(data, true);
674   }
675
676   private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
677   {
678     fr.setFeaturePriority(data, visibleNew);
679     af.alignPanel.paintAlignment(true);
680   }
681
682   int selectedRow = -1;
683
684   JTabbedPane tabbedPane = new JTabbedPane();
685
686   BorderLayout borderLayout1 = new BorderLayout();
687
688   BorderLayout borderLayout2 = new BorderLayout();
689
690   BorderLayout borderLayout3 = new BorderLayout();
691
692   JPanel bigPanel = new JPanel();
693
694   BorderLayout borderLayout4 = new BorderLayout();
695
696   JButton invert = new JButton();
697
698   JPanel buttonPanel = new JPanel();
699
700   JButton cancel = new JButton();
701
702   JButton ok = new JButton();
703
704   JButton loadColours = new JButton();
705
706   JButton saveColours = new JButton();
707
708   JPanel dasButtonPanel = new JPanel();
709
710   JButton fetchDAS = new JButton();
711
712   JButton saveDAS = new JButton();
713
714   JButton cancelDAS = new JButton();
715
716   JButton optimizeOrder = new JButton();
717   JButton sortByScore = new JButton();
718   JButton sortByDens = new JButton();
719
720   JPanel transbuttons = new JPanel(new BorderLayout());
721
722   private void jbInit() throws Exception
723   {
724     this.setLayout(borderLayout1);
725     settingsPane.setLayout(borderLayout2);
726     dasSettingsPane.setLayout(borderLayout3);
727     bigPanel.setLayout(borderLayout4);
728     invert.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
729     invert.setText("Invert Selection");
730     invert.addActionListener(new ActionListener()
731     {
732       public void actionPerformed(ActionEvent e)
733       {
734         invertSelection();
735       }
736     });
737     optimizeOrder.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
738     optimizeOrder.setText("Optimise Order");
739     optimizeOrder.addActionListener(new ActionListener()
740     {
741       public void actionPerformed(ActionEvent e)
742       {
743         orderByAvWidth();
744       }
745     });
746     sortByScore.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
747     sortByScore.setText("Seq sort by Score");
748     sortByScore.addActionListener(new ActionListener()
749     {
750       public void actionPerformed(ActionEvent e)
751       {
752         sortByScore(null);
753       }
754     });
755     sortByDens.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
756     sortByDens.setText("Seq Sort by density");
757     sortByDens.addActionListener(new ActionListener()
758     {
759       public void actionPerformed(ActionEvent e)
760       {
761         sortByDens(null);
762       }
763     });    cancel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
764     cancel.setText("Cancel");
765     cancel.addActionListener(new ActionListener()
766     {
767       public void actionPerformed(ActionEvent e)
768       {
769         updateFeatureRenderer(originalData);
770         close();
771       }
772     });
773     ok.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
774     ok.setText("OK");
775     ok.addActionListener(new ActionListener()
776     {
777       public void actionPerformed(ActionEvent e)
778       {
779         close();
780       }
781     });
782     loadColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
783     loadColours.setText("Load Colours");
784     loadColours.addActionListener(new ActionListener()
785     {
786       public void actionPerformed(ActionEvent e)
787       {
788         load();
789       }
790     });
791     saveColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
792     saveColours.setText("Save Colours");
793     saveColours.addActionListener(new ActionListener()
794     {
795       public void actionPerformed(ActionEvent e)
796       {
797         save();
798       }
799     });
800     transparency.addChangeListener(new ChangeListener()
801     {
802       public void stateChanged(ChangeEvent evt)
803       {
804         fr.setTransparency((float) (100 - transparency.getValue()) / 100f);
805         af.alignPanel.paintAlignment(true);
806       }
807     });
808
809     transparency.setMaximum(70);
810     fetchDAS.setText("Fetch DAS Features");
811     fetchDAS.addActionListener(new ActionListener()
812     {
813       public void actionPerformed(ActionEvent e)
814       {
815         fetchDAS_actionPerformed(e);
816       }
817     });
818     saveDAS.setText("Save as default");
819     saveDAS.addActionListener(new ActionListener()
820     {
821       public void actionPerformed(ActionEvent e)
822       {
823         saveDAS_actionPerformed(e);
824       }
825     });
826     dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
827     dasSettingsPane.setBorder(null);
828     cancelDAS.setEnabled(false);
829     cancelDAS.setText("Cancel Fetch");
830     cancelDAS.addActionListener(new ActionListener()
831     {
832       public void actionPerformed(ActionEvent e)
833       {
834         cancelDAS_actionPerformed(e);
835       }
836     });
837     this.add(tabbedPane, java.awt.BorderLayout.CENTER);
838     tabbedPane.addTab("Feature Settings", settingsPane);
839     tabbedPane.addTab("DAS Settings", dasSettingsPane);
840     bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
841     transPanel.add(transparency);
842     transbuttons.add(invert, java.awt.BorderLayout.NORTH);
843     transbuttons.add(optimizeOrder, java.awt.BorderLayout.SOUTH);
844     transPanel.add(transbuttons);
845     buttonPanel.add(ok);
846     buttonPanel.add(cancel);
847     buttonPanel.add(loadColours);
848     buttonPanel.add(saveColours);
849     buttonPanel.add(sortByScore);
850     buttonPanel.add(sortByDens);
851     bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
852     dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
853     dasButtonPanel.add(fetchDAS);
854     dasButtonPanel.add(cancelDAS);
855     dasButtonPanel.add(saveDAS);
856     settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
857     settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
858   }
859
860   protected void sortByDens(String[] typ)
861   {
862     sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
863   }
864   protected void sortBy(String[] typ, String methodText, final String method) {
865     if (typ==null)
866     {
867       typ = getDisplayedFeatureTypes();
868     }
869     String gps[]=null;
870     gps = getDisplayedFeatureGroups();
871     if (typ!=null)
872     {
873       for (int i=0;i<typ.length; i++)
874       {
875         System.err.println("Sorting on Types:"+typ[i]);
876       }
877     }
878     if (gps!=null)
879     {
880       
881       for (int i=0;i<gps.length; i++)
882       {
883         System.err.println("Sorting on groups:"+gps[i]);
884       }
885     }
886     AlignmentPanel alignPanel = af.alignPanel;
887     AlignmentI al = alignPanel.av.getAlignment();
888
889     int start,stop;
890     SequenceGroup sg = alignPanel.av.getSelectionGroup();
891     if (sg!=null)
892     {
893       start = sg.getStartRes();
894       stop = sg.getEndRes();
895     } else {
896       start = 0;
897       stop = al.getWidth();
898     }
899     SequenceI[] oldOrder = al.getSequencesArray();
900     AlignmentSorter.sortByFeature(typ, gps, start, stop,
901             al, method);
902     af.addHistoryItem(new OrderCommand(methodText, oldOrder,
903             alignPanel.av.getAlignment()));
904     alignPanel.paintAlignment(true);
905     
906   }
907
908   protected void sortByScore(String[] typ)
909   {
910     sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
911   }
912
913   private String[] getDisplayedFeatureTypes()
914   {
915     String[] typ=null;
916     if (fr!=null)
917     {
918       synchronized (fr.renderOrder) {
919         typ = new String[fr.renderOrder.length];
920         System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
921         for (int i=0;i<typ.length;i++)
922         {
923           if (af.viewport.featuresDisplayed.get(typ[i])==null)
924           {
925             typ[i] = null;
926           }
927         }
928       }
929     }
930     return typ;
931   }
932
933   private String[] getDisplayedFeatureGroups()
934   {
935     String[] gps=null;
936     if (fr!=null)
937     {
938       
939       if (fr.featureGroups!=null)
940       {
941         Enumeration en = fr.featureGroups.keys();
942         gps = new String[fr.featureColours.size()];
943         int g=0;
944         boolean valid=false;
945         while (en.hasMoreElements())
946         {
947           String gp = (String) en.nextElement();
948           Boolean on = (Boolean )fr.featureGroups.get(gp);
949           if (on!=null && on.booleanValue())
950           {
951             valid = true;
952             gps[g++] = gp;
953           }
954         }
955         while (g<gps.length)
956         {
957           gps[g++] = null;
958         }
959         if (!valid)
960         {
961           return null;
962         }
963       }
964     }
965     return gps;
966   }
967
968   public void fetchDAS_actionPerformed(ActionEvent e)
969   {
970     fetchDAS.setEnabled(false);
971     cancelDAS.setEnabled(true);
972     Vector selectedSources = dassourceBrowser.getSelectedSources();
973     doDasFeatureFetch(selectedSources, true, true);
974   }
975
976   /**
977    * get the features from selectedSources for all or the current selection
978    * 
979    * @param selectedSources
980    * @param checkDbRefs
981    * @param promptFetchDbRefs
982    */
983   private void doDasFeatureFetch(Vector selectedSources,
984           boolean checkDbRefs, boolean promptFetchDbRefs)
985   {
986     SequenceI[] dataset, seqs;
987     int iSize;
988     AlignViewport vp = af.getViewport();
989     if (vp.getSelectionGroup() != null
990             && vp.getSelectionGroup().getSize() > 0)
991     {
992       iSize = vp.getSelectionGroup().getSize();
993       dataset = new SequenceI[iSize];
994       seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
995     }
996     else
997     {
998       iSize = vp.getAlignment().getHeight();
999       seqs = vp.getAlignment().getSequencesArray();
1000     }
1001
1002     dataset = new SequenceI[iSize];
1003     for (int i = 0; i < iSize; i++)
1004     {
1005       dataset[i] = seqs[i].getDatasetSequence();
1006     }
1007
1008     cancelDAS.setEnabled(true);
1009     dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1010             this, selectedSources, checkDbRefs, promptFetchDbRefs);
1011     af.getViewport().setShowSequenceFeatures(true);
1012     af.showSeqFeatures.setSelected(true);
1013   }
1014
1015   /**
1016    * blocking call to initialise the das source browser
1017    */
1018   public void initDasSources()
1019   {
1020     dassourceBrowser.initDasSources();
1021   }
1022   /**
1023    * examine the current list of das sources and return any matching the given nicknames in sources 
1024    * @param sources Vector of Strings to resolve to DAS source nicknames.
1025    * @return sources that are present in source list.
1026    */
1027   public Vector resolveSourceNicknames(Vector sources)
1028   {
1029     return dassourceBrowser.resolveSourceNicknames(sources);
1030   }
1031   /**
1032    * get currently selected das sources. ensure you have called initDasSources before calling this.
1033    * @return vector of selected das source nicknames
1034    */
1035   public Vector getSelectedSources()
1036   {
1037     return dassourceBrowser.getSelectedSources();
1038   }
1039   /**
1040    * properly initialise DAS fetcher and then initiate a new thread to fetch
1041    * features from the named sources (rather than any turned on by default)
1042    * 
1043    * @param sources
1044    */
1045   public void fetchDasFeatures(Vector sources)
1046   {
1047     initDasSources();
1048     Vector resolved = resolveSourceNicknames(sources);
1049     if (resolved.size() == 0)
1050     {
1051       resolved = dassourceBrowser.getSelectedSources();
1052     }
1053     if (resolved.size() > 0)
1054     {
1055       final Vector dassources = resolved;
1056       SwingUtilities.invokeLater(new Runnable()
1057       {
1058
1059         public void run()
1060         {
1061           fetchDAS.setEnabled(false);
1062           cancelDAS.setEnabled(true);
1063           doDasFeatureFetch(dassources, true, false);
1064
1065         }
1066       });
1067     }
1068   }
1069
1070   public void saveDAS_actionPerformed(ActionEvent e)
1071   {
1072     dassourceBrowser
1073             .saveProperties(jalview.bin.Cache.applicationProperties);
1074   }
1075
1076   public void complete()
1077   {
1078     fetchDAS.setEnabled(true);
1079     cancelDAS.setEnabled(false);
1080   }
1081
1082   public void cancelDAS_actionPerformed(ActionEvent e)
1083   {
1084     if (dasFeatureFetcher != null)
1085     {
1086       dasFeatureFetcher.cancel();
1087     }
1088     fetchDAS.setEnabled(true);
1089     cancelDAS.setEnabled(false);
1090   }
1091
1092   public void noDasSourceActive()
1093   {
1094     complete();
1095     JOptionPane.showInternalConfirmDialog(Desktop.desktop,
1096             "No das sources were selected.\n"
1097                     + "Please select some sources and\n" + " try again.",
1098             "No Sources Selected", JOptionPane.DEFAULT_OPTION,
1099             JOptionPane.INFORMATION_MESSAGE);
1100   }
1101
1102   // ///////////////////////////////////////////////////////////////////////
1103   // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1104   // ///////////////////////////////////////////////////////////////////////
1105   class FeatureTableModel extends AbstractTableModel
1106   {
1107     FeatureTableModel(Object[][] data)
1108     {
1109       this.data = data;
1110     }
1111
1112     private String[] columnNames =
1113     { "Feature Type", "Colour", "Display" };
1114
1115     private Object[][] data;
1116
1117     public Object[][] getData()
1118     {
1119       return data;
1120     }
1121
1122     public void setData(Object[][] data)
1123     {
1124       this.data = data;
1125     }
1126
1127     public int getColumnCount()
1128     {
1129       return columnNames.length;
1130     }
1131
1132     public Object[] getRow(int row)
1133     {
1134       return data[row];
1135     }
1136
1137     public int getRowCount()
1138     {
1139       return data.length;
1140     }
1141
1142     public String getColumnName(int col)
1143     {
1144       return columnNames[col];
1145     }
1146
1147     public Object getValueAt(int row, int col)
1148     {
1149       return data[row][col];
1150     }
1151
1152     public Class getColumnClass(int c)
1153     {
1154       return getValueAt(0, c).getClass();
1155     }
1156
1157     public boolean isCellEditable(int row, int col)
1158     {
1159       return col == 0 ? false : true;
1160     }
1161
1162     public void setValueAt(Object value, int row, int col)
1163     {
1164       data[row][col] = value;
1165       fireTableCellUpdated(row, col);
1166       updateFeatureRenderer(data);
1167     }
1168
1169   }
1170
1171   class ColorRenderer extends JLabel implements TableCellRenderer
1172   {
1173     javax.swing.border.Border unselectedBorder = null;
1174
1175     javax.swing.border.Border selectedBorder = null;
1176
1177     public ColorRenderer()
1178     {
1179       setOpaque(true); // MUST do this for background to show up.
1180     }
1181
1182     public Component getTableCellRendererComponent(JTable table,
1183             Object color, boolean isSelected, boolean hasFocus, int row,
1184             int column)
1185     {
1186       Color newColor = (Color) color;
1187       setBackground(newColor);
1188       if (isSelected)
1189       {
1190         if (selectedBorder == null)
1191         {
1192           selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1193                   table.getSelectionBackground());
1194         }
1195         setBorder(selectedBorder);
1196       }
1197       else
1198       {
1199         if (unselectedBorder == null)
1200         {
1201           unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1202                   table.getBackground());
1203         }
1204         setBorder(unselectedBorder);
1205       }
1206
1207       setToolTipText("RGB value: " + newColor.getRed() + ", "
1208               + newColor.getGreen() + ", " + newColor.getBlue());
1209       return this;
1210     }
1211   }
1212 }
1213
1214 class ColorEditor extends AbstractCellEditor implements TableCellEditor,
1215         ActionListener
1216 {
1217   Color currentColor;
1218
1219   JButton button;
1220
1221   JColorChooser colorChooser;
1222
1223   JDialog dialog;
1224
1225   protected static final String EDIT = "edit";
1226
1227   public ColorEditor()
1228   {
1229     // Set up the editor (from the table's point of view),
1230     // which is a button.
1231     // This button brings up the color chooser dialog,
1232     // which is the editor from the user's point of view.
1233     button = new JButton();
1234     button.setActionCommand(EDIT);
1235     button.addActionListener(this);
1236     button.setBorderPainted(false);
1237     // Set up the dialog that the button brings up.
1238     colorChooser = new JColorChooser();
1239     dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1240             colorChooser, this, // OK button handler
1241             null); // no CANCEL button handler
1242   }
1243
1244   /**
1245    * Handles events from the editor button and from the dialog's OK button.
1246    */
1247   public void actionPerformed(ActionEvent e)
1248   {
1249
1250     if (EDIT.equals(e.getActionCommand()))
1251     {
1252       // The user has clicked the cell, so
1253       // bring up the dialog.
1254       button.setBackground(currentColor);
1255       colorChooser.setColor(currentColor);
1256       dialog.setVisible(true);
1257
1258       // Make the renderer reappear.
1259       fireEditingStopped();
1260
1261     }
1262     else
1263     { // User pressed dialog's "OK" button.
1264       currentColor = colorChooser.getColor();
1265     }
1266   }
1267
1268   // Implement the one CellEditor method that AbstractCellEditor doesn't.
1269   public Object getCellEditorValue()
1270   {
1271     return currentColor;
1272   }
1273
1274   // Implement the one method defined by TableCellEditor.
1275   public Component getTableCellEditorComponent(JTable table, Object value,
1276           boolean isSelected, int row, int column)
1277   {
1278     currentColor = (Color) value;
1279     return button;
1280   }
1281 }