load and save graduated feature colours in XML
[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             float thrsh=Float.NaN;
621             try {
622               mincol = new Color(
623                       Integer.parseInt(newcol.getMinRGB(), 16));
624               maxcol = new Color(
625                       Integer.parseInt(newcol.getRGB(), 16));
626               
627             } catch (Exception e)
628             {
629               Cache.log.warn("Couldn't parse out graduated feature color.",e);
630             }
631             GraduatedColor gcol = 
632               new GraduatedColor(mincol,maxcol, 
633                   newcol.getMin(),newcol.getMax());
634             if (newcol.hasAutoScale())
635             {
636               gcol.setAutoScaled(newcol.getAutoScale());
637             }
638             if (newcol.hasColourByLabel())
639             {
640               gcol.setColourByLabel(newcol.getColourByLabel());
641             }
642             if (newcol.hasThreshold())
643             {
644               gcol.setThresh(newcol.getThreshold());
645               gcol.setThreshType(AnnotationColourGradient.NO_THRESHOLD); // default
646             }
647             if (newcol.getThreshType().length()>0)
648             {
649               String ttyp = newcol.getThreshType();
650               if (ttyp.equalsIgnoreCase("NONE")) {
651                 gcol.setThreshType(AnnotationColourGradient.NO_THRESHOLD);
652               }
653               if (ttyp.equalsIgnoreCase("ABOVE"))
654               {
655                 gcol.setThreshType(AnnotationColourGradient.ABOVE_THRESHOLD);
656               }
657               if (ttyp.equalsIgnoreCase("BELOW")){
658                 gcol.setThreshType(AnnotationColourGradient.BELOW_THRESHOLD);
659               } 
660             }
661             fr.setColour(name = newcol.getName(), gcol);
662           } else {
663             fr.setColour(name = jucs.getColour(i).getName(), new Color(
664                   Integer.parseInt(jucs.getColour(i).getRGB(), 16)));
665           }
666           fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
667         }
668         if (table != null)
669         {
670           resetTable(null);
671           Object[][] data = ((FeatureTableModel) table.getModel())
672                   .getData();
673           ensureOrder(data);
674           updateFeatureRenderer(data, false);
675           table.repaint();
676         }
677       } catch (Exception ex)
678       {
679         System.out.println("Error loading User Colour File\n" + ex);
680       }
681     }
682   }
683
684   void save()
685   {
686     JalviewFileChooser chooser = new JalviewFileChooser(jalview.bin.Cache
687             .getProperty("LAST_DIRECTORY"), new String[]
688     { "fc" }, new String[]
689     { "Sequence Feature Colours" }, "Sequence Feature Colours");
690     chooser.setFileView(new jalview.io.JalviewFileView());
691     chooser.setDialogTitle("Save Feature Colour Scheme");
692     chooser.setToolTipText("Save");
693
694     int value = chooser.showSaveDialog(this);
695
696     if (value == JalviewFileChooser.APPROVE_OPTION)
697     {
698       String choice = chooser.getSelectedFile().getPath();
699       jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
700       ucs.setSchemeName("Sequence Features");
701       try
702       {
703         PrintWriter out = new PrintWriter(new OutputStreamWriter(
704                 new FileOutputStream(choice), "UTF-8"));
705
706         Enumeration e = fr.featureColours.keys();
707         float[] sortOrder = new float[fr.featureColours.size()];
708         String[] sortTypes = new String[fr.featureColours.size()];
709         int i = 0;
710         while (e.hasMoreElements())
711         {
712           sortTypes[i] = e.nextElement().toString();
713           sortOrder[i] = fr.getOrder(sortTypes[i]);
714           i++;
715         }
716         jalview.util.QuickSort.sort(sortOrder, sortTypes);
717         sortOrder = null;
718         Object fcol;
719         GraduatedColor gcol;
720         for (i = 0; i < sortTypes.length; i++)
721         {
722           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
723           col.setName(sortTypes[i]);
724           col.setRGB(jalview.util.Format.getHexString(fr.getColour(col
725                   .getName())));
726           fcol = fr.getFeatureStyle(sortTypes[i]);
727           if (fcol instanceof GraduatedColor)
728           {
729             gcol = (GraduatedColor) fcol;
730             col.setMin(gcol.getMin());
731             col.setMax(gcol.getMax());
732             col.setMinRGB(jalview.util.Format.getHexString(gcol
733                     .getMinColor()));
734             col.setAutoScale(gcol.isAutoScale());
735             col.setThreshold(gcol.getThresh());
736             col.setColourByLabel(gcol.isColourByLabel());
737             switch (gcol.getThreshType())
738             {
739             case AnnotationColourGradient.NO_THRESHOLD:
740               col.setThreshType("NONE");
741               break;
742             case AnnotationColourGradient.ABOVE_THRESHOLD:
743               col.setThreshType("ABOVE");
744               break;
745             case AnnotationColourGradient.BELOW_THRESHOLD:
746               col.setThreshType("BELOW");
747               break;
748             }
749           }
750           ucs.addColour(col);
751         }
752         ucs.marshal(out);
753         out.close();
754       } catch (Exception ex)
755       {
756         ex.printStackTrace();
757       }
758     }
759   }
760
761   public void invertSelection()
762   {
763     for (int i = 0; i < table.getRowCount(); i++)
764     {
765       Boolean value = (Boolean) table.getValueAt(i, 2);
766
767       table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
768     }
769   }
770
771   public void orderByAvWidth()
772   {
773     if (table == null || table.getModel() == null)
774       return;
775     Object[][] data = ((FeatureTableModel) table.getModel()).getData();
776     float[] width = new float[data.length];
777     float[] awidth;
778     float max = 0;
779     int num = 0;
780     for (int i = 0; i < data.length; i++)
781     {
782       awidth = (float[]) typeWidth.get(data[i][0]);
783       if (awidth[0] > 0)
784       {
785         width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
786         // weight - but have to make per
787         // sequence, too (awidth[2])
788         // if (width[i]==1) // hack to distinguish single width sequences.
789         num++;
790       }
791       else
792       {
793         width[i] = 0;
794       }
795       if (max < width[i])
796         max = width[i];
797     }
798     boolean sort = false;
799     for (int i = 0; i < width.length; i++)
800     {
801       // awidth = (float[]) typeWidth.get(data[i][0]);
802       if (width[i] == 0)
803       {
804         width[i] = fr.getOrder(data[i][0].toString());
805         if (width[i] < 0)
806         {
807           width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
808         }
809       }
810       else
811       {
812         width[i] /= max; // normalize
813         fr.setOrder(data[i][0].toString(), width[i]); // store for later
814       }
815       if (i > 0)
816         sort = sort || width[i - 1] > width[i];
817     }
818     if (sort)
819       jalview.util.QuickSort.sort(width, data);
820     // update global priority order
821
822     updateFeatureRenderer(data, false);
823     table.repaint();
824   }
825
826   public void close()
827   {
828     try
829     {
830       frame.setClosed(true);
831     } catch (Exception exe)
832     {
833     }
834
835   }
836
837   public void updateFeatureRenderer(Object[][] data)
838   {
839     updateFeatureRenderer(data, true);
840   }
841
842   private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
843   {
844     fr.setFeaturePriority(data, visibleNew);
845     af.alignPanel.paintAlignment(true);
846   }
847
848   int selectedRow = -1;
849
850   JTabbedPane tabbedPane = new JTabbedPane();
851
852   BorderLayout borderLayout1 = new BorderLayout();
853
854   BorderLayout borderLayout2 = new BorderLayout();
855
856   BorderLayout borderLayout3 = new BorderLayout();
857
858   JPanel bigPanel = new JPanel();
859
860   BorderLayout borderLayout4 = new BorderLayout();
861
862   JButton invert = new JButton();
863
864   JPanel buttonPanel = new JPanel();
865
866   JButton cancel = new JButton();
867
868   JButton ok = new JButton();
869
870   JButton loadColours = new JButton();
871
872   JButton saveColours = new JButton();
873
874   JPanel dasButtonPanel = new JPanel();
875
876   JButton fetchDAS = new JButton();
877
878   JButton saveDAS = new JButton();
879
880   JButton cancelDAS = new JButton();
881
882   JButton optimizeOrder = new JButton();
883
884   JButton sortByScore = new JButton();
885
886   JButton sortByDens = new JButton();
887
888   JPanel transbuttons = new JPanel(new GridLayout(4, 1));
889
890   private void jbInit() throws Exception
891   {
892     this.setLayout(borderLayout1);
893     settingsPane.setLayout(borderLayout2);
894     dasSettingsPane.setLayout(borderLayout3);
895     bigPanel.setLayout(borderLayout4);
896     invert.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
897     invert.setText("Invert Selection");
898     invert.addActionListener(new ActionListener()
899     {
900       public void actionPerformed(ActionEvent e)
901       {
902         invertSelection();
903       }
904     });
905     optimizeOrder.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
906     optimizeOrder.setText("Optimise Order");
907     optimizeOrder.addActionListener(new ActionListener()
908     {
909       public void actionPerformed(ActionEvent e)
910       {
911         orderByAvWidth();
912       }
913     });
914     sortByScore.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
915     sortByScore.setText("Seq sort by Score");
916     sortByScore.addActionListener(new ActionListener()
917     {
918       public void actionPerformed(ActionEvent e)
919       {
920         sortByScore(null);
921       }
922     });
923     sortByDens.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
924     sortByDens.setText("Seq Sort by density");
925     sortByDens.addActionListener(new ActionListener()
926     {
927       public void actionPerformed(ActionEvent e)
928       {
929         sortByDens(null);
930       }
931     });
932     cancel.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
933     cancel.setText("Cancel");
934     cancel.addActionListener(new ActionListener()
935     {
936       public void actionPerformed(ActionEvent e)
937       {
938         updateFeatureRenderer(originalData);
939         close();
940       }
941     });
942     ok.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
943     ok.setText("OK");
944     ok.addActionListener(new ActionListener()
945     {
946       public void actionPerformed(ActionEvent e)
947       {
948         close();
949       }
950     });
951     loadColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
952     loadColours.setText("Load Colours");
953     loadColours.addActionListener(new ActionListener()
954     {
955       public void actionPerformed(ActionEvent e)
956       {
957         load();
958       }
959     });
960     saveColours.setFont(new java.awt.Font("Verdana", Font.PLAIN, 11));
961     saveColours.setText("Save Colours");
962     saveColours.addActionListener(new ActionListener()
963     {
964       public void actionPerformed(ActionEvent e)
965       {
966         save();
967       }
968     });
969     transparency.addChangeListener(new ChangeListener()
970     {
971       public void stateChanged(ChangeEvent evt)
972       {
973         fr.setTransparency((float) (100 - transparency.getValue()) / 100f);
974         af.alignPanel.paintAlignment(true);
975       }
976     });
977
978     transparency.setMaximum(70);
979     fetchDAS.setText("Fetch DAS Features");
980     fetchDAS.addActionListener(new ActionListener()
981     {
982       public void actionPerformed(ActionEvent e)
983       {
984         fetchDAS_actionPerformed(e);
985       }
986     });
987     saveDAS.setText("Save as default");
988     saveDAS.addActionListener(new ActionListener()
989     {
990       public void actionPerformed(ActionEvent e)
991       {
992         saveDAS_actionPerformed(e);
993       }
994     });
995     dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
996     dasSettingsPane.setBorder(null);
997     cancelDAS.setEnabled(false);
998     cancelDAS.setText("Cancel Fetch");
999     cancelDAS.addActionListener(new ActionListener()
1000     {
1001       public void actionPerformed(ActionEvent e)
1002       {
1003         cancelDAS_actionPerformed(e);
1004       }
1005     });
1006     this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1007     tabbedPane.addTab("Feature Settings", settingsPane);
1008     tabbedPane.addTab("DAS Settings", dasSettingsPane);
1009     bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1010     transbuttons.add(optimizeOrder);
1011     transbuttons.add(invert);
1012     transbuttons.add(sortByScore);
1013     transbuttons.add(sortByDens);
1014     transPanel.add(transparency);
1015     transPanel.add(transbuttons);
1016     buttonPanel.add(ok);
1017     buttonPanel.add(cancel);
1018     buttonPanel.add(loadColours);
1019     buttonPanel.add(saveColours);
1020     bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1021     dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1022     dasButtonPanel.add(fetchDAS);
1023     dasButtonPanel.add(cancelDAS);
1024     dasButtonPanel.add(saveDAS);
1025     settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1026     settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1027   }
1028
1029   protected void sortByDens(String[] typ)
1030   {
1031     sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
1032   }
1033
1034   protected void sortBy(String[] typ, String methodText, final String method)
1035   {
1036     if (typ == null)
1037     {
1038       typ = getDisplayedFeatureTypes();
1039     }
1040     String gps[] = null;
1041     gps = getDisplayedFeatureGroups();
1042     if (typ != null)
1043     {
1044       for (int i = 0; i < typ.length; i++)
1045       {
1046         System.err.println("Sorting on Types:" + typ[i]);
1047       }
1048     }
1049     if (gps != null)
1050     {
1051
1052       for (int i = 0; i < gps.length; i++)
1053       {
1054         System.err.println("Sorting on groups:" + gps[i]);
1055       }
1056     }
1057     AlignmentPanel alignPanel = af.alignPanel;
1058     AlignmentI al = alignPanel.av.getAlignment();
1059
1060     int start, stop;
1061     SequenceGroup sg = alignPanel.av.getSelectionGroup();
1062     if (sg != null)
1063     {
1064       start = sg.getStartRes();
1065       stop = sg.getEndRes();
1066     }
1067     else
1068     {
1069       start = 0;
1070       stop = al.getWidth();
1071     }
1072     SequenceI[] oldOrder = al.getSequencesArray();
1073     AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
1074     af.addHistoryItem(new OrderCommand(methodText, oldOrder, alignPanel.av
1075             .getAlignment()));
1076     alignPanel.paintAlignment(true);
1077
1078   }
1079
1080   protected void sortByScore(String[] typ)
1081   {
1082     sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
1083   }
1084
1085   private String[] getDisplayedFeatureTypes()
1086   {
1087     String[] typ = null;
1088     if (fr != null)
1089     {
1090       synchronized (fr.renderOrder)
1091       {
1092         typ = new String[fr.renderOrder.length];
1093         System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
1094         for (int i = 0; i < typ.length; i++)
1095         {
1096           if (af.viewport.featuresDisplayed.get(typ[i]) == null)
1097           {
1098             typ[i] = null;
1099           }
1100         }
1101       }
1102     }
1103     return typ;
1104   }
1105
1106   private String[] getDisplayedFeatureGroups()
1107   {
1108     String[] gps = null;
1109     if (fr != null)
1110     {
1111
1112       if (fr.featureGroups != null)
1113       {
1114         Enumeration en = fr.featureGroups.keys();
1115         gps = new String[fr.featureColours.size()];
1116         int g = 0;
1117         boolean valid = false;
1118         while (en.hasMoreElements())
1119         {
1120           String gp = (String) en.nextElement();
1121           Boolean on = (Boolean) fr.featureGroups.get(gp);
1122           if (on != null && on.booleanValue())
1123           {
1124             valid = true;
1125             gps[g++] = gp;
1126           }
1127         }
1128         while (g < gps.length)
1129         {
1130           gps[g++] = null;
1131         }
1132         if (!valid)
1133         {
1134           return null;
1135         }
1136       }
1137     }
1138     return gps;
1139   }
1140
1141   public void fetchDAS_actionPerformed(ActionEvent e)
1142   {
1143     fetchDAS.setEnabled(false);
1144     cancelDAS.setEnabled(true);
1145     Vector selectedSources = dassourceBrowser.getSelectedSources();
1146     doDasFeatureFetch(selectedSources, true, true);
1147   }
1148
1149   /**
1150    * get the features from selectedSources for all or the current selection
1151    * 
1152    * @param selectedSources
1153    * @param checkDbRefs
1154    * @param promptFetchDbRefs
1155    */
1156   private void doDasFeatureFetch(Vector selectedSources,
1157           boolean checkDbRefs, boolean promptFetchDbRefs)
1158   {
1159     SequenceI[] dataset, seqs;
1160     int iSize;
1161     AlignViewport vp = af.getViewport();
1162     if (vp.getSelectionGroup() != null
1163             && vp.getSelectionGroup().getSize() > 0)
1164     {
1165       iSize = vp.getSelectionGroup().getSize();
1166       dataset = new SequenceI[iSize];
1167       seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1168     }
1169     else
1170     {
1171       iSize = vp.getAlignment().getHeight();
1172       seqs = vp.getAlignment().getSequencesArray();
1173     }
1174
1175     dataset = new SequenceI[iSize];
1176     for (int i = 0; i < iSize; i++)
1177     {
1178       dataset[i] = seqs[i].getDatasetSequence();
1179     }
1180
1181     cancelDAS.setEnabled(true);
1182     dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1183             this, selectedSources, checkDbRefs, promptFetchDbRefs);
1184     af.getViewport().setShowSequenceFeatures(true);
1185     af.showSeqFeatures.setSelected(true);
1186   }
1187
1188   /**
1189    * blocking call to initialise the das source browser
1190    */
1191   public void initDasSources()
1192   {
1193     dassourceBrowser.initDasSources();
1194   }
1195
1196   /**
1197    * examine the current list of das sources and return any matching the given
1198    * nicknames in sources
1199    * 
1200    * @param sources
1201    *          Vector of Strings to resolve to DAS source nicknames.
1202    * @return sources that are present in source list.
1203    */
1204   public Vector resolveSourceNicknames(Vector sources)
1205   {
1206     return dassourceBrowser.resolveSourceNicknames(sources);
1207   }
1208
1209   /**
1210    * get currently selected das sources. ensure you have called initDasSources
1211    * before calling this.
1212    * 
1213    * @return vector of selected das source nicknames
1214    */
1215   public Vector getSelectedSources()
1216   {
1217     return dassourceBrowser.getSelectedSources();
1218   }
1219
1220   /**
1221    * properly initialise DAS fetcher and then initiate a new thread to fetch
1222    * features from the named sources (rather than any turned on by default)
1223    * 
1224    * @param sources
1225    * @param block
1226    *          if true then runs in same thread, otherwise passes to the Swing
1227    *          executor
1228    */
1229   public void fetchDasFeatures(Vector sources, boolean block)
1230   {
1231     initDasSources();
1232     Vector resolved = resolveSourceNicknames(sources);
1233     if (resolved.size() == 0)
1234     {
1235       resolved = dassourceBrowser.getSelectedSources();
1236     }
1237     if (resolved.size() > 0)
1238     {
1239       final Vector dassources = resolved;
1240       fetchDAS.setEnabled(false);
1241       // cancelDAS.setEnabled(true); doDasFetch does this.
1242       Runnable fetcher = new Runnable()
1243       {
1244
1245         public void run()
1246         {
1247           doDasFeatureFetch(dassources, true, false);
1248
1249         }
1250       };
1251       if (block)
1252       {
1253         fetcher.run();
1254       }
1255       else
1256       {
1257         SwingUtilities.invokeLater(fetcher);
1258       }
1259     }
1260   }
1261
1262   public void saveDAS_actionPerformed(ActionEvent e)
1263   {
1264     dassourceBrowser
1265             .saveProperties(jalview.bin.Cache.applicationProperties);
1266   }
1267
1268   public void complete()
1269   {
1270     fetchDAS.setEnabled(true);
1271     cancelDAS.setEnabled(false);
1272   }
1273
1274   public void cancelDAS_actionPerformed(ActionEvent e)
1275   {
1276     if (dasFeatureFetcher != null)
1277     {
1278       dasFeatureFetcher.cancel();
1279     }
1280     fetchDAS.setEnabled(true);
1281     cancelDAS.setEnabled(false);
1282   }
1283
1284   public void noDasSourceActive()
1285   {
1286     complete();
1287     JOptionPane.showInternalConfirmDialog(Desktop.desktop,
1288             "No das sources were selected.\n"
1289                     + "Please select some sources and\n" + " try again.",
1290             "No Sources Selected", JOptionPane.DEFAULT_OPTION,
1291             JOptionPane.INFORMATION_MESSAGE);
1292   }
1293
1294   // ///////////////////////////////////////////////////////////////////////
1295   // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1296   // ///////////////////////////////////////////////////////////////////////
1297   class FeatureTableModel extends AbstractTableModel
1298   {
1299     FeatureTableModel(Object[][] data)
1300     {
1301       this.data = data;
1302     }
1303
1304     private String[] columnNames =
1305     { "Feature Type", "Colour", "Display" };
1306
1307     private Object[][] data;
1308
1309     public Object[][] getData()
1310     {
1311       return data;
1312     }
1313
1314     public void setData(Object[][] data)
1315     {
1316       this.data = data;
1317     }
1318
1319     public int getColumnCount()
1320     {
1321       return columnNames.length;
1322     }
1323
1324     public Object[] getRow(int row)
1325     {
1326       return data[row];
1327     }
1328
1329     public int getRowCount()
1330     {
1331       return data.length;
1332     }
1333
1334     public String getColumnName(int col)
1335     {
1336       return columnNames[col];
1337     }
1338
1339     public Object getValueAt(int row, int col)
1340     {
1341       return data[row][col];
1342     }
1343
1344     public Class getColumnClass(int c)
1345     {
1346       return getValueAt(0, c).getClass();
1347     }
1348
1349     public boolean isCellEditable(int row, int col)
1350     {
1351       return col == 0 ? false : true;
1352     }
1353
1354     public void setValueAt(Object value, int row, int col)
1355     {
1356       data[row][col] = value;
1357       fireTableCellUpdated(row, col);
1358       updateFeatureRenderer(data);
1359     }
1360
1361   }
1362
1363   class ColorRenderer extends JLabel implements TableCellRenderer
1364   {
1365     javax.swing.border.Border unselectedBorder = null;
1366
1367     javax.swing.border.Border selectedBorder = null;
1368
1369     final String baseTT = "Click to edit, right/apple click for menu.";
1370
1371     public ColorRenderer()
1372     {
1373       setOpaque(true); // MUST do this for background to show up.
1374     }
1375
1376     public Component getTableCellRendererComponent(JTable table,
1377             Object color, boolean isSelected, boolean hasFocus, int row,
1378             int column)
1379     {
1380       // JLabel comp = new JLabel();
1381       // comp.
1382       setOpaque(true);
1383       // comp.
1384       // setBounds(getBounds());
1385       Color newColor;
1386       setToolTipText(baseTT);
1387       setBackground(table.getBackground());
1388       if (color instanceof GraduatedColor)
1389       {
1390         Rectangle cr = table.getCellRect(row, column, false);
1391         FeatureSettings.renderGraduatedColor(this, (GraduatedColor) color,
1392                 (int) cr.getWidth(), (int) cr.getHeight());
1393
1394       }
1395       else
1396       {
1397         this.setText("");
1398         this.setIcon(null);
1399         newColor = (Color) color;
1400         // comp.
1401         setBackground(newColor);
1402         // comp.setToolTipText("RGB value: " + newColor.getRed() + ", "
1403         // + newColor.getGreen() + ", " + newColor.getBlue());
1404       }
1405       if (isSelected)
1406       {
1407         if (selectedBorder == null)
1408         {
1409           selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1410                   table.getSelectionBackground());
1411         }
1412         // comp.
1413         setBorder(selectedBorder);
1414       }
1415       else
1416       {
1417         if (unselectedBorder == null)
1418         {
1419           unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1420                   table.getBackground());
1421         }
1422         // comp.
1423         setBorder(unselectedBorder);
1424       }
1425
1426       return this;
1427     }
1428   }
1429
1430   /**
1431    * update comp using rendering settings from gcol
1432    * 
1433    * @param comp
1434    * @param gcol
1435    */
1436   public static void renderGraduatedColor(JLabel comp, GraduatedColor gcol)
1437   {
1438     int w = comp.getWidth(), h = comp.getHeight();
1439     if (w < 20)
1440     {
1441       w = (int) comp.getPreferredSize().getWidth();
1442       h = (int) comp.getPreferredSize().getHeight();
1443       if (w < 20)
1444       {
1445         w = 80;
1446         h = 12;
1447       }
1448     }
1449     renderGraduatedColor(comp, gcol, w, h);
1450   }
1451
1452   public static void renderGraduatedColor(JLabel comp, GraduatedColor gcol,
1453           int w, int h)
1454   {
1455
1456     String tt = "";
1457     String tx = "";
1458     if (gcol.getThreshType() == AnnotationColourGradient.ABOVE_THRESHOLD)
1459     {
1460       tx += "> ";
1461       tt += "Thresholded (Above " + gcol.getThresh() + ") ";
1462     }
1463     if (gcol.getThreshType() == AnnotationColourGradient.BELOW_THRESHOLD)
1464     {
1465       tx += "< ";
1466       tt += "Thresholded (Below " + gcol.getThresh() + ") ";
1467     }
1468     if (gcol.isColourByLabel())
1469     {
1470       tt = "Coloured by label text. " + tt;
1471       tx += "Label";
1472       comp.setIcon(null);
1473     }
1474     else
1475     {
1476       Color newColor = gcol.getMaxColor();
1477       comp.setBackground(newColor);
1478       System.err.println("Width is " + w / 2);
1479       Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w / 2, h);
1480       comp.setIcon(ficon);
1481       // tt+="RGB value: Max (" + newColor.getRed() + ", "
1482       // + newColor.getGreen() + ", " + newColor.getBlue()
1483       // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1484       // + ", " + minCol.getBlue() + ")");
1485     }
1486     comp.setText(tx);
1487     if (tt.length() > 0)
1488     {
1489       if (comp.getToolTipText() == null)
1490       {
1491         comp.setToolTipText(tt);
1492       }
1493       else
1494       {
1495         comp.setToolTipText(tt + " " + comp.getToolTipText());
1496       }
1497     }
1498   }
1499 }
1500
1501 class FeatureIcon implements Icon
1502 {
1503   GraduatedColor gcol;
1504
1505   Color backg;
1506
1507   int width = 50, height = 20;
1508
1509   FeatureIcon(GraduatedColor gfc, Color bg, int w, int h)
1510   {
1511     gcol = gfc;
1512     backg = bg;
1513     width = w;
1514     height = h;
1515   }
1516
1517   public int getIconWidth()
1518   {
1519     return width;
1520   }
1521
1522   public int getIconHeight()
1523   {
1524     return height;
1525   }
1526
1527   public void paintIcon(Component c, Graphics g, int x, int y)
1528   {
1529
1530     if (gcol.isColourByLabel())
1531     {
1532       g.setColor(backg);
1533       g.fillRect(0, 0, width, height);
1534       // need an icon here.
1535       g.setColor(gcol.getMaxColor());
1536
1537       g.setFont(new Font("Verdana", Font.PLAIN, 9));
1538
1539       // g.setFont(g.getFont().deriveFont(
1540       // AffineTransform.getScaleInstance(
1541       // width/g.getFontMetrics().stringWidth("Label"),
1542       // height/g.getFontMetrics().getHeight())));
1543
1544       g.drawString("Label", 0, 0);
1545
1546     }
1547     else
1548     {
1549       Color minCol = gcol.getMinColor();
1550       g.setColor(minCol);
1551       g.fillRect(0, 0, width, height);
1552     }
1553   }
1554 }
1555
1556 class ColorEditor extends AbstractCellEditor implements TableCellEditor,
1557         ActionListener
1558 {
1559   FeatureSettings me;
1560
1561   GraduatedColor currentGColor;
1562
1563   FeatureColourChooser chooser;
1564
1565   String type;
1566
1567   Color currentColor;
1568
1569   JButton button;
1570
1571   JColorChooser colorChooser;
1572
1573   JDialog dialog;
1574
1575   protected static final String EDIT = "edit";
1576
1577   int selectedRow = 0;
1578
1579   public ColorEditor(FeatureSettings me)
1580   {
1581     this.me = me;
1582     // Set up the editor (from the table's point of view),
1583     // which is a button.
1584     // This button brings up the color chooser dialog,
1585     // which is the editor from the user's point of view.
1586     button = new JButton();
1587     button.setActionCommand(EDIT);
1588     button.addActionListener(this);
1589     button.setBorderPainted(false);
1590     // Set up the dialog that the button brings up.
1591     colorChooser = new JColorChooser();
1592     dialog = JColorChooser.createDialog(button, "Select new Colour", true, // modal
1593             colorChooser, this, // OK button handler
1594             null); // no CANCEL button handler
1595   }
1596
1597   /**
1598    * Handles events from the editor button and from the dialog's OK button.
1599    */
1600   public void actionPerformed(ActionEvent e)
1601   {
1602
1603     if (EDIT.equals(e.getActionCommand()))
1604     {
1605       // The user has clicked the cell, so
1606       // bring up the dialog.
1607       if (currentColor != null)
1608       {
1609         // bring up simple color chooser
1610         button.setBackground(currentColor);
1611         colorChooser.setColor(currentColor);
1612         dialog.setVisible(true);
1613       }
1614       else
1615       {
1616         // bring up graduated chooser.
1617         chooser = new FeatureColourChooser(me.fr, type);
1618         chooser.setRequestFocusEnabled(true);
1619         chooser.requestFocus();
1620         chooser.addActionListener(this);
1621       }
1622       // Make the renderer reappear.
1623       fireEditingStopped();
1624
1625     }
1626     else
1627     { // User pressed dialog's "OK" button.
1628       if (currentColor != null)
1629       {
1630         currentColor = colorChooser.getColor();
1631       }
1632       else
1633       {
1634         // class cast exceptions may be raised if the chooser created on a
1635         // non-graduated color
1636         currentGColor = (GraduatedColor) chooser.getLastColour();
1637       }
1638       me.table.setValueAt(getCellEditorValue(), selectedRow, 1);
1639       fireEditingStopped();
1640       me.table.validate();
1641     }
1642   }
1643
1644   // Implement the one CellEditor method that AbstractCellEditor doesn't.
1645   public Object getCellEditorValue()
1646   {
1647     if (currentColor == null)
1648     {
1649       return currentGColor;
1650     }
1651     return currentColor;
1652   }
1653
1654   // Implement the one method defined by TableCellEditor.
1655   public Component getTableCellEditorComponent(JTable table, Object value,
1656           boolean isSelected, int row, int column)
1657   {
1658     currentGColor = null;
1659     currentColor = null;
1660     this.selectedRow = row;
1661     type = me.table.getValueAt(row, 0).toString();
1662     button.setOpaque(true);
1663     button.setBackground(me.getBackground());
1664     if (value instanceof GraduatedColor)
1665     {
1666       currentGColor = (GraduatedColor) value;
1667       JLabel btn = new JLabel();
1668       btn.setSize(button.getSize());
1669       FeatureSettings.renderGraduatedColor(btn, currentGColor);
1670       button.setBackground(btn.getBackground());
1671       button.setIcon(btn.getIcon());
1672       button.setText(btn.getText());
1673     }
1674     else
1675     {
1676       button.setText("");
1677       button.setIcon(null);
1678       currentColor = (Color) value;
1679       button.setBackground(currentColor);
1680     }
1681     return button;
1682   }
1683 }