Merge branch 'Release_2_8_1_Branch_i18n' into try_r20b1_merge
[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.util.MessageManager;
42 import jalview.ws.dbsources.das.api.jalviewSourceI;
43
44 public class FeatureSettings extends JPanel
45 {
46   DasSourceBrowser dassourceBrowser;
47
48   jalview.ws.DasSequenceFeatureFetcher dasFeatureFetcher;
49
50   JPanel settingsPane = new JPanel();
51
52   JPanel dasSettingsPane = new JPanel();
53
54   final FeatureRenderer fr;
55
56   public final AlignFrame af;
57
58   Object[][] originalData;
59
60   final JInternalFrame frame;
61
62   JScrollPane scrollPane = new JScrollPane();
63
64   JTable table;
65
66   JPanel groupPanel;
67
68   JSlider transparency = new JSlider();
69
70   JPanel transPanel = new JPanel(new GridLayout(1, 2));
71
72   public FeatureSettings(AlignFrame af)
73   {
74     this.af = af;
75     fr = af.getFeatureRenderer();
76
77     transparency.setMaximum(100 - (int) (fr.transparency * 100));
78
79     try
80     {
81       jbInit();
82     } catch (Exception ex)
83     {
84       ex.printStackTrace();
85     }
86
87     table = new JTable();
88     table.getTableHeader().setFont(new Font("Verdana", Font.PLAIN, 12));
89     table.setFont(new Font("Verdana", Font.PLAIN, 12));
90     table.setDefaultRenderer(Color.class, new ColorRenderer());
91
92     table.setDefaultEditor(Color.class, new ColorEditor(this));
93
94     table.setDefaultEditor(GraduatedColor.class, new ColorEditor(this));
95     table.setDefaultRenderer(GraduatedColor.class, new ColorRenderer());
96     table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
97
98     table.addMouseListener(new MouseAdapter()
99     {
100       public void mousePressed(MouseEvent evt)
101       {
102         selectedRow = table.rowAtPoint(evt.getPoint());
103         if (javax.swing.SwingUtilities.isRightMouseButton(evt))
104         {
105           popupSort(selectedRow, (String) table.getValueAt(selectedRow, 0),
106                   table.getValueAt(selectedRow, 1), fr.minmax, evt.getX(),
107                   evt.getY());
108         }
109       }
110     });
111
112     table.addMouseMotionListener(new MouseMotionAdapter()
113     {
114       public void mouseDragged(MouseEvent evt)
115       {
116         int newRow = table.rowAtPoint(evt.getPoint());
117         if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
118         {
119           Object[] temp = new Object[3];
120           temp[0] = table.getValueAt(selectedRow, 0);
121           temp[1] = table.getValueAt(selectedRow, 1);
122           temp[2] = table.getValueAt(selectedRow, 2);
123
124           table.setValueAt(table.getValueAt(newRow, 0), selectedRow, 0);
125           table.setValueAt(table.getValueAt(newRow, 1), selectedRow, 1);
126           table.setValueAt(table.getValueAt(newRow, 2), selectedRow, 2);
127
128           table.setValueAt(temp[0], newRow, 0);
129           table.setValueAt(temp[1], newRow, 1);
130           table.setValueAt(temp[2], newRow, 2);
131
132           selectedRow = newRow;
133         }
134       }
135     });
136
137     scrollPane.setViewportView(table);
138
139     dassourceBrowser = new DasSourceBrowser(this);
140     dasSettingsPane.add(dassourceBrowser, BorderLayout.CENTER);
141
142     if (af.getViewport().featuresDisplayed == null
143             || fr.renderOrder == null)
144     {
145       fr.findAllFeatures(true); // display everything!
146     }
147
148     setTableData();
149     final PropertyChangeListener change;
150     final FeatureSettings fs = this;
151     fr.addPropertyChangeListener(change = new PropertyChangeListener()
152     {
153       public void propertyChange(PropertyChangeEvent evt)
154       {
155         if (!fs.resettingTable && !fs.handlingUpdate)
156         {
157           fs.handlingUpdate = true;
158           fs.resetTable(null); // new groups may be added with new seuqence
159           // feature types only
160           fs.handlingUpdate = false;
161         }
162       }
163
164     });
165
166     frame = new JInternalFrame();
167     frame.setContentPane(this);
168     if (new jalview.util.Platform().isAMac())
169     {
170       Desktop.addInternalFrame(frame, MessageManager.getString("label.sequence_feature_settings"), 475, 480);
171     }
172     else
173     {
174       Desktop.addInternalFrame(frame, MessageManager.getString("label.sequence_feature_settings"), 400, 450);
175     }
176
177     frame.addInternalFrameListener(new javax.swing.event.InternalFrameAdapter()
178     {
179       public void internalFrameClosed(
180               javax.swing.event.InternalFrameEvent evt)
181       {
182         fr.removePropertyChangeListener(change);
183         dassourceBrowser.fs = null;
184       };
185     });
186     frame.setLayer(JLayeredPane.PALETTE_LAYER);
187   }
188
189   protected void popupSort(final int selectedRow, final String type,
190           final Object typeCol, final Hashtable minmax, int x, int y)
191   {
192     JPopupMenu men = new JPopupMenu(MessageManager.formatMessage("label.settings_for_param", new String[]{type}));
193     JMenuItem scr = new JMenuItem(MessageManager.getString("label.sort_by_score"));
194     men.add(scr);
195     final FeatureSettings me = this;
196     scr.addActionListener(new ActionListener()
197     {
198
199       public void actionPerformed(ActionEvent e)
200       {
201         me.sortByScore(new String[]
202         { type });
203       }
204
205     });
206     JMenuItem dens = new JMenuItem(MessageManager.getString("label.sort_by_density"));
207     dens.addActionListener(new ActionListener()
208     {
209
210       public void actionPerformed(ActionEvent e)
211       {
212         me.sortByDens(new String[]
213         { type });
214       }
215
216     });
217     men.add(dens);
218     if (minmax != null)
219     {
220       final Object typeMinMax = minmax.get(type);
221       /*
222        * final JCheckBoxMenuItem chb = new JCheckBoxMenuItem("Vary Height"); //
223        * this is broken at the moment and isn't that useful anyway!
224        * chb.setSelected(minmax.get(type) != null); chb.addActionListener(new
225        * ActionListener() {
226        * 
227        * public void actionPerformed(ActionEvent e) {
228        * chb.setState(chb.getState()); if (chb.getState()) { minmax.put(type,
229        * null); } else { minmax.put(type, typeMinMax); } }
230        * 
231        * });
232        * 
233        * men.add(chb);
234        */
235       if (typeMinMax != null && ((float[][]) typeMinMax)[0] != null)
236       {
237         // if (table.getValueAt(row, column));
238         // graduated colourschemes for those where minmax exists for the
239         // positional features
240         final JCheckBoxMenuItem mxcol = new JCheckBoxMenuItem(
241                 "Graduated Colour");
242         mxcol.setSelected(!(typeCol instanceof Color));
243         men.add(mxcol);
244         mxcol.addActionListener(new ActionListener()
245         {
246           JColorChooser colorChooser;
247
248           public void actionPerformed(ActionEvent e)
249           {
250             if (e.getSource() == mxcol)
251             {
252               if (typeCol instanceof Color)
253               {
254                 FeatureColourChooser fc = new FeatureColourChooser(me.fr,
255                         type);
256                 fc.addActionListener(this);
257               }
258               else
259               {
260                 // bring up simple color chooser
261                 colorChooser = new JColorChooser();
262                 JDialog dialog = JColorChooser.createDialog(me,
263                         "Select new Colour", true, // modal
264                         colorChooser, this, // OK button handler
265                         null); // no CANCEL button handler
266                 colorChooser.setColor(((GraduatedColor) typeCol)
267                         .getMaxColor());
268                 dialog.setVisible(true);
269               }
270             }
271             else
272             {
273               if (e.getSource() instanceof FeatureColourChooser)
274               {
275                 FeatureColourChooser fc = (FeatureColourChooser) e
276                         .getSource();
277                 table.setValueAt(fc.getLastColour(), selectedRow, 1);
278                 table.validate();
279               }
280               else
281               {
282                 // probably the color chooser!
283                 table.setValueAt(colorChooser.getColor(), selectedRow, 1);
284                 table.validate();
285                 me.updateFeatureRenderer(
286                         ((FeatureTableModel) table.getModel()).getData(),
287                         false);
288               }
289             }
290           }
291
292         });
293       }
294     }
295     men.show(table, x, y);
296   }
297
298   /**
299    * true when Feature Settings are updating from feature renderer
300    */
301   private boolean handlingUpdate = false;
302
303   /**
304    * contains a float[3] for each feature type string. created by setTableData
305    */
306   Hashtable typeWidth = null;
307
308   synchronized public void setTableData()
309   {
310     if (fr.featureGroups == null)
311     {
312       fr.featureGroups = new Hashtable();
313     }
314     Vector allFeatures = new Vector();
315     Vector allGroups = new Vector();
316     SequenceFeature[] tmpfeatures;
317     String group;
318     for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
319     {
320       if (af.getViewport().getAlignment().getSequenceAt(i)
321               .getDatasetSequence().getSequenceFeatures() == null)
322       {
323         continue;
324       }
325
326       tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
327               .getDatasetSequence().getSequenceFeatures();
328
329       int index = 0;
330       while (index < tmpfeatures.length)
331       {
332         if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
333         {
334           index++;
335           continue;
336         }
337
338         if (tmpfeatures[index].getFeatureGroup() != null)
339         {
340           group = tmpfeatures[index].featureGroup;
341           if (!allGroups.contains(group))
342           {
343             allGroups.addElement(group);
344             if (group != null)
345             {
346               checkGroupState(group);
347             }
348           }
349         }
350
351         if (!allFeatures.contains(tmpfeatures[index].getType()))
352         {
353           allFeatures.addElement(tmpfeatures[index].getType());
354         }
355         index++;
356       }
357     }
358
359     resetTable(null);
360
361     validate();
362   }
363
364   /**
365    * 
366    * @param group
367    * @return true if group has been seen before and is already added to set.
368    */
369   private boolean checkGroupState(String group)
370   {
371     boolean visible;
372     if (fr.featureGroups.containsKey(group))
373     {
374       visible = ((Boolean) fr.featureGroups.get(group)).booleanValue();
375     }
376     else
377     {
378       visible = true; // new group is always made visible
379     }
380
381     if (groupPanel == null)
382     {
383       groupPanel = new JPanel();
384     }
385
386     boolean alreadyAdded = false;
387     for (int g = 0; g < groupPanel.getComponentCount(); g++)
388     {
389       if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
390       {
391         alreadyAdded = true;
392         ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
393         break;
394       }
395     }
396
397     if (alreadyAdded)
398     {
399
400       return true;
401     }
402
403     fr.featureGroups.put(group, new Boolean(visible));
404     final String grp = group;
405     final JCheckBox check = new JCheckBox(group, visible);
406     check.setFont(new Font("Serif", Font.BOLD, 12));
407     check.addItemListener(new ItemListener()
408     {
409       public void itemStateChanged(ItemEvent evt)
410       {
411         fr.featureGroups.put(check.getText(),
412                 new Boolean(check.isSelected()));
413         af.alignPanel.seqPanel.seqCanvas.repaint();
414         if (af.alignPanel.overviewPanel != null)
415         {
416           af.alignPanel.overviewPanel.updateOverviewImage();
417         }
418
419         resetTable(new String[]
420         { grp });
421       }
422     });
423     groupPanel.add(check);
424     return false;
425   }
426
427   boolean resettingTable = false;
428
429   synchronized void resetTable(String[] groupChanged)
430   {
431     if (resettingTable == true)
432     {
433       return;
434     }
435     resettingTable = true;
436     typeWidth = new Hashtable();
437     // TODO: change avWidth calculation to 'per-sequence' average and use long
438     // rather than float
439     float[] avWidth = null;
440     SequenceFeature[] tmpfeatures;
441     String group = null, type;
442     Vector visibleChecks = new Vector();
443
444     // Find out which features should be visible depending on which groups
445     // are selected / deselected
446     // and recompute average width ordering
447     for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
448     {
449
450       tmpfeatures = af.getViewport().getAlignment().getSequenceAt(i)
451               .getDatasetSequence().getSequenceFeatures();
452       if (tmpfeatures == null)
453       {
454         continue;
455       }
456
457       int index = 0;
458       while (index < tmpfeatures.length)
459       {
460         group = tmpfeatures[index].featureGroup;
461
462         if (tmpfeatures[index].begin == 0 && tmpfeatures[index].end == 0)
463         {
464           index++;
465           continue;
466         }
467
468         if (group == null || fr.featureGroups.get(group) == null
469                 || ((Boolean) fr.featureGroups.get(group)).booleanValue())
470         {
471           if (group != null)
472             checkGroupState(group);
473           type = tmpfeatures[index].getType();
474           if (!visibleChecks.contains(type))
475           {
476             visibleChecks.addElement(type);
477           }
478         }
479         if (!typeWidth.containsKey(tmpfeatures[index].getType()))
480         {
481           typeWidth.put(tmpfeatures[index].getType(),
482                   avWidth = new float[3]);
483         }
484         else
485         {
486           avWidth = (float[]) typeWidth.get(tmpfeatures[index].getType());
487         }
488         avWidth[0]++;
489         if (tmpfeatures[index].getBegin() > tmpfeatures[index].getEnd())
490         {
491           avWidth[1] += 1 + tmpfeatures[index].getBegin()
492                   - tmpfeatures[index].getEnd();
493         }
494         else
495         {
496           avWidth[1] += 1 + tmpfeatures[index].getEnd()
497                   - tmpfeatures[index].getBegin();
498         }
499         index++;
500       }
501     }
502
503     int fSize = visibleChecks.size();
504     Object[][] data = new Object[fSize][3];
505     int dataIndex = 0;
506
507     if (fr.renderOrder != null)
508     {
509       if (!handlingUpdate)
510         fr.findAllFeatures(groupChanged != null); // prod to update
511       // colourschemes. but don't
512       // affect display
513       // First add the checks in the previous render order,
514       // in case the window has been closed and reopened
515       for (int ro = fr.renderOrder.length - 1; ro > -1; ro--)
516       {
517         type = fr.renderOrder[ro];
518
519         if (!visibleChecks.contains(type))
520         {
521           continue;
522         }
523
524         data[dataIndex][0] = type;
525         data[dataIndex][1] = fr.getFeatureStyle(type);
526         data[dataIndex][2] = new Boolean(
527                 af.getViewport().featuresDisplayed.containsKey(type));
528         dataIndex++;
529         visibleChecks.removeElement(type);
530       }
531     }
532
533     fSize = visibleChecks.size();
534     for (int i = 0; i < fSize; i++)
535     {
536       // These must be extra features belonging to the group
537       // which was just selected
538       type = visibleChecks.elementAt(i).toString();
539       data[dataIndex][0] = type;
540
541       data[dataIndex][1] = fr.getFeatureStyle(type);
542       if (data[dataIndex][1] == null)
543       {
544         // "Colour has been updated in another view!!"
545         fr.renderOrder = null;
546         return;
547       }
548
549       data[dataIndex][2] = new Boolean(true);
550       dataIndex++;
551     }
552
553     if (originalData == null)
554     {
555       originalData = new Object[data.length][3];
556       for (int i = 0; i < data.length; i++)
557       {
558         System.arraycopy(data[i], 0, originalData[i], 0, 3);
559       }
560     }
561
562     table.setModel(new FeatureTableModel(data));
563     table.getColumnModel().getColumn(0).setPreferredWidth(200);
564
565     if (groupPanel != null)
566     {
567       groupPanel.setLayout(new GridLayout(fr.featureGroups.size() / 4 + 1,
568               4));
569
570       groupPanel.validate();
571       bigPanel.add(groupPanel, BorderLayout.NORTH);
572     }
573
574     updateFeatureRenderer(data, groupChanged != null);
575     resettingTable = false;
576   }
577
578   /**
579    * reorder data based on the featureRenderers global priority list.
580    * 
581    * @param data
582    */
583   private void ensureOrder(Object[][] data)
584   {
585     boolean sort = false;
586     float[] order = new float[data.length];
587     for (int i = 0; i < order.length; i++)
588     {
589       order[i] = fr.getOrder(data[i][0].toString());
590       if (order[i] < 0)
591         order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
592       if (i > 1)
593         sort = sort || order[i - 1] > order[i];
594     }
595     if (sort)
596       jalview.util.QuickSort.sort(order, data);
597   }
598
599   void load()
600   {
601     JalviewFileChooser chooser = new JalviewFileChooser(
602             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
603             { "fc" }, new String[]
604             { "Sequence Feature Colours" }, "Sequence Feature Colours");
605     chooser.setFileView(new jalview.io.JalviewFileView());
606     chooser.setDialogTitle("Load Feature Colours");
607     chooser.setToolTipText(MessageManager.getString("action.load"));
608
609     int value = chooser.showOpenDialog(this);
610
611     if (value == JalviewFileChooser.APPROVE_OPTION)
612     {
613       File file = chooser.getSelectedFile();
614
615       try
616       {
617         InputStreamReader in = new InputStreamReader(new FileInputStream(
618                 file), "UTF-8");
619
620         jalview.schemabinding.version2.JalviewUserColours jucs = new jalview.schemabinding.version2.JalviewUserColours();
621         jucs = (jalview.schemabinding.version2.JalviewUserColours) jucs
622                 .unmarshal(in);
623
624         for (int i = jucs.getColourCount() - 1; i >= 0; i--)
625         {
626           String name;
627           jalview.schemabinding.version2.Colour newcol = jucs.getColour(i);
628           if (newcol.hasMax())
629           {
630             Color mincol = null, maxcol = null;
631             try
632             {
633               mincol = new Color(Integer.parseInt(newcol.getMinRGB(), 16));
634               maxcol = new Color(Integer.parseInt(newcol.getRGB(), 16));
635
636             } catch (Exception e)
637             {
638               Cache.log.warn("Couldn't parse out graduated feature color.",
639                       e);
640             }
641             GraduatedColor gcol = new GraduatedColor(mincol, maxcol,
642                     newcol.getMin(), newcol.getMax());
643             if (newcol.hasAutoScale())
644             {
645               gcol.setAutoScaled(newcol.getAutoScale());
646             }
647             if (newcol.hasColourByLabel())
648             {
649               gcol.setColourByLabel(newcol.getColourByLabel());
650             }
651             if (newcol.hasThreshold())
652             {
653               gcol.setThresh(newcol.getThreshold());
654               gcol.setThreshType(AnnotationColourGradient.NO_THRESHOLD); // default
655             }
656             if (newcol.getThreshType().length() > 0)
657             {
658               String ttyp = newcol.getThreshType();
659               if (ttyp.equalsIgnoreCase("NONE"))
660               {
661                 gcol.setThreshType(AnnotationColourGradient.NO_THRESHOLD);
662               }
663               if (ttyp.equalsIgnoreCase("ABOVE"))
664               {
665                 gcol.setThreshType(AnnotationColourGradient.ABOVE_THRESHOLD);
666               }
667               if (ttyp.equalsIgnoreCase("BELOW"))
668               {
669                 gcol.setThreshType(AnnotationColourGradient.BELOW_THRESHOLD);
670               }
671             }
672             fr.setColour(name = newcol.getName(), gcol);
673           }
674           else
675           {
676             fr.setColour(name = jucs.getColour(i).getName(), new Color(
677                     Integer.parseInt(jucs.getColour(i).getRGB(), 16)));
678           }
679           fr.setOrder(name, (i == 0) ? 0 : i / jucs.getColourCount());
680         }
681         if (table != null)
682         {
683           resetTable(null);
684           Object[][] data = ((FeatureTableModel) table.getModel())
685                   .getData();
686           ensureOrder(data);
687           updateFeatureRenderer(data, false);
688           table.repaint();
689         }
690       } catch (Exception ex)
691       {
692         System.out.println("Error loading User Colour File\n" + ex);
693       }
694     }
695   }
696
697   void save()
698   {
699     JalviewFileChooser chooser = new JalviewFileChooser(
700             jalview.bin.Cache.getProperty("LAST_DIRECTORY"), new String[]
701             { "fc" }, new String[]
702             { "Sequence Feature Colours" }, "Sequence Feature Colours");
703     chooser.setFileView(new jalview.io.JalviewFileView());
704     chooser.setDialogTitle("Save Feature Colour Scheme");
705     chooser.setToolTipText(MessageManager.getString("action.save"));
706
707     int value = chooser.showSaveDialog(this);
708
709     if (value == JalviewFileChooser.APPROVE_OPTION)
710     {
711       String choice = chooser.getSelectedFile().getPath();
712       jalview.schemabinding.version2.JalviewUserColours ucs = new jalview.schemabinding.version2.JalviewUserColours();
713       ucs.setSchemeName("Sequence Features");
714       try
715       {
716         PrintWriter out = new PrintWriter(new OutputStreamWriter(
717                 new FileOutputStream(choice), "UTF-8"));
718
719         Iterator e = fr.featureColours.keySet().iterator();
720         float[] sortOrder = new float[fr.featureColours.size()];
721         String[] sortTypes = new String[fr.featureColours.size()];
722         int i = 0;
723         while (e.hasNext())
724         {
725           sortTypes[i] = e.next().toString();
726           sortOrder[i] = fr.getOrder(sortTypes[i]);
727           i++;
728         }
729         jalview.util.QuickSort.sort(sortOrder, sortTypes);
730         sortOrder = null;
731         Object fcol;
732         GraduatedColor gcol;
733         for (i = 0; i < sortTypes.length; i++)
734         {
735           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
736           col.setName(sortTypes[i]);
737           col.setRGB(jalview.util.Format.getHexString(fr.getColour(col
738                   .getName())));
739           fcol = fr.getFeatureStyle(sortTypes[i]);
740           if (fcol instanceof GraduatedColor)
741           {
742             gcol = (GraduatedColor) fcol;
743             col.setMin(gcol.getMin());
744             col.setMax(gcol.getMax());
745             col.setMinRGB(jalview.util.Format.getHexString(gcol
746                     .getMinColor()));
747             col.setAutoScale(gcol.isAutoScale());
748             col.setThreshold(gcol.getThresh());
749             col.setColourByLabel(gcol.isColourByLabel());
750             switch (gcol.getThreshType())
751             {
752             case AnnotationColourGradient.NO_THRESHOLD:
753               col.setThreshType("NONE");
754               break;
755             case AnnotationColourGradient.ABOVE_THRESHOLD:
756               col.setThreshType("ABOVE");
757               break;
758             case AnnotationColourGradient.BELOW_THRESHOLD:
759               col.setThreshType("BELOW");
760               break;
761             }
762           }
763           ucs.addColour(col);
764         }
765         ucs.marshal(out);
766         out.close();
767       } catch (Exception ex)
768       {
769         ex.printStackTrace();
770       }
771     }
772   }
773
774   public void invertSelection()
775   {
776     for (int i = 0; i < table.getRowCount(); i++)
777     {
778       Boolean value = (Boolean) table.getValueAt(i, 2);
779
780       table.setValueAt(new Boolean(!value.booleanValue()), i, 2);
781     }
782   }
783
784   public void orderByAvWidth()
785   {
786     if (table == null || table.getModel() == null)
787       return;
788     Object[][] data = ((FeatureTableModel) table.getModel()).getData();
789     float[] width = new float[data.length];
790     float[] awidth;
791     float max = 0;
792     int num = 0;
793     for (int i = 0; i < data.length; i++)
794     {
795       awidth = (float[]) typeWidth.get(data[i][0]);
796       if (awidth[0] > 0)
797       {
798         width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
799         // weight - but have to make per
800         // sequence, too (awidth[2])
801         // if (width[i]==1) // hack to distinguish single width sequences.
802         num++;
803       }
804       else
805       {
806         width[i] = 0;
807       }
808       if (max < width[i])
809         max = width[i];
810     }
811     boolean sort = false;
812     for (int i = 0; i < width.length; i++)
813     {
814       // awidth = (float[]) typeWidth.get(data[i][0]);
815       if (width[i] == 0)
816       {
817         width[i] = fr.getOrder(data[i][0].toString());
818         if (width[i] < 0)
819         {
820           width[i] = fr.setOrder(data[i][0].toString(), i / data.length);
821         }
822       }
823       else
824       {
825         width[i] /= max; // normalize
826         fr.setOrder(data[i][0].toString(), width[i]); // store for later
827       }
828       if (i > 0)
829         sort = sort || width[i - 1] > width[i];
830     }
831     if (sort)
832       jalview.util.QuickSort.sort(width, data);
833     // update global priority order
834
835     updateFeatureRenderer(data, false);
836     table.repaint();
837   }
838
839   public void close()
840   {
841     try
842     {
843       frame.setClosed(true);
844     } catch (Exception exe)
845     {
846     }
847
848   }
849
850   public void updateFeatureRenderer(Object[][] data)
851   {
852     updateFeatureRenderer(data, true);
853   }
854
855   private void updateFeatureRenderer(Object[][] data, boolean visibleNew)
856   {
857     fr.setFeaturePriority(data, visibleNew);
858     af.alignPanel.paintAlignment(true);
859   }
860
861   int selectedRow = -1;
862
863   JTabbedPane tabbedPane = new JTabbedPane();
864
865   BorderLayout borderLayout1 = new BorderLayout();
866
867   BorderLayout borderLayout2 = new BorderLayout();
868
869   BorderLayout borderLayout3 = new BorderLayout();
870
871   JPanel bigPanel = new JPanel();
872
873   BorderLayout borderLayout4 = new BorderLayout();
874
875   JButton invert = new JButton();
876
877   JPanel buttonPanel = new JPanel();
878
879   JButton cancel = new JButton();
880
881   JButton ok = new JButton();
882
883   JButton loadColours = new JButton();
884
885   JButton saveColours = new JButton();
886
887   JPanel dasButtonPanel = new JPanel();
888
889   JButton fetchDAS = new JButton();
890
891   JButton saveDAS = new JButton();
892
893   JButton cancelDAS = new JButton();
894
895   JButton optimizeOrder = new JButton();
896
897   JButton sortByScore = new JButton();
898
899   JButton sortByDens = new JButton();
900
901   JPanel transbuttons = new JPanel(new GridLayout(4, 1));
902
903   private void jbInit() throws Exception
904   {
905     this.setLayout(borderLayout1);
906     settingsPane.setLayout(borderLayout2);
907     dasSettingsPane.setLayout(borderLayout3);
908     bigPanel.setLayout(borderLayout4);
909     invert.setFont(JvSwingUtils.getLabelFont());
910     invert.setText(MessageManager.getString("label.invert_selection"));
911     invert.addActionListener(new ActionListener()
912     {
913       public void actionPerformed(ActionEvent e)
914       {
915         invertSelection();
916       }
917     });
918     optimizeOrder.setFont(JvSwingUtils.getLabelFont());
919     optimizeOrder.setText(MessageManager.getString("label.optimise_order"));
920     optimizeOrder.addActionListener(new ActionListener()
921     {
922       public void actionPerformed(ActionEvent e)
923       {
924         orderByAvWidth();
925       }
926     });
927     sortByScore.setFont(JvSwingUtils.getLabelFont());
928     sortByScore.setText(MessageManager.getString("label.seq_sort_by_score"));
929     sortByScore.addActionListener(new ActionListener()
930     {
931       public void actionPerformed(ActionEvent e)
932       {
933         sortByScore(null);
934       }
935     });
936     sortByDens.setFont(JvSwingUtils.getLabelFont());
937     sortByDens.setText(MessageManager.getString("label.sequence_sort_by_density"));
938     sortByDens.addActionListener(new ActionListener()
939     {
940       public void actionPerformed(ActionEvent e)
941       {
942         sortByDens(null);
943       }
944     });
945     cancel.setFont(JvSwingUtils.getLabelFont());
946     cancel.setText(MessageManager.getString("action.cancel"));
947     cancel.addActionListener(new ActionListener()
948     {
949       public void actionPerformed(ActionEvent e)
950       {
951         updateFeatureRenderer(originalData);
952         close();
953       }
954     });
955     ok.setFont(JvSwingUtils.getLabelFont());
956     ok.setText(MessageManager.getString("action.ok"));
957     ok.addActionListener(new ActionListener()
958     {
959       public void actionPerformed(ActionEvent e)
960       {
961         close();
962       }
963     });
964     loadColours.setFont(JvSwingUtils.getLabelFont());
965     loadColours.setText(MessageManager.getString("label.load_colours"));
966     loadColours.addActionListener(new ActionListener()
967     {
968       public void actionPerformed(ActionEvent e)
969       {
970         load();
971       }
972     });
973     saveColours.setFont(JvSwingUtils.getLabelFont());
974     saveColours.setText(MessageManager.getString("label.save_colours"));
975     saveColours.addActionListener(new ActionListener()
976     {
977       public void actionPerformed(ActionEvent e)
978       {
979         save();
980       }
981     });
982     transparency.addChangeListener(new ChangeListener()
983     {
984       public void stateChanged(ChangeEvent evt)
985       {
986         fr.setTransparency((float) (100 - transparency.getValue()) / 100f);
987         af.alignPanel.paintAlignment(true);
988       }
989     });
990
991     transparency.setMaximum(70);
992     fetchDAS.setText(MessageManager.getString("label.fetch_das_features"));
993     fetchDAS.addActionListener(new ActionListener()
994     {
995       public void actionPerformed(ActionEvent e)
996       {
997         fetchDAS_actionPerformed(e);
998       }
999     });
1000     saveDAS.setText(MessageManager.getString("action.save_as_default"));
1001     saveDAS.addActionListener(new ActionListener()
1002     {
1003       public void actionPerformed(ActionEvent e)
1004       {
1005         saveDAS_actionPerformed(e);
1006       }
1007     });
1008     dasButtonPanel.setBorder(BorderFactory.createEtchedBorder());
1009     dasSettingsPane.setBorder(null);
1010     cancelDAS.setEnabled(false);
1011     cancelDAS.setText(MessageManager.getString("action.cancel_fetch"));
1012     cancelDAS.addActionListener(new ActionListener()
1013     {
1014       public void actionPerformed(ActionEvent e)
1015       {
1016         cancelDAS_actionPerformed(e);
1017       }
1018     });
1019     this.add(tabbedPane, java.awt.BorderLayout.CENTER);
1020     tabbedPane.addTab("Feature Settings", settingsPane);
1021     tabbedPane.addTab("DAS Settings", dasSettingsPane);
1022     bigPanel.add(transPanel, java.awt.BorderLayout.SOUTH);
1023     transbuttons.add(optimizeOrder);
1024     transbuttons.add(invert);
1025     transbuttons.add(sortByScore);
1026     transbuttons.add(sortByDens);
1027     transPanel.add(transparency);
1028     transPanel.add(transbuttons);
1029     buttonPanel.add(ok);
1030     buttonPanel.add(cancel);
1031     buttonPanel.add(loadColours);
1032     buttonPanel.add(saveColours);
1033     bigPanel.add(scrollPane, java.awt.BorderLayout.CENTER);
1034     dasSettingsPane.add(dasButtonPanel, java.awt.BorderLayout.SOUTH);
1035     dasButtonPanel.add(fetchDAS);
1036     dasButtonPanel.add(cancelDAS);
1037     dasButtonPanel.add(saveDAS);
1038     settingsPane.add(bigPanel, java.awt.BorderLayout.CENTER);
1039     settingsPane.add(buttonPanel, java.awt.BorderLayout.SOUTH);
1040   }
1041
1042   protected void sortByDens(String[] typ)
1043   {
1044     sortBy(typ, "Sort by Density", AlignmentSorter.FEATURE_DENSITY);
1045   }
1046
1047   protected void sortBy(String[] typ, String methodText, final String method)
1048   {
1049     if (typ == null)
1050     {
1051       typ = getDisplayedFeatureTypes();
1052     }
1053     String gps[] = null;
1054     gps = getDisplayedFeatureGroups();
1055     if (typ != null)
1056     {
1057       ArrayList types = new ArrayList();
1058       for (int i = 0; i < typ.length; i++)
1059       {
1060         if (typ[i] != null)
1061         {
1062           types.add(typ[i]);
1063         }
1064         typ = new String[types.size()];
1065         types.toArray(typ);
1066       }
1067     }
1068     if (gps != null)
1069     {
1070       ArrayList grps = new ArrayList();
1071
1072       for (int i = 0; i < gps.length; i++)
1073       {
1074         if (gps[i] != null)
1075         {
1076           grps.add(gps[i]);
1077         }
1078       }
1079       gps = new String[grps.size()];
1080       grps.toArray(gps);
1081     }
1082     AlignmentPanel alignPanel = af.alignPanel;
1083     AlignmentI al = alignPanel.av.getAlignment();
1084
1085     int start, stop;
1086     SequenceGroup sg = alignPanel.av.getSelectionGroup();
1087     if (sg != null)
1088     {
1089       start = sg.getStartRes();
1090       stop = sg.getEndRes();
1091     }
1092     else
1093     {
1094       start = 0;
1095       stop = al.getWidth();
1096     }
1097     SequenceI[] oldOrder = al.getSequencesArray();
1098     AlignmentSorter.sortByFeature(typ, gps, start, stop, al, method);
1099     af.addHistoryItem(new OrderCommand(methodText, oldOrder, alignPanel.av
1100             .getAlignment()));
1101     alignPanel.paintAlignment(true);
1102
1103   }
1104
1105   protected void sortByScore(String[] typ)
1106   {
1107     sortBy(typ, "Sort by Feature Score", AlignmentSorter.FEATURE_SCORE);
1108   }
1109
1110   private String[] getDisplayedFeatureTypes()
1111   {
1112     String[] typ = null;
1113     if (fr != null)
1114     {
1115       synchronized (fr.renderOrder)
1116       {
1117         typ = new String[fr.renderOrder.length];
1118         System.arraycopy(fr.renderOrder, 0, typ, 0, typ.length);
1119         for (int i = 0; i < typ.length; i++)
1120         {
1121           if (af.viewport.featuresDisplayed.get(typ[i]) == null)
1122           {
1123             typ[i] = null;
1124           }
1125         }
1126       }
1127     }
1128     return typ;
1129   }
1130
1131   private String[] getDisplayedFeatureGroups()
1132   {
1133     String[] gps = null;
1134     if (fr != null)
1135     {
1136
1137       if (fr.featureGroups != null)
1138       {
1139         Iterator en = fr.featureGroups.keySet().iterator();
1140         gps = new String[fr.featureColours.size()];
1141         int g = 0;
1142         boolean valid = false;
1143         while (en.hasNext())
1144         {
1145           String gp = (String) en.next();
1146           Boolean on = (Boolean) fr.featureGroups.get(gp);
1147           if (on != null && on.booleanValue())
1148           {
1149             valid = true;
1150             gps[g++] = gp;
1151           }
1152         }
1153         while (g < gps.length)
1154         {
1155           gps[g++] = null;
1156         }
1157         if (!valid)
1158         {
1159           return null;
1160         }
1161       }
1162     }
1163     return gps;
1164   }
1165
1166   public void fetchDAS_actionPerformed(ActionEvent e)
1167   {
1168     fetchDAS.setEnabled(false);
1169     cancelDAS.setEnabled(true);
1170     dassourceBrowser.setGuiEnabled(false);
1171     Vector selectedSources = dassourceBrowser.getSelectedSources();
1172     doDasFeatureFetch(selectedSources, true, true);
1173   }
1174
1175   /**
1176    * get the features from selectedSources for all or the current selection
1177    * 
1178    * @param selectedSources
1179    * @param checkDbRefs
1180    * @param promptFetchDbRefs
1181    */
1182   private void doDasFeatureFetch(List<jalviewSourceI> selectedSources,
1183           boolean checkDbRefs, boolean promptFetchDbRefs)
1184   {
1185     SequenceI[] dataset, seqs;
1186     int iSize;
1187     AlignViewport vp = af.getViewport();
1188     if (vp.getSelectionGroup() != null
1189             && vp.getSelectionGroup().getSize() > 0)
1190     {
1191       iSize = vp.getSelectionGroup().getSize();
1192       dataset = new SequenceI[iSize];
1193       seqs = vp.getSelectionGroup().getSequencesInOrder(vp.getAlignment());
1194     }
1195     else
1196     {
1197       iSize = vp.getAlignment().getHeight();
1198       seqs = vp.getAlignment().getSequencesArray();
1199     }
1200
1201     dataset = new SequenceI[iSize];
1202     for (int i = 0; i < iSize; i++)
1203     {
1204       dataset[i] = seqs[i].getDatasetSequence();
1205     }
1206
1207     cancelDAS.setEnabled(true);
1208     dasFeatureFetcher = new jalview.ws.DasSequenceFeatureFetcher(dataset,
1209             this, selectedSources, checkDbRefs, promptFetchDbRefs);
1210     af.getViewport().setShowSequenceFeatures(true);
1211     af.showSeqFeatures.setSelected(true);
1212   }
1213
1214   /**
1215    * blocking call to initialise the das source browser
1216    */
1217   public void initDasSources()
1218   {
1219     dassourceBrowser.initDasSources();
1220   }
1221
1222   /**
1223    * examine the current list of das sources and return any matching the given
1224    * nicknames in sources
1225    * 
1226    * @param sources
1227    *          Vector of Strings to resolve to DAS source nicknames.
1228    * @return sources that are present in source list.
1229    */
1230   public List<jalviewSourceI> resolveSourceNicknames(Vector sources)
1231   {
1232     return dassourceBrowser.sourceRegistry.resolveSourceNicknames(sources);
1233   }
1234
1235   /**
1236    * get currently selected das sources. ensure you have called initDasSources
1237    * before calling this.
1238    * 
1239    * @return vector of selected das source nicknames
1240    */
1241   public Vector getSelectedSources()
1242   {
1243     return dassourceBrowser.getSelectedSources();
1244   }
1245
1246   /**
1247    * properly initialise DAS fetcher and then initiate a new thread to fetch
1248    * features from the named sources (rather than any turned on by default)
1249    * 
1250    * @param sources
1251    * @param block
1252    *          if true then runs in same thread, otherwise passes to the Swing
1253    *          executor
1254    */
1255   public void fetchDasFeatures(Vector sources, boolean block)
1256   {
1257     initDasSources();
1258     List<jalviewSourceI> resolved = dassourceBrowser.sourceRegistry
1259             .resolveSourceNicknames(sources);
1260     if (resolved.size() == 0)
1261     {
1262       resolved = dassourceBrowser.getSelectedSources();
1263     }
1264     if (resolved.size() > 0)
1265     {
1266       final List<jalviewSourceI> dassources = resolved;
1267       fetchDAS.setEnabled(false);
1268       // cancelDAS.setEnabled(true); doDasFetch does this.
1269       Runnable fetcher = new Runnable()
1270       {
1271
1272         public void run()
1273         {
1274           doDasFeatureFetch(dassources, true, false);
1275
1276         }
1277       };
1278       if (block)
1279       {
1280         fetcher.run();
1281       }
1282       else
1283       {
1284         SwingUtilities.invokeLater(fetcher);
1285       }
1286     }
1287   }
1288
1289   public void saveDAS_actionPerformed(ActionEvent e)
1290   {
1291     dassourceBrowser
1292             .saveProperties(jalview.bin.Cache.applicationProperties);
1293   }
1294
1295   public void complete()
1296   {
1297     fetchDAS.setEnabled(true);
1298     cancelDAS.setEnabled(false);
1299     dassourceBrowser.setGuiEnabled(true);
1300
1301   }
1302
1303   public void cancelDAS_actionPerformed(ActionEvent e)
1304   {
1305     if (dasFeatureFetcher != null)
1306     {
1307       dasFeatureFetcher.cancel();
1308     }
1309     complete();
1310   }
1311
1312   public void noDasSourceActive()
1313   {
1314     complete();
1315     JOptionPane.showInternalConfirmDialog(Desktop.desktop,
1316             MessageManager.getString("label.no_das_sources_selected_warn"),
1317             MessageManager.getString("label.no_das_sources_selected_title"), 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(MessageManager.getString("label.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 }