unnecessary code
[jalview.git] / src / jalview / gui / FeatureSettings.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 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.awt.geom.AffineTransform;
27 import java.awt.image.BufferedImage;
28 import java.beans.PropertyChangeEvent;
29 import java.beans.PropertyChangeListener;
30
31 import javax.swing.*;
32 import javax.swing.event.*;
33 import javax.swing.table.*;
34
35 import jalview.analysis.AlignmentSorter;
36 import jalview.bin.Cache;
37 import jalview.commands.OrderCommand;
38 import jalview.datamodel.*;
39 import jalview.io.*;
40 import jalview.schemes.AnnotationColourGradient;
41 import jalview.schemes.GraduatedColor;
42
43 public class FeatureSettings extends JPanel
44 {
45   DasSourceBrowser dassourceBrowser;
46
47   jalview.ws.DasSequenceFeatureFetcher dasFeatureFetcher;
48
49   JPanel settingsPane = new JPanel();
50
51   JPanel dasSettingsPane = new JPanel();
52
53   final FeatureRenderer fr;
54
55   public final AlignFrame af;
56
57   Object[][] originalData;
58
59   final JInternalFrame frame;
60
61   JScrollPane scrollPane = new JScrollPane();
62
63   JTable table;
64
65   JPanel groupPanel;
66
67   JSlider transparency = new JSlider();
68
69   JPanel transPanel = new JPanel(new GridLayout(1, 2));
70
71   public FeatureSettings(AlignFrame af)
72   {
73     this.af = af;
74     fr = af.getFeatureRenderer();
75
76     transparency.setMaximum(100 - (int) (fr.transparency * 100));
77
78     try
79     {
80       jbInit();
81     } catch (Exception ex)
82     {
83       ex.printStackTrace();
84     }
85
86     table = new JTable();
87     table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
88     table.setFont(new Font("Verdana", Font.PLAIN, 12));
89     table.setDefaultRenderer(Color.class, new ColorRenderer());
90
91     table.setDefaultEditor(Color.class, new ColorEditor(this));
92
93     table.setDefaultEditor(GraduatedColor.class, new ColorEditor(this));
94     table.setDefaultRenderer(GraduatedColor.class, new ColorRenderer());
95     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
96
97     table.addMouseListener(new MouseAdapter()
98     {
99       public void mousePressed(MouseEvent evt)
100       {
101         selectedRow = table.rowAtPoint(evt.getPoint());
102         if (javax.swing.SwingUtilities.isRightMouseButton(evt))
103         {
104           popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
105                   table.getValueAt(selectedRow, 1), fr.minmax, evt.getX(),
106                   evt.getY());
107         }
108       }
109     });
110
111     table.addMouseMotionListener(new MouseMotionAdapter()
112     {
113       public void mouseDragged(MouseEvent evt)
114       {
115         int newRow = table.rowAtPoint(evt.getPoint());
116         if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
117         {
118           Object[] temp = new Object[3];
119           temp[0] = table.getValueAt(selectedRow, 0);
120           temp[1] = table.getValueAt(selectedRow, 1);
121           temp[2] = table.getValueAt(selectedRow, 2);
122
123           table.setValueAt(table.getValueAt(newRow, 0), selectedRow, 0);
124           table.setValueAt(table.getValueAt(newRow, 1), selectedRow, 1);
125           table.setValueAt(table.getValueAt(newRow, 2), selectedRow, 2);
126
127           table.setValueAt(temp[0], newRow, 0);
128           table.setValueAt(temp[1], newRow, 1);
129           table.setValueAt(temp[2], newRow, 2);
130
131           selectedRow = newRow;
132         }
133       }
134     });
135
136     scrollPane.setViewportView(table);
137
138     dassourceBrowser = new DasSourceBrowser();
139     dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
140
141     if (af.getViewport().featuresDisplayed == null
142             || fr.renderOrder == null)
143     {
144       fr.findAllFeatures(true); // display everything!
145     }
146
147     setTableData();
148     final PropertyChangeListener change;
149     final FeatureSettings fs = this;
150     fr.addPropertyChangeListener(change = new PropertyChangeListener()
151     {
152       public void propertyChange(PropertyChangeEvent evt)
153       {
154         if (!fs.resettingTable && !fs.handlingUpdate)
155         {
156           fs.handlingUpdate = true;
157           fs.resetTable(null); // new groups may be added with new seuqence
158           // feature types only
159           fs.handlingUpdate = false;
160         }
161       }
162
163     });
164
165     frame = new JInternalFrame();
166     frame.setContentPane(this);
167     Desktop.addInternalFrame(frame, "Sequence Feature Settings", 400, 450);
168     frame
169             .addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
170             {
171               public void internalFrameClosed(
172                       javax.swing.event.InternalFrameEvent evt)
173               {
174                 fr.removePropertyChangeListener(change);
175               };
176             });
177     frame.setLayer(JLayeredPane.PALETTE_LAYER);
178   }
179
180   protected void popupSort(final int selectedRow, final String type,
181           final Object typeCol, final Hashtable minmax, int x, int y)
182   {
183     JPopupMenu men = new JPopupMenu("Settings for " + type);
184     JMenuItem scr = new JMenuItem("Sort by Score");
185     men.add(scr);
186     final FeatureSettings me = this;
187     scr.addActionListener(new ActionListener()
188     {
189
190       public void actionPerformed(ActionEvent e)
191       {
192         me.sortByScore(new String[]
193         { type });
194       }
195
196     });
197     JMenuItem dens = new JMenuItem("Sort by Density");
198     dens.addActionListener(new ActionListener()
199     {
200
201       public void actionPerformed(ActionEvent e)
202       {
203         me.sortByDens(new String[]
204         { type });
205       }
206
207     });
208     men.add(dens);
209     if (minmax != null)
210     {
211       final Object typeMinMax = minmax.get(type);
212       /*
213        * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
214        * this is broken at the moment and isn't that useful anyway!
215        * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
216        * ActionListener() {
217        * 
218        * public void actionPerformed(ActionEvent e) {
219        * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
220        * null); } else { minmax.put(type, typeMinMax); } }
221        * 
222        * });
223        * 
224        * men.add(chb);
225        */
226       if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
227       {
228         // if (table.getValueAt(row, column));
229         // graduated colourschemes for those where minmax exists for the
230         // positional features
231         final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
232                 "Graduated Colour");
233         mxcol.setSelected(!(typeCol instanceof Color));
234         men.add(mxcol);
235         mxcol.addActionListener(new ActionListener()
236         {
237           JColorChooser colorChooser;
238
239           public void actionPerformed(ActionEvent e)
240           {
241             if (e.getSource() == mxcol)
242             {
243               if (typeCol instanceof Color)
244               {
245                 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
246                         type);
247                 fc.addActionListener(this);
248               }
249               else
250               {
251                 // bring up simple color chooser
252                 colorChooser = new JColorChooser();
253                 JDialog dialog = JColorChooser.createDialog(me,
254                         "Select new Colour", true, // modal
255                         colorChooser, this, // OK button handler
256                         null); // no CANCEL button handler
257                 colorChooser.setColor(((GraduatedColor) typeCol)
258                         .getMaxColor());
259                 dialog.setVisible(true);
260               }
261             }
262             else
263             {
264               if (e.getSource() instanceof FeatureColourChooser)
265               {
266                 FeatureColourChooser fc = (FeatureColourChooser) e
267                         .getSource();
268                 table.setValueAt(fc.getLastColour(), selectedRow, 1);
269                 table.validate();
270               }
271               else
272               {
273                 // probably the color chooser!
274                 table.setValueAt(colorChooser.getColor(), selectedRow, 1);
275                 table.validate();
276                 me.updateFeatureRenderer(((FeatureTableModel) table
277                         .getModel()).getData(), false);
278               }
279             }
280           }
281
282         });
283       }
284     }
285     men.show(table, x, y);
286   }
287
288   /**
289    * true when Feature Settings are updating from feature renderer
290    */
291   private boolean handlingUpdate = false;
292
293   /**
294    * contains a float[3] for each feature type string. created by setTableData
295    */
296   Hashtable typeWidth = null;
297
298   synchronized public void setTableData()
299   {
300     if (fr.featureGroups == null)
301     {
302       fr.featureGroups = new Hashtable();
303     }
304     Vector allFeatures = new Vector();
305     Vector allGroups = new Vector();
306     SequenceFeature[] tmpfeatures;
307     String group;
308     for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
309     {
310       if (af.getViewport().alignment.getSequenceAt(i).getDatasetSequence()
311               .getSequenceFeatures() == null)
312       {
313         continue;
314       }
315
316       tmpfeatures = af.getViewport().alignment.getSequenceAt(i)
317               .getDatasetSequence().getSequenceFeatures();
318
319       int index = 0;
320       while (index < tmpfeatures.length)
321       {
322         if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
323         {
324           index++;
325           continue;
326         }
327
328         if (tmpfeatures[index].getFeatureGroup() != null)
329         {
330           group = tmpfeatures[index].featureGroup;
331           if (!allGroups.contains(group))
332           {
333             allGroups.addElement(group);
334             if (group != null)
335             {
336               checkGroupState(group);
337             }
338           }
339         }
340
341         if (!allFeatures.contains(tmpfeatures[index].getType()))
342         {
343           allFeatures.addElement(tmpfeatures[index].getType());
344         }
345         index++;
346       }
347     }
348
349     resetTable(null);
350
351     validate();
352   }
353
354   /**
355    * 
356    * @param group
357    * @return true if group has been seen before and is already added to set.
358    */
359   private boolean checkGroupState(String group)
360   {
361     boolean visible;
362     if (fr.featureGroups.containsKey(group))
363     {
364       visible = ((Boolean) fr.featureGroups.get(group)).booleanValue();
365     }
366     else
367     {
368       visible = true; // new group is always made visible
369     }
370
371     if (groupPanel == null)
372     {
373       groupPanel = new JPanel();
374     }
375
376     boolean alreadyAdded = false;
377     for (int g = 0; g < groupPanel.getComponentCount(); g++)
378     {
379       if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
380       {
381         alreadyAdded = true;
382         ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
383         break;
384       }
385     }
386
387     if (alreadyAdded)
388     {
389
390       return true;
391     }
392
393     fr.featureGroups.put(group, new Boolean(visible));
394     final String grp = group;
395     final JCheckBox check = new JCheckBox(group, visible);
396     check.setFont(new Font("Serif", Font.BOLD, 12));
397     check.addItemListener(new ItemListener()
398     {
399       public void itemStateChanged(ItemEvent evt)
400       {
401         fr.featureGroups.put(check.getText(), new Boolean(check
402                 .isSelected()));
403         af.alignPanel.seqPanel.seqCanvas.repaint();
404         if (af.alignPanel.overviewPanel != null)
405         {
406           af.alignPanel.overviewPanel.updateOverviewImage();
407         }
408
409         resetTable(new String[]
410         { grp });
411       }
412     });
413     groupPanel.add(check);
414     return false;
415   }
416
417   boolean resettingTable = false;
418
419   synchronized void resetTable(String[] groupChanged)
420   {
421     if (resettingTable == true)
422     {
423       return;
424     }
425     resettingTable = true;
426     typeWidth = new Hashtable();
427     // TODO: change avWidth calculation to 'per-sequence' average and use long
428     // rather than float
429     float[] avWidth = null;
430     SequenceFeature[] tmpfeatures;
431     String group = null, type;
432     Vector visibleChecks = new Vector();
433
434     // Find out which features should be visible depending on which groups
435     // are selected / deselected
436     // and recompute average width ordering
437     for (int i = 0; i < af.getViewport().alignment.getHeight(); i++)
438     {
439
440       tmpfeatures = af.getViewport().alignment.getSequenceAt(i)
441               .getDatasetSequence().getSequenceFeatures();
442       if (tmpfeatures == null)
443       {
444         continue;
445       }
446
447       int index = 0;
448       while (index < tmpfeatures.length)
449       {
450         group = tmpfeatures[index].featureGroup;
451
452         if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
453         {
454           index++;
455           continue;
456         }
457
458         if (group == null || fr.featureGroups.get(group) == null
459                 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
460         {
461           if (group != null)
462             checkGroupState(group);
463           type = tmpfeatures[index].getType();
464           if (!visibleChecks.contains(type))
465           {
466             visibleChecks.addElement(type);
467           }
468         }
469         if (!typeWidth.containsKey(tmpfeatures[index].getType()))
470         {
471           typeWidth.put(tmpfeatures[index].getType(),
472                   avWidth = new float[3]);
473         }
474         else
475         {
476           avWidth = (float[]) typeWidth.get(tmpfeatures[index].getType());
477         }
478         avWidth[0]++;
479         if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
480         {
481           avWidth[1] += 1 + tmpfeatures[index].getBegin()
482                   - tmpfeatures[index].getEnd();
483         }
484         else
485         {
486           avWidth[1] += 1 + tmpfeatures[index].getEnd()
487                   - tmpfeatures[index].getBegin();
488         }
489         index++;
490       }
491     }
492
493     int fSize = visibleChecks.size();
494     Object[][] data = new Object[fSize][3];
495     int dataIndex = 0;
496
497     if (fr.renderOrder != null)
498     {
499       if (!handlingUpdate)
500         fr.findAllFeatures(groupChanged != null); // prod to update
501       // colourschemes. but don't
502       // affect display
503       // First add the checks in the previous render order,
504       // in case the window has been closed and reopened
505       for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
506       {
507         type = fr.renderOrder[ro];
508
509         if (!visibleChecks.contains(type))
510         {
511           continue;
512         }
513
514         data[dataIndex][0] = type;
515         data[dataIndex][1] = fr.getFeatureStyle(type);
516         data[dataIndex][2] = new Boolean(af.getViewport().featuresDisplayed
517                 .containsKey(type));
518         dataIndex++;
519         visibleChecks.removeElement(type);
520       }
521     }
522
523     fSize = visibleChecks.size();
524     for (int i = 0; i < fSize; i++)
525     {
526       // These must be extra features belonging to the group
527       // which was just selected
528       type = visibleChecks.elementAt(i).toString();
529       data[dataIndex][0] = type;
530
531       data[dataIndex][1] = fr.getFeatureStyle(type);
532       if (data[dataIndex][1] == null)
533       {
534         // "Colour has been updated in another view!!"
535         fr.renderOrder = null;
536         return;
537       }
538
539       data[dataIndex][2] = new Boolean(true);
540       dataIndex++;
541     }
542
543     if (originalData == null)
544     {
545       originalData = new Object[data.length][3];
546       for (int i = 0; i < data.length; i++)
547       {
548         System.arraycopy(data[i], 0, originalData[i], 0, 3);
549       }
550     }
551
552     table.setModel(new FeatureTableModel(data));
553     table.getColumnModel().getColumn(0).setPreferredWidth(200);
554
555     if (groupPanel != null)
556     {
557       groupPanel.setLayout(new GridLayout(fr.featureGroups.size() / 4 + 1,
558               4));
559
560       groupPanel.validate();
561       bigPanel.add(groupPanel, BorderLayout.NORTH);
562     }
563
564     updateFeatureRenderer(data, groupChanged != null);
565     resettingTable = false;
566   }
567
568   /**
569    * reorder data based on the featureRenderers global priority list.
570    * 
571    * @param data
572    */
573   private void ensureOrder(Object[][] data)
574   {
575     boolean sort = false;
576     float[] order = new float[data.length];
577     for (int i = 0; i < order.length; i++)
578     {
579       order[i] = fr.getOrder(data[i][0].toString());
580       if (order[i] < 0)
581         order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
582       if (i > 1)
583         sort = sort || order[i - 1] > order[i];
584     }
585     if (sort)
586       jalview.util.QuickSort.sort(order, data);
587   }
588
589   void load()
590   {
591     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
592             .getProperty("LAST_DIRECTORY"), new String[]
593     { "fc" }, new String[]
594     { "Sequence Feature Colours" }, "Sequence Feature Colours");
595     chooser.setFileView(new jalview.io.JalviewFileView());
596     chooser.setDialogTitle("Load Feature Colours");
597     chooser.setToolTipText("Load");
598
599     int value = chooser.showOpenDialog(this);
600
601     if (value == JalviewFileChooser.APPROVE_OPTION)
602     {
603       File file = chooser.getSelectedFile();
604
605       try
606       {
607         InputStreamReader in = new InputStreamReader(new FileInputStream(
608                 file), "UTF-8");
609
610         jalview.schemabinding.version2.JalviewUserColours jucs = new jalview.schemabinding.version2.JalviewUserColours();
611         jucs = (jalview.schemabinding.version2.JalviewUserColours) jucs.unmarshal(in);
612         
613         for (int i = jucs.getColourCount() - 1; i >= 0; i--)
614         {
615           String name;
616           jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
617           if (newcol.hasMax())
618           {
619             Color mincol=null,maxcol=null;
620             try {
621               mincol = new Color(
622                       Integer.parseInt(newcol.getMinRGB(), 16));
623               maxcol = new Color(
624                       Integer.parseInt(newcol.getRGB(), 16));
625               
626             } catch (Exception e)
627             {
628               Cache.log.warn("Couldn't parse out graduated feature color.",e);
629             }
630             GraduatedColor gcol = 
631               new GraduatedColor(mincol,maxcol, 
632                   newcol.getMin(),newcol.getMax());
633             if (newcol.hasAutoScale())
634             {
635               gcol.setAutoScaled(newcol.getAutoScale());
636             }
637             if (newcol.hasColourByLabel())
638             {
639               gcol.setColourByLabel(newcol.getColourByLabel());
640             }
641             if (newcol.hasThreshold())
642             {
643               gcol.setThresh(newcol.getThreshold());
644               gcol.setThreshType(AnnotationColourGradient.NO_THRESHOLD); // default
645             }
646             if (newcol.getThreshType().length()>0)
647             {
648               String ttyp = newcol.getThreshType();
649               if (ttyp.equalsIgnoreCase("NONE")) {
650                 gcol.setThreshType(AnnotationColourGradient.NO_THRESHOLD);
651               }
652               if (ttyp.equalsIgnoreCase("ABOVE"))
653               {
654                 gcol.setThreshType(AnnotationColourGradient.ABOVE_THRESHOLD);
655               }
656               if (ttyp.equalsIgnoreCase("BELOW")){
657                 gcol.setThreshType(AnnotationColourGradient.BELOW_THRESHOLD);
658               } 
659             }
660             fr.setColour(name = newcol.getName(), gcol);
661           } else {
662             fr.setColour(name = jucs.getColour(i).getName(), new Color(
663                   Integer.parseInt(jucs.getColour(i).getRGB(), 16)));
664           }
665           fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
666         }
667         if (table != null)
668         {
669           resetTable(null);
670           Object[][] data = ((FeatureTableModel) table.getModel())
671                   .getData();
672           ensureOrder(data);
673           updateFeatureRenderer(data, false);
674           table.repaint();
675         }
676       } catch (Exception ex)
677       {
678         System.out.println("Error loading User Colour File\n" + ex);
679       }
680     }
681   }
682
683   void save()
684   {
685     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
686             .getProperty("LAST_DIRECTORY"), new String[]
687     { "fc" }, new String[]
688     { "Sequence Feature Colours" }, "Sequence Feature Colours");
689     chooser.setFileView(new jalview.io.JalviewFileView());
690     chooser.setDialogTitle("Save Feature Colour Scheme");
691     chooser.setToolTipText("Save");
692
693     int value = chooser.showSaveDialog(this);
694
695     if (value == JalviewFileChooser.APPROVE_OPTION)
696     {
697       String choice = chooser.getSelectedFile().getPath();
698       jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
699       ucs.setSchemeName("Sequence Features");
700       try
701       {
702         PrintWriter out = new PrintWriter(new OutputStreamWriter(
703                 new FileOutputStream(choice), "UTF-8"));
704
705         Enumeration e = fr.featureColours.keys();
706         float[] sortOrder = new float[fr.featureColours.size()];
707         String[] sortTypes = new String[fr.featureColours.size()];
708         int i = 0;
709         while (e.hasMoreElements())
710         {
711           sortTypes[i] = e.nextElement().toString();
712           sortOrder[i] = fr.getOrder(sortTypes[i]);
713           i++;
714         }
715         jalview.util.QuickSort.sort(sortOrder, sortTypes);
716         sortOrder = null;
717         Object fcol;
718         GraduatedColor gcol;
719         for (i = 0; i < sortTypes.length; i++)
720         {
721           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
722           col.setName(sortTypes[i]);
723           col.setRGB(jalview.util.Format.getHexString(fr.getColour(col
724                   .getName())));
725           fcol = fr.getFeatureStyle(sortTypes[i]);
726           if (fcol instanceof GraduatedColor)
727           {
728             gcol = (GraduatedColor) fcol;
729             col.setMin(gcol.getMin());
730             col.setMax(gcol.getMax());
731             col.setMinRGB(jalview.util.Format.getHexString(gcol
732                     .getMinColor()));
733             col.setAutoScale(gcol.isAutoScale());
734             col.setThreshold(gcol.getThresh());
735             col.setColourByLabel(gcol.isColourByLabel());
736             switch (gcol.getThreshType())
737             {
738             case AnnotationColourGradient.NO_THRESHOLD:
739               col.setThreshType("NONE");
740               break;
741             case AnnotationColourGradient.ABOVE_THRESHOLD:
742               col.setThreshType("ABOVE");
743               break;
744             case AnnotationColourGradient.BELOW_THRESHOLD:
745               col.setThreshType("BELOW");
746               break;
747             }
748           }
749           ucs.addColour(col);
750         }
751         ucs.marshal(out);
752         out.close();
753       } catch (Exception ex)
754       {
755         ex.printStackTrace();
756       }
757     }
758   }
759
760   public void invertSelection()
761   {
762     for (int i = 0; i < table.getRowCount(); i++)
763     {
764       Boolean value = (Boolean) table.getValueAt(i, 2);
765
766       table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
767     }
768   }
769
770   public void orderByAvWidth()
771   {
772     if (table == null || table.getModel() == null)
773       return;
774     Object[][] data = ((FeatureTableModel) table.getModel()).getData();
775     float[] width = new float[data.length];
776     float[] awidth;
777     float max = 0;
778     int num = 0;
779     for (int i = 0; i < data.length; i++)
780     {
781       awidth = (float[]) typeWidth.get(data[i][0]);
782       if (awidth[0] > 0)
783       {
784         width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
785         // weight - but have to make per
786         // sequence, too (awidth[2])
787         // if (width[i]==1) // hack to distinguish single width sequences.
788         num++;
789       }
790       else
791       {
792         width[i] = 0;
793       }
794       if (max < width[i])
795         max = width[i];
796     }
797     boolean sort = false;
798     for (int i = 0; i < width.length; i++)
799     {
800       // awidth = (float[]) typeWidth.get(data[i][0]);
801       if (width[i] == 0)
802       {
803         width[i] = fr.getOrder(data[i][0].toString());
804         if (width[i] < 0)
805         {
806           width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
807         }
808       }
809       else
810       {
811         width[i] /= max; // normalize
812         fr.setOrder(data[i][0].toString(), width[i]); // store for later
813       }
814       if (i > 0)
815         sort = sort || width[i - 1] > width[i];
816     }
817     if (sort)
818       jalview.util.QuickSort.sort(width, data);
819     // update global priority order
820
821     updateFeatureRenderer(data, false);
822     table.repaint();
823   }
824
825   public void close()
826   {
827     try
828     {
829       frame.setClosed(true);
830     } catch (Exception exe)
831     {
832     }
833
834   }
835
836   public void updateFeatureRenderer(Object[][] data)
837   {
838     updateFeatureRenderer(data, true);
839   }
840
841   private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
842   {
843     fr.setFeaturePriority(data, visibleNew);
844     af.alignPanel.paintAlignment(true);
845   }
846
847   int selectedRow = -1;
848
849   JTabbedPane tabbedPane = new JTabbedPane();
850
851   BorderLayout borderLayout1 = new BorderLayout();
852
853   BorderLayout borderLayout2 = new BorderLayout();
854
855   BorderLayout borderLayout3 = new BorderLayout();
856
857   JPanel bigPanel = new JPanel();
858
859   BorderLayout borderLayout4 = new BorderLayout();
860
861   JButton invert = new JButton();
862
863   JPanel buttonPanel = new JPanel();
864
865   JButton cancel = new JButton();
866
867   JButton ok = new JButton();
868
869   JButton loadColours = new JButton();
870
871   JButton saveColours = new JButton();
872
873   JPanel dasButtonPanel = new JPanel();
874
875   JButton fetchDAS = new JButton();
876
877   JButton saveDAS = new JButton();
878
879   JButton cancelDAS = new JButton();
880
881   JButton optimizeOrder = new JButton();
882
883   JButton sortByScore = new JButton();
884
885   JButton sortByDens = new JButton();
886
887   JPanel transbuttons = new JPanel(new GridLayout(4, 1));
888
889   private void jbInit() throws Exception
890   {
891     this.setLayout(borderLayout1);
892     settingsPane.setLayout(borderLayout2);
893     dasSettingsPane.setLayout(borderLayout3);
894     bigPanel.setLayout(borderLayout4);
895     invert.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
896     invert.setText("Invert Selection");
897     invert.addActionListener(new ActionListener()
898     {
899       public void actionPerformed(ActionEvent e)
900       {
901         invertSelection();
902       }
903     });
904     optimizeOrder.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
905     optimizeOrder.setText("Optimise Order");
906     optimizeOrder.addActionListener(new ActionListener()
907     {
908       public void actionPerformed(ActionEvent e)
909       {
910         orderByAvWidth();
911       }
912     });
913     sortByScore.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
914     sortByScore.setText("Seq sort by Score");
915     sortByScore.addActionListener(new ActionListener()
916     {
917       public void actionPerformed(ActionEvent e)
918       {
919         sortByScore(null);
920       }
921     });
922     sortByDens.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
923     sortByDens.setText("Seq Sort by density");
924     sortByDens.addActionListener(new ActionListener()
925     {
926       public void actionPerformed(ActionEvent e)
927       {
928         sortByDens(null);
929       }
930     });
931     cancel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
932     cancel.setText("Cancel");
933     cancel.addActionListener(new ActionListener()
934     {
935       public void actionPerformed(ActionEvent e)
936       {
937         updateFeatureRenderer(originalData);
938         close();
939       }
940     });
941     ok.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
942     ok.setText("OK");
943     ok.addActionListener(new ActionListener()
944     {
945       public void actionPerformed(ActionEvent e)
946       {
947         close();
948       }
949     });
950     loadColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
951     loadColours.setText("Load Colours");
952     loadColours.addActionListener(new ActionListener()
953     {
954       public void actionPerformed(ActionEvent e)
955       {
956         load();
957       }
958     });
959     saveColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
960     saveColours.setText("Save Colours");
961     saveColours.addActionListener(new ActionListener()
962     {
963       public void actionPerformed(ActionEvent e)
964       {
965         save();
966       }
967     });
968     transparency.addChangeListener(new ChangeListener()
969     {
970       public void stateChanged(ChangeEvent evt)
971       {
972         fr.setTransparency((float) (100 - transparency.getValue()) / 100f);
973         af.alignPanel.paintAlignment(true);
974       }
975     });
976
977     transparency.setMaximum(70);
978     fetchDAS.setText("Fetch DAS Features");
979     fetchDAS.addActionListener(new ActionListener()
980     {
981       public void actionPerformed(ActionEvent e)
982       {
983         fetchDAS_actionPerformed(e);
984       }
985     });
986     saveDAS.setText("Save as default");
987     saveDAS.addActionListener(new ActionListener()
988     {
989       public void actionPerformed(ActionEvent e)
990       {
991         saveDAS_actionPerformed(e);
992       }
993     });
994     dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
995     dasSettingsPane.setBorder(null);
996     cancelDAS.setEnabled(false);
997     cancelDAS.setText("Cancel Fetch");
998     cancelDAS.addActionListener(new ActionListener()
999     {
1000       public void actionPerformed(ActionEvent e)
1001       {
1002         cancelDAS_actionPerformed(e);
1003       }
1004     });
1005     this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1006     tabbedPane.addTab("Feature Settings", settingsPane);
1007     tabbedPane.addTab("DAS Settings", dasSettingsPane);
1008     bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1009     transbuttons.add(optimizeOrder);
1010     transbuttons.add(invert);
1011     transbuttons.add(sortByScore);
1012     transbuttons.add(sortByDens);
1013     transPanel.add(transparency);
1014     transPanel.add(transbuttons);
1015     buttonPanel.add(ok);
1016     buttonPanel.add(cancel);
1017     buttonPanel.add(loadColours);
1018     buttonPanel.add(saveColours);
1019     bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1020     dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1021     dasButtonPanel.add(fetchDAS);
1022     dasButtonPanel.add(cancelDAS);
1023     dasButtonPanel.add(saveDAS);
1024     settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1025     settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1026   }
1027
1028   protected void sortByDens(String[] typ)
1029   {
1030     sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
1031   }
1032
1033   protected void sortBy(String[] typ, String methodText, final String method)
1034   {
1035     if (typ == null)
1036     {
1037       typ = getDisplayedFeatureTypes();
1038     }
1039     String gps[] = null;
1040     gps = getDisplayedFeatureGroups();
1041     if (typ != null)
1042     {
1043       for (int i = 0; i < typ.length; i++)
1044       {
1045         System.err.println("Sorting on Types:" + typ[i]);
1046       }
1047     }
1048     if (gps != null)
1049     {
1050
1051       for (int i = 0; i < gps.length; i++)
1052       {
1053         System.err.println("Sorting on groups:" + gps[i]);
1054       }
1055     }
1056     AlignmentPanel alignPanel = af.alignPanel;
1057     AlignmentI al = alignPanel.av.getAlignment();
1058
1059     int start, stop;
1060     SequenceGroup sg = alignPanel.av.getSelectionGroup();
1061     if (sg != null)
1062     {
1063       start = sg.getStartRes();
1064       stop = sg.getEndRes();
1065     }
1066     else
1067     {
1068       start = 0;
1069       stop = al.getWidth();
1070     }
1071     SequenceI[] oldOrder = al.getSequencesArray();
1072     AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
1073     af.addHistoryItem(new OrderCommand(methodText, oldOrder, alignPanel.av
1074             .getAlignment()));
1075     alignPanel.paintAlignment(true);
1076
1077   }
1078
1079   protected void sortByScore(String[] typ)
1080   {
1081     sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
1082   }
1083
1084   private String[] getDisplayedFeatureTypes()
1085   {
1086     String[] typ = null;
1087     if (fr != null)
1088     {
1089       synchronized (fr.renderOrder)
1090       {
1091         typ = new String[fr.renderOrder.length];
1092         System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
1093         for (int i = 0; i < typ.length; i++)
1094         {
1095           if (af.viewport.featuresDisplayed.get(typ[i]) == null)
1096           {
1097             typ[i] = null;
1098           }
1099         }
1100       }
1101     }
1102     return typ;
1103   }
1104
1105   private String[] getDisplayedFeatureGroups()
1106   {
1107     String[] gps = null;
1108     if (fr != null)
1109     {
1110
1111       if (fr.featureGroups != null)
1112       {
1113         Enumeration en = fr.featureGroups.keys();
1114         gps = new String[fr.featureColours.size()];
1115         int g = 0;
1116         boolean valid = false;
1117         while (en.hasMoreElements())
1118         {
1119           String gp = (String) en.nextElement();
1120           Boolean on = (Boolean) fr.featureGroups.get(gp);
1121           if (on != null && on.booleanValue())
1122           {
1123             valid = true;
1124             gps[g++] = gp;
1125           }
1126         }
1127         while (g < gps.length)
1128         {
1129           gps[g++] = null;
1130         }
1131         if (!valid)
1132         {
1133           return null;
1134         }
1135       }
1136     }
1137     return gps;
1138   }
1139
1140   public void fetchDAS_actionPerformed(ActionEvent e)
1141   {
1142     fetchDAS.setEnabled(false);
1143     cancelDAS.setEnabled(true);
1144     Vector selectedSources = dassourceBrowser.getSelectedSources();
1145     doDasFeatureFetch(selectedSources, true, true);
1146   }
1147
1148   /**
1149    * get the features from selectedSources for all or the current selection
1150    * 
1151    * @param selectedSources
1152    * @param checkDbRefs
1153    * @param promptFetchDbRefs
1154    */
1155   private void doDasFeatureFetch(Vector selectedSources,
1156           boolean checkDbRefs, boolean promptFetchDbRefs)
1157   {
1158     SequenceI[] dataset, seqs;
1159     int iSize;
1160     AlignViewport vp = af.getViewport();
1161     if (vp.getSelectionGroup() != null
1162             && vp.getSelectionGroup().getSize() > 0)
1163     {
1164       iSize = vp.getSelectionGroup().getSize();
1165       dataset = new SequenceI[iSize];
1166       seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1167     }
1168     else
1169     {
1170       iSize = vp.getAlignment().getHeight();
1171       seqs = vp.getAlignment().getSequencesArray();
1172     }
1173
1174     dataset = new SequenceI[iSize];
1175     for (int i = 0; i < iSize; i++)
1176     {
1177       dataset[i] = seqs[i].getDatasetSequence();
1178     }
1179
1180     cancelDAS.setEnabled(true);
1181     dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1182             this, selectedSources, checkDbRefs, promptFetchDbRefs);
1183     af.getViewport().setShowSequenceFeatures(true);
1184     af.showSeqFeatures.setSelected(true);
1185   }
1186
1187   /**
1188    * blocking call to initialise the das source browser
1189    */
1190   public void initDasSources()
1191   {
1192     dassourceBrowser.initDasSources();
1193   }
1194
1195   /**
1196    * examine the current list of das sources and return any matching the given
1197    * nicknames in sources
1198    * 
1199    * @param sources
1200    *          Vector of Strings to resolve to DAS source nicknames.
1201    * @return sources that are present in source list.
1202    */
1203   public Vector resolveSourceNicknames(Vector sources)
1204   {
1205     return dassourceBrowser.resolveSourceNicknames(sources);
1206   }
1207
1208   /**
1209    * get currently selected das sources. ensure you have called initDasSources
1210    * before calling this.
1211    * 
1212    * @return vector of selected das source nicknames
1213    */
1214   public Vector getSelectedSources()
1215   {
1216     return dassourceBrowser.getSelectedSources();
1217   }
1218
1219   /**
1220    * properly initialise DAS fetcher and then initiate a new thread to fetch
1221    * features from the named sources (rather than any turned on by default)
1222    * 
1223    * @param sources
1224    * @param block
1225    *          if true then runs in same thread, otherwise passes to the Swing
1226    *          executor
1227    */
1228   public void fetchDasFeatures(Vector sources, boolean block)
1229   {
1230     initDasSources();
1231     Vector resolved = resolveSourceNicknames(sources);
1232     if (resolved.size() == 0)
1233     {
1234       resolved = dassourceBrowser.getSelectedSources();
1235     }
1236     if (resolved.size() > 0)
1237     {
1238       final Vector dassources = resolved;
1239       fetchDAS.setEnabled(false);
1240       // cancelDAS.setEnabled(true); doDasFetch does this.
1241       Runnable fetcher = new Runnable()
1242       {
1243
1244         public void run()
1245         {
1246           doDasFeatureFetch(dassources, true, false);
1247
1248         }
1249       };
1250       if (block)
1251       {
1252         fetcher.run();
1253       }
1254       else
1255       {
1256         SwingUtilities.invokeLater(fetcher);
1257       }
1258     }
1259   }
1260
1261   public void saveDAS_actionPerformed(ActionEvent e)
1262   {
1263     dassourceBrowser
1264             .saveProperties(jalview.bin.Cache.applicationProperties);
1265   }
1266
1267   public void complete()
1268   {
1269     fetchDAS.setEnabled(true);
1270     cancelDAS.setEnabled(false);
1271   }
1272
1273   public void cancelDAS_actionPerformed(ActionEvent e)
1274   {
1275     if (dasFeatureFetcher != null)
1276     {
1277       dasFeatureFetcher.cancel();
1278     }
1279     fetchDAS.setEnabled(true);
1280     cancelDAS.setEnabled(false);
1281   }
1282
1283   public void noDasSourceActive()
1284   {
1285     complete();
1286     JOptionPane.showInternalConfirmDialog(Desktop.desktop,
1287             "No das sources were selected.\n"
1288                     + "Please select some sources and\n" + " try again.",
1289             "No Sources Selected", JOptionPane.DEFAULT_OPTION,
1290             JOptionPane.INFORMATION_MESSAGE);
1291   }
1292
1293   // ///////////////////////////////////////////////////////////////////////
1294   // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1295   // ///////////////////////////////////////////////////////////////////////
1296   class FeatureTableModel extends AbstractTableModel
1297   {
1298     FeatureTableModel(Object[][] data)
1299     {
1300       this.data = data;
1301     }
1302
1303     private String[] columnNames =
1304     { "Feature Type", "Colour", "Display" };
1305
1306     private Object[][] data;
1307
1308     public Object[][] getData()
1309     {
1310       return data;
1311     }
1312
1313     public void setData(Object[][] data)
1314     {
1315       this.data = data;
1316     }
1317
1318     public int getColumnCount()
1319     {
1320       return columnNames.length;
1321     }
1322
1323     public Object[] getRow(int row)
1324     {
1325       return data[row];
1326     }
1327
1328     public int getRowCount()
1329     {
1330       return data.length;
1331     }
1332
1333     public String getColumnName(int col)
1334     {
1335       return columnNames[col];
1336     }
1337
1338     public Object getValueAt(int row, int col)
1339     {
1340       return data[row][col];
1341     }
1342
1343     public Class getColumnClass(int c)
1344     {
1345       return getValueAt(0, c).getClass();
1346     }
1347
1348     public boolean isCellEditable(int row, int col)
1349     {
1350       return col == 0 ? false : true;
1351     }
1352
1353     public void setValueAt(Object value, int row, int col)
1354     {
1355       data[row][col] = value;
1356       fireTableCellUpdated(row, col);
1357       updateFeatureRenderer(data);
1358     }
1359
1360   }
1361
1362   class ColorRenderer extends JLabel implements TableCellRenderer
1363   {
1364     javax.swing.border.Border unselectedBorder = null;
1365
1366     javax.swing.border.Border selectedBorder = null;
1367
1368     final String baseTT = "Click to edit, right/apple click for menu.";
1369
1370     public ColorRenderer()
1371     {
1372       setOpaque(true); // MUST do this for background to show up.
1373     }
1374
1375     public Component getTableCellRendererComponent(JTable table,
1376             Object color, boolean isSelected, boolean hasFocus, int row,
1377             int column)
1378     {
1379       // JLabel comp = new JLabel();
1380       // comp.
1381       setOpaque(true);
1382       // comp.
1383       // setBounds(getBounds());
1384       Color newColor;
1385       setToolTipText(baseTT);
1386       setBackground(table.getBackground());
1387       if (color instanceof GraduatedColor)
1388       {
1389         Rectangle cr = table.getCellRect(row, column, false);
1390         FeatureSettings.renderGraduatedColor(this, (GraduatedColor) color,
1391                 (int) cr.getWidth(), (int) cr.getHeight());
1392
1393       }
1394       else
1395       {
1396         this.setText("");
1397         this.setIcon(null);
1398         newColor = (Color) color;
1399         // comp.
1400         setBackground(newColor);
1401         // comp.setToolTipText("RGB value: " + newColor.getRed() + ", "
1402         // + newColor.getGreen() + ", " + newColor.getBlue());
1403       }
1404       if (isSelected)
1405       {
1406         if (selectedBorder == null)
1407         {
1408           selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1409                   table.getSelectionBackground());
1410         }
1411         // comp.
1412         setBorder(selectedBorder);
1413       }
1414       else
1415       {
1416         if (unselectedBorder == null)
1417         {
1418           unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1419                   table.getBackground());
1420         }
1421         // comp.
1422         setBorder(unselectedBorder);
1423       }
1424
1425       return this;
1426     }
1427   }
1428
1429   /**
1430    * update comp using rendering settings from gcol
1431    * 
1432    * @param comp
1433    * @param gcol
1434    */
1435   public static void renderGraduatedColor(JLabel comp, GraduatedColor gcol)
1436   {
1437     int w = comp.getWidth(), h = comp.getHeight();
1438     if (w < 20)
1439     {
1440       w = (int) comp.getPreferredSize().getWidth();
1441       h = (int) comp.getPreferredSize().getHeight();
1442       if (w < 20)
1443       {
1444         w = 80;
1445         h = 12;
1446       }
1447     }
1448     renderGraduatedColor(comp, gcol, w, h);
1449   }
1450
1451   public static void renderGraduatedColor(JLabel comp, GraduatedColor gcol,
1452           int w, int h)
1453   {
1454
1455     String tt = "";
1456     String tx = "";
1457     if (gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD)
1458     {
1459       tx += "> ";
1460       tt += "Thresholded (Above " + gcol.getThresh() + ") ";
1461     }
1462     if (gcol.getThreshType() == AnnotationColourGradient.BELOW_THRESHOLD)
1463     {
1464       tx += "< ";
1465       tt += "Thresholded (Below " + gcol.getThresh() + ") ";
1466     }
1467     if (gcol.isColourByLabel())
1468     {
1469       tt = "Coloured by label text. " + tt;
1470       tx += "Label";
1471       comp.setIcon(null);
1472     }
1473     else
1474     {
1475       Color newColor = gcol.getMaxColor();
1476       comp.setBackground(newColor);
1477       System.err.println("Width is " + w / 2);
1478       Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w / 2, h);
1479       comp.setIcon(ficon);
1480       // tt+="RGB value: Max (" + newColor.getRed() + ", "
1481       // + newColor.getGreen() + ", " + newColor.getBlue()
1482       // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1483       // + ", " + minCol.getBlue() + ")");
1484     }
1485     comp.setText(tx);
1486     if (tt.length() > 0)
1487     {
1488       if (comp.getToolTipText() == null)
1489       {
1490         comp.setToolTipText(tt);
1491       }
1492       else
1493       {
1494         comp.setToolTipText(tt + " " + comp.getToolTipText());
1495       }
1496     }
1497   }
1498 }
1499
1500 class FeatureIcon implements Icon
1501 {
1502   GraduatedColor gcol;
1503
1504   Color backg;
1505
1506   int width = 50, height = 20;
1507
1508   FeatureIcon(GraduatedColor gfc, Color bg, int w, int h)
1509   {
1510     gcol = gfc;
1511     backg = bg;
1512     width = w;
1513     height = h;
1514   }
1515
1516   public int getIconWidth()
1517   {
1518     return width;
1519   }
1520
1521   public int getIconHeight()
1522   {
1523     return height;
1524   }
1525
1526   public void paintIcon(Component c, Graphics g, int x, int y)
1527   {
1528
1529     if (gcol.isColourByLabel())
1530     {
1531       g.setColor(backg);
1532       g.fillRect(0, 0, width, height);
1533       // need an icon here.
1534       g.setColor(gcol.getMaxColor());
1535
1536       g.setFont(new Font("Verdana", Font.PLAIN, 9));
1537
1538       // g.setFont(g.getFont().deriveFont(
1539       // AffineTransform.getScaleInstance(
1540       // width/g.getFontMetrics().stringWidth("Label"),
1541       // height/g.getFontMetrics().getHeight())));
1542
1543       g.drawString("Label", 0, 0);
1544
1545     }
1546     else
1547     {
1548       Color minCol = gcol.getMinColor();
1549       g.setColor(minCol);
1550       g.fillRect(0, 0, width, height);
1551     }
1552   }
1553 }
1554
1555 class ColorEditor extends AbstractCellEditor implements TableCellEditor,
1556         ActionListener
1557 {
1558   FeatureSettings me;
1559
1560   GraduatedColor currentGColor;
1561
1562   FeatureColourChooser chooser;
1563
1564   String type;
1565
1566   Color currentColor;
1567
1568   JButton button;
1569
1570   JColorChooser colorChooser;
1571
1572   JDialog dialog;
1573
1574   protected static final String EDIT = "edit";
1575
1576   int selectedRow = 0;
1577
1578   public ColorEditor(FeatureSettings me)
1579   {
1580     this.me = me;
1581     // Set up the editor (from the table's point of view),
1582     // which is a button.
1583     // This button brings up the color chooser dialog,
1584     // which is the editor from the user's point of view.
1585     button = new JButton();
1586     button.setActionCommand(EDIT);
1587     button.addActionListener(this);
1588     button.setBorderPainted(false);
1589     // Set up the dialog that the button brings up.
1590     colorChooser = new JColorChooser();
1591     dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1592             colorChooser, this, // OK button handler
1593             null); // no CANCEL button handler
1594   }
1595
1596   /**
1597    * Handles events from the editor button and from the dialog's OK button.
1598    */
1599   public void actionPerformed(ActionEvent e)
1600   {
1601
1602     if (EDIT.equals(e.getActionCommand()))
1603     {
1604       // The user has clicked the cell, so
1605       // bring up the dialog.
1606       if (currentColor != null)
1607       {
1608         // bring up simple color chooser
1609         button.setBackground(currentColor);
1610         colorChooser.setColor(currentColor);
1611         dialog.setVisible(true);
1612       }
1613       else
1614       {
1615         // bring up graduated chooser.
1616         chooser = new FeatureColourChooser(me.fr, type);
1617         chooser.setRequestFocusEnabled(true);
1618         chooser.requestFocus();
1619         chooser.addActionListener(this);
1620       }
1621       // Make the renderer reappear.
1622       fireEditingStopped();
1623
1624     }
1625     else
1626     { // User pressed dialog's "OK" button.
1627       if (currentColor != null)
1628       {
1629         currentColor = colorChooser.getColor();
1630       }
1631       else
1632       {
1633         // class cast exceptions may be raised if the chooser created on a
1634         // non-graduated color
1635         currentGColor = (GraduatedColor) chooser.getLastColour();
1636       }
1637       me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1638       fireEditingStopped();
1639       me.table.validate();
1640     }
1641   }
1642
1643   // Implement the one CellEditor method that AbstractCellEditor doesn't.
1644   public Object getCellEditorValue()
1645   {
1646     if (currentColor == null)
1647     {
1648       return currentGColor;
1649     }
1650     return currentColor;
1651   }
1652
1653   // Implement the one method defined by TableCellEditor.
1654   public Component getTableCellEditorComponent(JTable table, Object value,
1655           boolean isSelected, int row, int column)
1656   {
1657     currentGColor = null;
1658     currentColor = null;
1659     this.selectedRow = row;
1660     type = me.table.getValueAt(row, 0).toString();
1661     button.setOpaque(true);
1662     button.setBackground(me.getBackground());
1663     if (value instanceof GraduatedColor)
1664     {
1665       currentGColor = (GraduatedColor) value;
1666       JLabel btn = new JLabel();
1667       btn.setSize(button.getSize());
1668       FeatureSettings.renderGraduatedColor(btn, currentGColor);
1669       button.setBackground(btn.getBackground());
1670       button.setIcon(btn.getIcon());
1671       button.setText(btn.getText());
1672     }
1673     else
1674     {
1675       button.setText("");
1676       button.setIcon(null);
1677       currentColor = (Color) value;
1678       button.setBackground(currentColor);
1679     }
1680     return button;
1681   }
1682 }