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