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