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