895c23586204b66c8969a5e92f615b3f0688ff67
[jalview.git] / src / jalview / gui / FeatureSettings.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2007 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.datamodel.*;
34 import jalview.io.*;
35
36 public class FeatureSettings
37     extends JPanel
38 {
39   DasSourceBrowser dassourceBrowser;
40   jalview.io.DasSequenceFeatureFetcher dasFeatureFetcher;
41   JPanel settingsPane = new JPanel();
42   JPanel dasSettingsPane = new JPanel();
43
44   final FeatureRenderer fr;
45   public final AlignFrame af;
46   Object[][] originalData;
47   final JInternalFrame frame;
48   JScrollPane scrollPane = new JScrollPane();
49   JTable table;
50   JPanel groupPanel;
51   JSlider transparency = new JSlider();
52
53   JPanel transPanel = new JPanel(new FlowLayout());
54
55   public FeatureSettings(AlignFrame af)
56   {
57     this.af = af;
58     fr = af.getFeatureRenderer();
59
60     transparency.setMaximum(100 - (int) (fr.transparency * 100));
61
62     try
63     {
64       jbInit();
65     }
66     catch (Exception ex)
67     {
68       ex.printStackTrace();
69     }
70
71     table = new JTable();
72     table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
73     table.setFont(new Font("Verdana", Font.PLAIN, 12));
74     table.setDefaultRenderer(Color.class,
75                              new ColorRenderer());
76
77     table.setDefaultEditor(Color.class,
78                            new ColorEditor());
79
80     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
81
82     table.addMouseListener(new MouseAdapter()
83     {
84       public void mousePressed(MouseEvent evt)
85       {
86         selectedRow = table.rowAtPoint(evt.getPoint());
87       }
88     });
89
90     table.addMouseMotionListener(new MouseMotionAdapter()
91     {
92       public void mouseDragged(MouseEvent evt)
93       {
94         int newRow = table.rowAtPoint(evt.getPoint());
95         if (newRow != selectedRow
96             && selectedRow != -1
97             && newRow != -1)
98         {
99           Object[] temp = new Object[3];
100           temp[0] = table.getValueAt(selectedRow, 0);
101           temp[1] = table.getValueAt(selectedRow, 1);
102           temp[2] = table.getValueAt(selectedRow, 2);
103
104           table.setValueAt(table.getValueAt(newRow, 0), selectedRow, 0);
105           table.setValueAt(table.getValueAt(newRow, 1), selectedRow, 1);
106           table.setValueAt(table.getValueAt(newRow, 2), selectedRow, 2);
107
108           table.setValueAt(temp[0], newRow, 0);
109           table.setValueAt(temp[1], newRow, 1);
110           table.setValueAt(temp[2], newRow, 2);
111
112           selectedRow = newRow;
113         }
114       }
115     });
116
117     scrollPane.setViewportView(table);
118
119     dassourceBrowser = new DasSourceBrowser();
120     dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
121
122     if (af.getViewport().featuresDisplayed == null || fr.renderOrder == null)
123     {
124       fr.findAllFeatures(true); // display everything!
125     }
126
127     setTableData();
128     final PropertyChangeListener change;
129     final FeatureSettings fs=this;
130     fr.addPropertyChangeListener(change=new PropertyChangeListener() {
131       private boolean firing=false;
132       public void propertyChange(PropertyChangeEvent evt)
133       {
134         if (!fs.resettingTable && !firing) {
135           firing=true;
136           resetTable(null);
137           firing=false;
138         }
139       }
140       
141     });
142     
143     frame = new JInternalFrame();
144     frame.setContentPane(this);
145     Desktop.addInternalFrame(frame, "Sequence Feature Settings", 400, 450);
146     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
147     {
148       public void internalFrameClosed(
149           javax.swing.event.InternalFrameEvent evt)
150       {
151         fr.removePropertyChangeListener(change);
152       }
153       ;
154     });
155     frame.setLayer(JLayeredPane.PALETTE_LAYER);
156   }
157   /**
158    * contains a float[3] for each feature type string. created by setTableData
159    */
160   Hashtable typeWidth=null;
161   synchronized public void setTableData()
162   {
163     if (fr.featureGroups == null)
164     {
165       fr.featureGroups = new Hashtable();
166     }
167     Vector allFeatures = new Vector();
168     Vector allGroups = new Vector();
169     SequenceFeature[] tmpfeatures;
170     String group;
171     for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
172     {
173       if (af.getViewport().alignment.getSequenceAt(i).getDatasetSequence().
174           getSequenceFeatures() == null)
175       {
176         continue;
177       }
178
179       tmpfeatures = af.getViewport().alignment.getSequenceAt(i).
180           getDatasetSequence().getSequenceFeatures();
181
182       int index = 0;
183       while (index < tmpfeatures.length)
184       {
185         if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
186         {
187           index++;
188           continue;
189         }
190
191         if (tmpfeatures[index].getFeatureGroup() != null)
192         {
193           group = tmpfeatures[index].featureGroup;
194           if (!allGroups.contains(group))
195           {
196             allGroups.addElement(group);
197
198             boolean visible;
199             if (fr.featureGroups.containsKey(group))
200             {
201               visible = ( (Boolean) fr.featureGroups.get(group)).booleanValue();
202             } else {
203               visible=true; //initGroups || fr.av.featuresDisplayed.containsKey(tmpfeatures[index].getType());
204             }
205
206             if (groupPanel == null)
207             {
208               groupPanel = new JPanel();
209             }
210
211             boolean alreadyAdded = false;
212             for (int g = 0; g < groupPanel.getComponentCount(); g++)
213             {
214               if ( ( (JCheckBox) groupPanel.getComponent(g))
215                   .getText().equals(group))
216               {
217                 alreadyAdded = true;
218                 break;
219               }
220             }
221
222             if (alreadyAdded)
223             {
224               
225               continue;
226             }
227
228             fr.featureGroups.put(group, new Boolean(visible));
229             final String grp = group;
230             final JCheckBox check = new JCheckBox(group, visible);
231             check.setFont(new Font("Serif", Font.BOLD, 12));
232             check.addItemListener(new ItemListener()
233             {
234               public void itemStateChanged(ItemEvent evt)
235               {
236                 fr.featureGroups.put(check.getText(),
237                                      new Boolean(check.isSelected()));
238                 af.alignPanel.seqPanel.seqCanvas.repaint();
239                 if (af.alignPanel.overviewPanel != null)
240                 {
241                   af.alignPanel.overviewPanel.updateOverviewImage();
242                 }
243
244                 resetTable(new String[] { grp } );
245               }
246             });
247             groupPanel.add(check);
248           }
249         }
250
251         if (!allFeatures.contains(tmpfeatures[index].getType()))
252         {
253           allFeatures.addElement(tmpfeatures[index].getType());
254         }
255         index++;
256       }
257     }
258     
259     resetTable(null);
260
261     validate();
262   }
263   boolean resettingTable=false;
264   void resetTable(String[] groupChanged)
265   {
266     resettingTable=true;
267     typeWidth=new Hashtable();
268     Vector groupchanged = new Vector();
269     // TODO: change avWidth calculation to 'per-sequence' average and use long rather than float
270     float[] avWidth=null;
271     SequenceFeature[] tmpfeatures;
272     String group = null, type;
273     Vector visibleChecks = new Vector();
274
275     //Find out which features should be visible depending on which groups
276     //are selected / deselected
277     //and recompute average width ordering
278     for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
279     {
280
281       tmpfeatures = af.getViewport().alignment.getSequenceAt(i).
282           getDatasetSequence().getSequenceFeatures();
283       if (tmpfeatures == null)
284       {
285         continue;
286       }
287
288       int index = 0;
289       while (index < tmpfeatures.length)
290       {
291         group = tmpfeatures[index].featureGroup;
292
293         if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
294         {
295           index++;
296           continue;
297         }
298
299         if (group == null || fr.featureGroups.get(group) == null ||
300             ( (Boolean) fr.featureGroups.get(group)).booleanValue())
301         {
302           type = tmpfeatures[index].getType();
303           if (groupchanged==null || groupchanged.contains(group)) {
304             //af.getViewport().featuresDisplayed.put(type, fr.getColour(type));
305           }
306           if (!visibleChecks.contains(type))
307           {
308             visibleChecks.addElement(type);
309           }
310         }
311         if (!typeWidth.containsKey(tmpfeatures[index].getType())) {
312           typeWidth.put(tmpfeatures[index].getType(), avWidth=new float[3]);
313         } else {
314           avWidth = (float[]) typeWidth.get(tmpfeatures[index].getType());
315         }
316         avWidth[0]++;
317         if (tmpfeatures[index].getBegin()>tmpfeatures[index].getEnd())
318         {
319           avWidth[1]+=1+tmpfeatures[index].getBegin()-tmpfeatures[index].getEnd();
320         } else {
321           avWidth[1]+=1+tmpfeatures[index].getEnd()-tmpfeatures[index].getBegin();
322         }
323         index++;
324       }
325     }
326
327     int fSize = visibleChecks.size();
328     Object[][] data = new Object[fSize][3];
329     int dataIndex = 0;
330
331     if (fr.renderOrder != null)
332     {
333       fr.findAllFeatures(groupChanged!=null); // prod to update colourschemes. but don't affect display
334       //First add the checks in the previous render order,
335       //in case the window has been closed and reopened
336       for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
337       {
338         type = fr.renderOrder[ro];
339
340         if (!visibleChecks.contains(type))
341         {
342           continue;
343         }
344
345         data[dataIndex][0] = type;
346         data[dataIndex][1] = fr.getColour(type);
347         data[dataIndex][2] = new Boolean(af.getViewport().featuresDisplayed.containsKey(type));
348         dataIndex++;
349         visibleChecks.removeElement(type);
350       }
351     }
352
353     fSize = visibleChecks.size();
354     for (int i = 0; i < fSize; i++)
355     {
356       //These must be extra features belonging to the group
357       //which was just selected
358       type = visibleChecks.elementAt(i).toString();
359       data[dataIndex][0] = type;
360
361       data[dataIndex][1] = fr.getColour(type);
362       if (data[dataIndex][1] == null)
363       {
364         //"Colour has been updated in another view!!"
365         fr.renderOrder = null;
366         return;
367       }
368
369       data[dataIndex][2] = new Boolean(true);
370       dataIndex++;
371     }
372
373     if (originalData == null)
374     {
375       originalData = new Object[data.length][3];
376       System.arraycopy(data, 0, originalData, 0, data.length);
377     }
378
379     table.setModel(new FeatureTableModel(data));
380     table.getColumnModel().getColumn(0).setPreferredWidth(200);
381
382     if (groupPanel != null)
383     {
384       groupPanel.setLayout(
385           new GridLayout(fr.featureGroups.size() / 4 + 1, 4));
386
387       groupPanel.validate();
388       bigPanel.add(groupPanel, BorderLayout.NORTH);
389     }
390
391     updateFeatureRenderer(data, groupChanged!=null);
392     resettingTable=false;
393   }
394   /**
395    * reorder data based on the featureRenderers global priority list.
396    * @param data
397    */
398   private void ensureOrder(Object[][] data)
399   {
400     boolean sort=false;
401     float[] order = new float[data.length];
402     for (int i=0;i<order.length; i++)
403     {
404       order[i] = fr.getOrder(data[i][0].toString());
405       if (order[i]<0)
406         order[i] = fr.setOrder(data[i][0].toString(), i/order.length);
407       if (i>1)
408         sort = sort || order[i-1]>order[i];
409     }
410     if (sort)
411       jalview.util.QuickSort.sort(order, data);
412   }
413
414   void load()
415   {
416     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
417         getProperty(
418             "LAST_DIRECTORY"), new String[]
419         {"fc"},
420         new String[]
421         {"Sequence Feature Colours"}, "Sequence Feature Colours");
422     chooser.setFileView(new jalview.io.JalviewFileView());
423     chooser.setDialogTitle("Load Feature Colours");
424     chooser.setToolTipText("Load");
425
426     int value = chooser.showOpenDialog(this);
427
428     if (value == JalviewFileChooser.APPROVE_OPTION)
429     {
430       File file = chooser.getSelectedFile();
431
432       try
433       {
434         InputStreamReader in = new InputStreamReader(new FileInputStream(
435             file), "UTF-8");
436
437         jalview.binding.JalviewUserColours jucs = new jalview.binding.
438             JalviewUserColours();
439         jucs = (jalview.binding.JalviewUserColours) jucs.unmarshal(in);
440
441         for (int i = 0; i < jucs.getColourCount(); i++)
442         {
443           String name;
444           fr.setColour(name=jucs.getColour(i).getName(),
445                        new Color(Integer.parseInt(jucs.getColour(i).getRGB(),
446                                                   16)));
447           fr.setOrder(name,i/jucs.getColourCount());
448         }
449
450         setTableData();
451         af.alignPanel.paintAlignment(true);
452       }
453       catch (Exception ex)
454       {
455         System.out.println("Error loading User Colour File\n" + ex);
456       }
457     }
458   }
459
460   void save()
461   {
462     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache.
463         getProperty(
464             "LAST_DIRECTORY"), new String[]
465         {"fc"},
466         new String[]
467         {"Sequence Feature Colours"}, "Sequence Feature Colours");
468     chooser.setFileView(new jalview.io.JalviewFileView());
469     chooser.setDialogTitle("Save Feature Colour Scheme");
470     chooser.setToolTipText("Save");
471
472     int value = chooser.showSaveDialog(this);
473
474     if (value == JalviewFileChooser.APPROVE_OPTION)
475     {
476       String choice = chooser.getSelectedFile().getPath();
477       jalview.binding.JalviewUserColours ucs = new jalview.binding.
478           JalviewUserColours();
479       ucs.setSchemeName("Sequence Features");
480       try
481       {
482         PrintWriter out = new PrintWriter(new OutputStreamWriter(
483             new FileOutputStream(choice), "UTF-8"));
484
485         Enumeration e = fr.featureColours.keys();
486         while (e.hasMoreElements())
487         {
488           jalview.binding.Colour col = new jalview.binding.Colour();
489           col.setName(e.nextElement().toString());
490           col.setRGB(jalview.util.Format.getHexString(
491               fr.getColour(col.getName())));
492           ucs.addColour(col);
493         }
494
495         ucs.marshal(out);
496         out.close();
497       }
498       catch (Exception ex)
499       {
500         ex.printStackTrace();
501       }
502     }
503   }
504
505   public void invertSelection()
506   {
507     for (int i = 0; i < table.getRowCount(); i++)
508     {
509       Boolean value = (Boolean) table.getValueAt(i, 2);
510
511       table.setValueAt(
512           new Boolean(!value.booleanValue()),
513           i, 2);
514     }
515   }
516   public void orderByAvWidth() {
517     if (table==null || table.getModel()==null)
518       return;
519     Object[][] data = ((FeatureTableModel) table.getModel()).getData();
520     float[] width = new float[data.length];
521     float[] awidth;
522     float max=0;
523     int num=0;
524     for (int i=0;i<data.length;i++) {
525        awidth = (float[]) typeWidth.get(data[i][0]);
526        if (awidth[0]>0) {
527          width[i] = awidth[1]/awidth[0];// *awidth[0]*awidth[2]; - better weight - but have to make per sequence, too (awidth[2])
528          //if (width[i]==1) // hack to distinguish single width sequences.
529          num++;
530        } else {
531          width[i]=0;
532        }
533        if (max<width[i])
534          max=width[i];
535     }
536     boolean sort=false;
537     for (int i=0;i<width.length; i++) {
538       //awidth = (float[]) typeWidth.get(data[i][0]);
539       if (width[i]==0) 
540       {
541         width[i] = fr.getOrder(data[i][0].toString());
542         if (width[i]<0)
543         {
544           width[i] = fr.setOrder(data[i][0].toString(), i/data.length);
545         }
546       } else { 
547         width[i] /=max; // normalize
548         fr.setOrder(data[i][0].toString(), width[i]); // store for later 
549       }
550       if (i>0)
551         sort = sort || width[i-1]>width[i];
552     }
553     if (sort)
554       jalview.util.QuickSort.sort(width, data);
555     // update global priority order
556     
557     updateFeatureRenderer(data,false);
558     table.repaint();
559   }
560   public void close()
561   {
562     try
563     {
564       frame.setClosed(true);
565     }
566     catch (Exception exe)
567     {}
568
569   }
570
571   public void updateFeatureRenderer(Object[][] data)
572   {
573     updateFeatureRenderer(data, true);
574   }
575   private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
576   {
577     fr.setFeaturePriority(data, visibleNew);
578     af.alignPanel.paintAlignment(true);
579
580     if (af.alignPanel.overviewPanel != null)
581     {
582       af.alignPanel.overviewPanel.updateOverviewImage();
583     }
584   }
585
586   int selectedRow = -1;
587   JTabbedPane tabbedPane = new JTabbedPane();
588   BorderLayout borderLayout1 = new BorderLayout();
589   BorderLayout borderLayout2 = new BorderLayout();
590   BorderLayout borderLayout3 = new BorderLayout();
591   JPanel bigPanel = new JPanel();
592   BorderLayout borderLayout4 = new BorderLayout();
593   JButton invert = new JButton();
594   JPanel buttonPanel = new JPanel();
595   JButton cancel = new JButton();
596   JButton ok = new JButton();
597   JButton loadColours = new JButton();
598   JButton saveColours = new JButton();
599   JPanel dasButtonPanel = new JPanel();
600   JButton fetchDAS = new JButton();
601   JButton saveDAS = new JButton();
602   JButton cancelDAS = new JButton();
603   JButton optimizeOrder = new JButton();
604   JPanel transbuttons = new JPanel(new BorderLayout());
605   private void jbInit()
606       throws Exception
607   {
608     this.setLayout(borderLayout1);
609     settingsPane.setLayout(borderLayout2);
610     dasSettingsPane.setLayout(borderLayout3);
611     bigPanel.setLayout(borderLayout4);
612     invert.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
613     invert.setText("Invert Selection");
614     invert.addActionListener(new ActionListener()
615     {
616       public void actionPerformed(ActionEvent e)
617       {
618         invertSelection();
619       }
620     });
621     optimizeOrder.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
622     optimizeOrder.setText("Optimise Order");
623     optimizeOrder.addActionListener(new ActionListener() {
624       public void actionPerformed(ActionEvent e) {
625         orderByAvWidth();
626       }
627     });
628     cancel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
629     cancel.setText("Cancel");
630     cancel.addActionListener(new ActionListener()
631     {
632       public void actionPerformed(ActionEvent e)
633       {
634         updateFeatureRenderer(originalData);
635         close();
636       }
637     });
638     ok.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
639     ok.setText("OK");
640     ok.addActionListener(new ActionListener()
641     {
642       public void actionPerformed(ActionEvent e)
643       {
644         close();
645       }
646     });
647     loadColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
648     loadColours.setText("Load Colours");
649     loadColours.addActionListener(new ActionListener()
650     {
651       public void actionPerformed(ActionEvent e)
652       {
653         load();
654       }
655     });
656     saveColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
657     saveColours.setText("Save Colours");
658     saveColours.addActionListener(new ActionListener()
659     {
660       public void actionPerformed(ActionEvent e)
661       {
662         save();
663       }
664     });
665     transparency.addChangeListener(new ChangeListener()
666     {
667       public void stateChanged(ChangeEvent evt)
668       {
669         fr.setTransparency( (float) (100 - transparency.getValue()) / 100f);
670         af.alignPanel.paintAlignment(true);
671       }
672     });
673
674     transparency.setMaximum(70);
675     fetchDAS.setText("Fetch DAS Features");
676     fetchDAS.addActionListener(new ActionListener()
677     {
678       public void actionPerformed(ActionEvent e)
679       {
680         fetchDAS_actionPerformed(e);
681       }
682     });
683     saveDAS.setText("Save as default");
684     saveDAS.addActionListener(new ActionListener()
685     {
686       public void actionPerformed(ActionEvent e)
687       {
688         saveDAS_actionPerformed(e);
689       }
690     });
691     dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
692     dasSettingsPane.setBorder(null);
693     cancelDAS.setEnabled(false);
694     cancelDAS.setText("Cancel Fetch");
695     cancelDAS.addActionListener(new ActionListener()
696     {
697       public void actionPerformed(ActionEvent e)
698       {
699         cancelDAS_actionPerformed(e);
700       }
701     });
702     this.add(tabbedPane, java.awt.BorderLayout.CENTER);
703     tabbedPane.addTab("Feature Settings", settingsPane);
704     tabbedPane.addTab("DAS Settings", dasSettingsPane);
705     bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
706     transPanel.add(transparency);
707     transbuttons.add(invert, java.awt.BorderLayout.NORTH);
708     transbuttons.add(optimizeOrder,java.awt.BorderLayout.SOUTH);
709     transPanel.add(transbuttons);
710     buttonPanel.add(ok);
711     buttonPanel.add(cancel);
712     buttonPanel.add(loadColours);
713     buttonPanel.add(saveColours);
714     bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
715     dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
716     dasButtonPanel.add(fetchDAS);
717     dasButtonPanel.add(cancelDAS);
718     dasButtonPanel.add(saveDAS);
719     settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
720     settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
721   }
722
723   public void fetchDAS_actionPerformed(ActionEvent e)
724   {
725     fetchDAS.setEnabled(false);
726     cancelDAS.setEnabled(true);
727     Vector selectedSources = dassourceBrowser.getSelectedSources();
728
729     SequenceI[] dataset, seqs;
730     int iSize;
731
732     if (af.getViewport().getSelectionGroup() != null
733         && af.getViewport().getSelectionGroup().getSize() > 0)
734     {
735       iSize = af.getViewport().getSelectionGroup().getSize();
736       dataset = new SequenceI[iSize];
737       seqs = af.getViewport().getSelectionGroup().
738           getSequencesInOrder(
739               af.getViewport().getAlignment());
740     }
741     else
742     {
743       iSize = af.getViewport().getAlignment().getHeight();
744       seqs = af.getViewport().getAlignment().getSequencesArray();
745     }
746
747     dataset = new SequenceI[iSize];
748     for (int i = 0; i < iSize; i++)
749     {
750       dataset[i] = seqs[i].getDatasetSequence();
751     }
752
753     dasFeatureFetcher =
754         new jalview.io.DasSequenceFeatureFetcher(
755             dataset,
756             this,
757             selectedSources);
758     cancelDAS.setEnabled(true);
759     af.getViewport().setShowSequenceFeatures(true);
760     af.showSeqFeatures.setSelected(true);
761   }
762
763   public void saveDAS_actionPerformed(ActionEvent e)
764   {
765     dassourceBrowser.saveProperties(jalview.bin.Cache.applicationProperties);
766   }
767
768   public void complete()
769   {
770     fetchDAS.setEnabled(true);
771     cancelDAS.setEnabled(false);
772   }
773
774   public void cancelDAS_actionPerformed(ActionEvent e)
775   {
776     dasFeatureFetcher.cancel();
777     fetchDAS.setEnabled(true);
778     cancelDAS.setEnabled(false);
779   }
780
781   /////////////////////////////////////////////////////////////////////////
782   // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
783   /////////////////////////////////////////////////////////////////////////
784   class FeatureTableModel
785       extends AbstractTableModel
786   {
787     FeatureTableModel(Object[][] data)
788     {
789       this.data = data;
790     }
791
792     private String[] columnNames =
793         {
794         "Feature Type", "Colour", "Display"};
795     private Object[][] data;
796
797     public Object[][] getData()
798     {
799       return data;
800     }
801
802     public void setData(Object[][] data)
803     {
804       this.data = data;
805     }
806
807     public int getColumnCount()
808     {
809       return columnNames.length;
810     }
811
812     public Object[] getRow(int row)
813     {
814       return data[row];
815     }
816
817     public int getRowCount()
818     {
819       return data.length;
820     }
821
822     public String getColumnName(int col)
823     {
824       return columnNames[col];
825     }
826
827     public Object getValueAt(int row, int col)
828     {
829       return data[row][col];
830     }
831
832     public Class getColumnClass(int c)
833     {
834       return getValueAt(0, c).getClass();
835     }
836
837     public boolean isCellEditable(int row, int col)
838     {
839       return col == 0 ? false : true;
840     }
841
842     public void setValueAt(Object value, int row, int col)
843     {
844       data[row][col] = value;
845       fireTableCellUpdated(row, col);
846       updateFeatureRenderer(data);
847     }
848
849   }
850
851   class ColorRenderer
852       extends JLabel implements TableCellRenderer
853   {
854     javax.swing.border.Border unselectedBorder = null;
855     javax.swing.border.Border selectedBorder = null;
856
857     public ColorRenderer()
858     {
859       setOpaque(true); //MUST do this for background to show up.
860     }
861
862     public Component getTableCellRendererComponent(
863         JTable table, Object color,
864         boolean isSelected, boolean hasFocus,
865         int row, int column)
866     {
867       Color newColor = (Color) color;
868       setBackground(newColor);
869       if (isSelected)
870       {
871         if (selectedBorder == null)
872         {
873           selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
874               table.getSelectionBackground());
875         }
876         setBorder(selectedBorder);
877       }
878       else
879       {
880         if (unselectedBorder == null)
881         {
882           unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
883               table.getBackground());
884         }
885         setBorder(unselectedBorder);
886       }
887
888       setToolTipText("RGB value: " + newColor.getRed() + ", "
889                      + newColor.getGreen() + ", "
890                      + newColor.getBlue());
891       return this;
892     }
893   }
894 }
895
896 class ColorEditor
897     extends AbstractCellEditor implements TableCellEditor,
898     ActionListener
899 {
900   Color currentColor;
901   JButton button;
902   JColorChooser colorChooser;
903   JDialog dialog;
904   protected static final String EDIT = "edit";
905
906   public ColorEditor()
907   {
908     //Set up the editor (from the table's point of view),
909     //which is a button.
910     //This button brings up the color chooser dialog,
911     //which is the editor from the user's point of view.
912     button = new JButton();
913     button.setActionCommand(EDIT);
914     button.addActionListener(this);
915     button.setBorderPainted(false);
916     //Set up the dialog that the button brings up.
917     colorChooser = new JColorChooser();
918     dialog = JColorChooser.createDialog(button,
919                                         "Select new Colour",
920                                         true, //modal
921                                         colorChooser,
922                                         this, //OK button handler
923                                         null); //no CANCEL button handler
924   }
925
926   /**
927    * Handles events from the editor button and from
928    * the dialog's OK button.
929    */
930   public void actionPerformed(ActionEvent e)
931   {
932
933     if (EDIT.equals(e.getActionCommand()))
934     {
935       //The user has clicked the cell, so
936       //bring up the dialog.
937       button.setBackground(currentColor);
938       colorChooser.setColor(currentColor);
939       dialog.setVisible(true);
940
941       //Make the renderer reappear.
942       fireEditingStopped();
943
944     }
945     else
946     { //User pressed dialog's "OK" button.
947       currentColor = colorChooser.getColor();
948     }
949   }
950
951   //Implement the one CellEditor method that AbstractCellEditor doesn't.
952   public Object getCellEditorValue()
953   {
954     return currentColor;
955   }
956
957   //Implement the one method defined by TableCellEditor.
958   public Component getTableCellEditorComponent(JTable table,
959                                                Object value,
960                                                boolean isSelected,
961                                                int row,
962                                                int column)
963   {
964     currentColor = (Color) value;
965     return button;
966   }
967 }