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