Clover icon

Coverage Report

  1. Project Clover database Thu Aug 13 2020 12:04:21 BST
  2. Package jalview.gui

File FeatureSettings.java

 

Coverage histogram

../../img/srcFileCovDistChart5.png
39% of files have more coverage

Code metrics

222
748
95
7
2,273
1,726
228
0.3
7.87
13.57
2.4

Classes

Class Line # Actions
FeatureSettings 118 584 175
0.509025350.9%
FeatureSettings.FeatureTableModel 1699 21 15
0.529411852.9%
FeatureSettings.ColorRenderer 1788 20 6
0.880%
FeatureSettings.FilterRenderer 1843 17 5
0.1616%
FeatureSettings.ColorEditor 1914 47 12
0.0781257.8%
FeatureSettings.FilterEditor 2075 33 8
0.1111111111.1%
FeatureIcon 2192 26 7
0.722222272.2%
 

Contributing tests

This file is covered by 3 tests. .

Source view

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 java.awt.BorderLayout;
24    import java.awt.Color;
25    import java.awt.Component;
26    import java.awt.Dimension;
27    import java.awt.FlowLayout;
28    import java.awt.Font;
29    import java.awt.Graphics;
30    import java.awt.GridLayout;
31    import java.awt.Point;
32    import java.awt.Rectangle;
33    import java.awt.event.ActionEvent;
34    import java.awt.event.ActionListener;
35    import java.awt.event.ItemEvent;
36    import java.awt.event.ItemListener;
37    import java.awt.event.MouseAdapter;
38    import java.awt.event.MouseEvent;
39    import java.awt.event.MouseMotionAdapter;
40    import java.beans.PropertyChangeEvent;
41    import java.beans.PropertyChangeListener;
42    import java.io.File;
43    import java.io.FileInputStream;
44    import java.io.FileOutputStream;
45    import java.io.InputStreamReader;
46    import java.io.OutputStreamWriter;
47    import java.io.PrintWriter;
48    import java.util.Arrays;
49    import java.util.Comparator;
50    import java.util.HashMap;
51    import java.util.HashSet;
52    import java.util.Hashtable;
53    import java.util.Iterator;
54    import java.util.List;
55    import java.util.Map;
56    import java.util.Set;
57   
58    import javax.help.HelpSetException;
59    import javax.swing.AbstractCellEditor;
60    import javax.swing.BorderFactory;
61    import javax.swing.Icon;
62    import javax.swing.JButton;
63    import javax.swing.JCheckBox;
64    import javax.swing.JCheckBoxMenuItem;
65    import javax.swing.JInternalFrame;
66    import javax.swing.JLabel;
67    import javax.swing.JLayeredPane;
68    import javax.swing.JMenuItem;
69    import javax.swing.JPanel;
70    import javax.swing.JPopupMenu;
71    import javax.swing.JScrollPane;
72    import javax.swing.JSlider;
73    import javax.swing.JTable;
74    import javax.swing.ListSelectionModel;
75    import javax.swing.SwingConstants;
76    import javax.swing.ToolTipManager;
77    import javax.swing.border.Border;
78    import javax.swing.event.ChangeEvent;
79    import javax.swing.event.ChangeListener;
80    import javax.swing.table.AbstractTableModel;
81    import javax.swing.table.JTableHeader;
82    import javax.swing.table.TableCellEditor;
83    import javax.swing.table.TableCellRenderer;
84    import javax.swing.table.TableColumn;
85    import javax.xml.bind.JAXBContext;
86    import javax.xml.bind.JAXBElement;
87    import javax.xml.bind.Marshaller;
88    import javax.xml.stream.XMLInputFactory;
89    import javax.xml.stream.XMLStreamReader;
90   
91    import jalview.api.AlignViewControllerGuiI;
92    import jalview.api.AlignViewportI;
93    import jalview.api.FeatureColourI;
94    import jalview.api.FeatureSettingsControllerI;
95    import jalview.api.SplitContainerI;
96    import jalview.api.ViewStyleI;
97    import jalview.controller.FeatureSettingsControllerGuiI;
98    import jalview.datamodel.AlignmentI;
99    import jalview.datamodel.SequenceI;
100    import jalview.datamodel.features.FeatureMatcher;
101    import jalview.datamodel.features.FeatureMatcherI;
102    import jalview.datamodel.features.FeatureMatcherSet;
103    import jalview.datamodel.features.FeatureMatcherSetI;
104    import jalview.gui.Help.HelpId;
105    import jalview.gui.JalviewColourChooser.ColourChooserListener;
106    import jalview.io.JalviewFileChooser;
107    import jalview.io.JalviewFileView;
108    import jalview.schemes.FeatureColour;
109    import jalview.util.MessageManager;
110    import jalview.util.Platform;
111    import jalview.viewmodel.seqfeatures.FeatureRendererModel.FeatureSettingsBean;
112    import jalview.viewmodel.styles.ViewStyle;
113    import jalview.xml.binding.jalview.JalviewUserColours;
114    import jalview.xml.binding.jalview.JalviewUserColours.Colour;
115    import jalview.xml.binding.jalview.JalviewUserColours.Filter;
116    import jalview.xml.binding.jalview.ObjectFactory;
117   
 
118    public class FeatureSettings extends JPanel
119    implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
120    {
121    private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
122    .getString("label.sequence_feature_colours");
123   
124    /*
125    * column indices of fields in Feature Settings table
126    */
127    static final int TYPE_COLUMN = 0;
128   
129    static final int COLOUR_COLUMN = 1;
130   
131    static final int FILTER_COLUMN = 2;
132   
133    static final int SHOW_COLUMN = 3;
134   
135    private static final int COLUMN_COUNT = 4;
136   
137    private static final int MIN_WIDTH = 400;
138   
139    private static final int MIN_HEIGHT = 400;
140   
141    private final static String BASE_TOOLTIP = MessageManager
142    .getString("label.click_to_edit");
143   
144    final FeatureRenderer fr;
145   
146    public final AlignFrame af;
147   
148    /*
149    * 'original' fields hold settings to restore on Cancel
150    */
151    Object[][] originalData;
152   
153    private float originalTransparency;
154   
155    private ViewStyleI originalViewStyle;
156   
157    private Map<String, FeatureMatcherSetI> originalFilters;
158   
159    final JInternalFrame frame;
160   
161    JScrollPane scrollPane = new JScrollPane();
162   
163    JTable table;
164   
165    JPanel groupPanel;
166   
167    JSlider transparency = new JSlider();
168   
169    private JCheckBox showComplementOnTop;
170   
171    private JCheckBox showComplement;
172   
173    /*
174    * when true, constructor is still executing - so ignore UI events
175    */
176    protected volatile boolean inConstruction = true;
177   
178    int selectedRow = -1;
179   
180    boolean resettingTable = false;
181   
182    /*
183    * true when Feature Settings are updating from feature renderer
184    */
185    private boolean handlingUpdate = false;
186   
187    /*
188    * a change listener to ensure the dialog is updated if
189    * FeatureRenderer discovers new features
190    */
191    private PropertyChangeListener change;
192   
193    /*
194    * holds {featureCount, totalExtent} for each feature type
195    */
196    Map<String, float[]> typeWidth = null;
197   
 
198  1 toggle private void storeOriginalSettings()
199    {
200    // save transparency for restore on Cancel
201  1 originalTransparency = fr.getTransparency();
202   
203  1 updateTransparencySliderFromFR();
204   
205  1 originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
206  1 originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
207    }
208   
 
209  1 toggle private void updateTransparencySliderFromFR()
210    {
211  1 boolean incon = inConstruction;
212  1 inConstruction = true;
213   
214  1 int transparencyAsPercent = (int) (fr.getTransparency() * 100);
215  1 transparency.setValue(100 - transparencyAsPercent);
216  1 inConstruction = incon;
217    }
218    /**
219    * Constructor
220    *
221    * @param af
222    */
 
223  1 toggle public FeatureSettings(AlignFrame alignFrame)
224    {
225  1 this.af = alignFrame;
226  1 fr = af.getFeatureRenderer();
227   
228  1 storeOriginalSettings();
229   
230  1 try
231    {
232  1 jbInit();
233    } catch (Exception ex)
234    {
235  0 ex.printStackTrace();
236    }
237   
238  1 table = new JTable()
239    {
 
240  0 toggle @Override
241    public String getToolTipText(MouseEvent e)
242    {
243  0 String tip = null;
244  0 int column = table.columnAtPoint(e.getPoint());
245  0 int row = table.rowAtPoint(e.getPoint());
246   
247  0 switch (column)
248    {
249  0 case TYPE_COLUMN:
250  0 tip = JvSwingUtils.wrapTooltip(true, MessageManager
251    .getString("label.feature_settings_click_drag"));
252  0 break;
253  0 case COLOUR_COLUMN:
254  0 FeatureColourI colour = (FeatureColourI) table.getValueAt(row,
255    column);
256  0 tip = getColorTooltip(colour, true);
257  0 break;
258  0 case FILTER_COLUMN:
259  0 FeatureMatcherSet o = (FeatureMatcherSet) table.getValueAt(row,
260    column);
261  0 tip = o.isEmpty()
262    ? MessageManager
263    .getString("label.configure_feature_tooltip")
264    : o.toString();
265  0 break;
266  0 default:
267  0 break;
268    }
269   
270  0 return tip;
271    }
272   
273    /**
274    * Position the tooltip near the bottom edge of, and half way across, the
275    * current cell
276    */
 
277  0 toggle @Override
278    public Point getToolTipLocation(MouseEvent e)
279    {
280  0 Point point = e.getPoint();
281  0 int column = table.columnAtPoint(point);
282  0 int row = table.rowAtPoint(point);
283  0 Rectangle r = getCellRect(row, column, false);
284  0 Point loc = new Point(r.x + r.width / 2, r.y + r.height - 3);
285  0 return loc;
286    }
287    };
288  1 JTableHeader tableHeader = table.getTableHeader();
289  1 tableHeader.setFont(new Font("Verdana", Font.PLAIN, 12));
290  1 tableHeader.setReorderingAllowed(false);
291  1 table.setFont(new Font("Verdana", Font.PLAIN, 12));
292  1 ToolTipManager.sharedInstance().registerComponent(table);
293  1 table.setDefaultEditor(FeatureColour.class, new ColorEditor());
294  1 table.setDefaultRenderer(FeatureColour.class, new ColorRenderer());
295   
296  1 table.setDefaultEditor(FeatureMatcherSet.class, new FilterEditor());
297  1 table.setDefaultRenderer(FeatureMatcherSet.class, new FilterRenderer());
298   
299  1 TableColumn colourColumn = new TableColumn(COLOUR_COLUMN, 75,
300    new ColorRenderer(), new ColorEditor());
301  1 table.addColumn(colourColumn);
302   
303  1 TableColumn filterColumn = new TableColumn(FILTER_COLUMN, 75,
304    new FilterRenderer(), new FilterEditor());
305  1 table.addColumn(filterColumn);
306   
307  1 table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
308   
309  1 table.addMouseListener(new MouseAdapter()
310    {
 
311  0 toggle @Override
312    public void mousePressed(MouseEvent evt)
313    {
314  0 Point pt = evt.getPoint();
315  0 selectedRow = table.rowAtPoint(pt);
316  0 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
317  0 if (evt.isPopupTrigger())
318    {
319  0 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
320  0 showPopupMenu(selectedRow, type, colour, evt.getPoint());
321    }
322  0 else if (evt.getClickCount() == 2
323    && table.columnAtPoint(pt) == TYPE_COLUMN)
324    {
325  0 boolean invertSelection = evt.isAltDown();
326  0 boolean toggleSelection = Platform.isControlDown(evt);
327  0 boolean extendSelection = evt.isShiftDown();
328  0 fr.ap.alignFrame.avc.markColumnsContainingFeatures(
329    invertSelection, extendSelection, toggleSelection, type);
330  0 fr.ap.av.sendSelection();
331    }
332    }
333   
334    // isPopupTrigger fires on mouseReleased on Windows
 
335  0 toggle @Override
336    public void mouseReleased(MouseEvent evt)
337    {
338  0 selectedRow = table.rowAtPoint(evt.getPoint());
339  0 if (evt.isPopupTrigger())
340    {
341  0 String type = (String) table.getValueAt(selectedRow, TYPE_COLUMN);
342  0 Object colour = table.getValueAt(selectedRow, COLOUR_COLUMN);
343  0 showPopupMenu(selectedRow, type, colour, evt.getPoint());
344    }
345    }
346    });
347   
348  1 table.addMouseMotionListener(new MouseMotionAdapter()
349    {
 
350  0 toggle @Override
351    public void mouseDragged(MouseEvent evt)
352    {
353  0 int newRow = table.rowAtPoint(evt.getPoint());
354  0 if (newRow != selectedRow && selectedRow != -1 && newRow != -1)
355    {
356    /*
357    * reposition 'selectedRow' to 'newRow' (the dragged to location)
358    * this could be more than one row away for a very fast drag action
359    * so just swap it with adjacent rows until we get it there
360    */
361  0 Object[][] data = ((FeatureTableModel) table.getModel())
362    .getData();
363  0 int direction = newRow < selectedRow ? -1 : 1;
364  0 for (int i = selectedRow; i != newRow; i += direction)
365    {
366  0 Object[] temp = data[i];
367  0 data[i] = data[i + direction];
368  0 data[i + direction] = temp;
369    }
370  0 updateFeatureRenderer(data);
371  0 table.repaint();
372  0 selectedRow = newRow;
373    }
374    }
375    });
376    // table.setToolTipText(JvSwingUtils.wrapTooltip(true,
377    // MessageManager.getString("label.feature_settings_click_drag")));
378  1 scrollPane.setViewportView(table);
379   
380  1 if (af.getViewport().isShowSequenceFeatures() || !fr.hasRenderOrder())
381    {
382  1 fr.findAllFeatures(true); // display everything!
383    }
384   
385  1 discoverAllFeatureData();
386  1 final FeatureSettings fs = this;
387  1 fr.addPropertyChangeListener(change = new PropertyChangeListener()
388    {
 
389  0 toggle @Override
390    public void propertyChange(PropertyChangeEvent evt)
391    {
392  0 if (!fs.resettingTable && !fs.handlingUpdate)
393    {
394  0 fs.handlingUpdate = true;
395  0 fs.resetTable(null);
396    // new groups may be added with new sequence feature types only
397  0 fs.handlingUpdate = false;
398    }
399    }
400   
401    });
402   
403  1 SplitContainerI splitframe = af.getSplitViewContainer();
404  1 if (splitframe != null)
405    {
406  0 frame = null; // keeps eclipse happy
407  0 splitframe.addFeatureSettingsUI(this);
408    }
409    else
410    {
411  1 frame = new JInternalFrame();
412  1 frame.setContentPane(this);
413  1 Rectangle bounds = af.getFeatureSettingsGeometry();
414  1 String title;
415  1 if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
416    af.alignPanel.av.getSequenceSetId()).length > 1)
417    {
418  0 title = MessageManager.formatMessage(
419    "label.sequence_feature_settings_for_view",
420    af.alignPanel.getViewName());
421    }
422    else
423    {
424  1 title = MessageManager.getString("label.sequence_feature_settings");
425    }
426  1 if (bounds == null)
427    {
428  1 if (Platform.isAMacAndNotJS())
429    {
430  0 Desktop.addInternalFrame(frame, title, 600, 480);
431    }
432    else
433    {
434  1 Desktop.addInternalFrame(frame, title, 600, 450);
435    }
436    }
437    else
438    {
439  0 Desktop.addInternalFrame(frame, title, false, bounds.width,
440    bounds.height);
441  0 frame.setBounds(bounds);
442  0 frame.setVisible(true);
443    }
444  1 frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
445   
446  1 frame.addInternalFrameListener(
447    new javax.swing.event.InternalFrameAdapter()
448    {
 
449  0 toggle @Override
450    public void internalFrameClosed(
451    javax.swing.event.InternalFrameEvent evt)
452    {
453  0 featureSettings_isClosed();
454    };
455    });
456  1 frame.setLayer(JLayeredPane.PALETTE_LAYER);
457    }
458  1 inConstruction = false;
459    }
460   
461    /**
462    * Sets the state of buttons to show complement features from viewport
463    * settings
464    */
 
465  1 toggle private void updateComplementButtons()
466    {
467  1 showComplement.setSelected(af.getViewport().isShowComplementFeatures());
468  1 showComplementOnTop
469    .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
470    }
471   
 
472  0 toggle @Override
473    public AlignViewControllerGuiI getAlignframe()
474    {
475  0 return af;
476    }
477   
 
478  0 toggle @Override
479    public void featureSettings_isClosed()
480    {
481  0 fr.removePropertyChangeListener(change);
482  0 change = null;
483    }
484   
485    /**
486    * Constructs and shows a popup menu of possible actions on the selected row and
487    * feature type
488    *
489    * @param rowSelected
490    * @param type
491    * @param typeCol
492    * @param pt
493    */
 
494  0 toggle protected void showPopupMenu(final int rowSelected, final String type, final Object typeCol, final Point pt)
495    {
496  0 JPopupMenu men = new JPopupMenu(MessageManager
497    .formatMessage("label.settings_for_param", new String[]
498    { type }));
499  0 final FeatureColourI featureColour = (FeatureColourI) typeCol;
500   
501    /*
502    * menu option to select (or deselect) variable colour
503    */
504  0 final JCheckBoxMenuItem variableColourCB = new JCheckBoxMenuItem(
505    MessageManager.getString("label.variable_colour"));
506  0 variableColourCB.setSelected(!featureColour.isSimpleColour());
507  0 men.add(variableColourCB);
508   
509    /*
510    * checkbox action listener doubles up as listener to OK
511    * from the variable colour / filters dialog
512    */
513  0 variableColourCB.addActionListener(new ActionListener()
514    {
 
515  0 toggle @Override
516    public void actionPerformed(ActionEvent e)
517    {
518  0 if (e.getSource() == variableColourCB)
519    {
520    // BH 2018 for JavaScript because this is a checkbox
521  0 men.setVisible(true);
522  0 men.setVisible(false);
523  0 if (featureColour.isSimpleColour())
524    {
525    /*
526    * toggle simple colour to variable colour - show dialog
527    */
528  0 FeatureTypeSettings fc = new FeatureTypeSettings(fr, type);
529  0 fc.addActionListener(this);
530    }
531    else
532    {
533    /*
534    * toggle variable to simple colour - show colour chooser
535    */
536  0 String title = MessageManager
537    .formatMessage("label.select_colour_for", type);
538  0 ColourChooserListener listener = new ColourChooserListener()
539    {
 
540  0 toggle @Override
541    public void colourSelected(Color c)
542    {
543  0 table.setValueAt(new FeatureColour(c), rowSelected,
544    COLOUR_COLUMN);
545  0 table.validate();
546  0 updateFeatureRenderer(
547    ((FeatureTableModel) table.getModel()).getData(),
548    false);
549    }
550    };
551  0 JalviewColourChooser.showColourChooser(FeatureSettings.this,
552    title, featureColour.getMaxColour(), listener);
553    }
554    }
555    else
556    {
557  0 if (e.getSource() instanceof FeatureTypeSettings)
558    {
559    /*
560    * update after OK in feature colour dialog; the updated
561    * colour will have already been set in the FeatureRenderer
562    */
563  0 FeatureColourI fci = fr.getFeatureColours().get(type);
564  0 table.setValueAt(fci, rowSelected, COLOUR_COLUMN);
565    // BH 2018 setting a table value does not invalidate it.
566    // System.out.println("FeatureSettings is valied" +
567    // table.validate();
568    }
569    }
570    }
571    });
572   
573  0 men.addSeparator();
574   
575  0 JMenuItem scr = new JMenuItem(
576    MessageManager.getString("label.sort_by_score"));
577  0 men.add(scr);
578  0 scr.addActionListener(new ActionListener()
579    {
 
580  0 toggle @Override
581    public void actionPerformed(ActionEvent e)
582    {
583  0 sortByScore(Arrays.asList(new String[] { type }));
584    }
585    });
586  0 JMenuItem dens = new JMenuItem(
587    MessageManager.getString("label.sort_by_density"));
588  0 dens.addActionListener(new ActionListener()
589    {
 
590  0 toggle @Override
591    public void actionPerformed(ActionEvent e)
592    {
593  0 sortByDensity(Arrays.asList(new String[] { type }));
594    }
595    });
596  0 men.add(dens);
597   
598  0 JMenuItem selCols = new JMenuItem(
599    MessageManager.getString("label.select_columns_containing"));
600  0 selCols.addActionListener(new ActionListener()
601    {
 
602  0 toggle @Override
603    public void actionPerformed(ActionEvent arg0)
604    {
605  0 fr.ap.alignFrame.avc.markColumnsContainingFeatures(false, false,
606    false, type);
607  0 fr.ap.av.sendSelection();
608    }
609    });
610  0 JMenuItem clearCols = new JMenuItem(MessageManager
611    .getString("label.select_columns_not_containing"));
612  0 clearCols.addActionListener(new ActionListener()
613    {
 
614  0 toggle @Override
615    public void actionPerformed(ActionEvent arg0)
616    {
617  0 fr.ap.alignFrame.avc.markColumnsContainingFeatures(true, false,
618    false, type);
619  0 fr.ap.av.sendSelection();
620    }
621    });
622  0 JMenuItem hideCols = new JMenuItem(
623    MessageManager.getString("label.hide_columns_containing"));
624  0 hideCols.addActionListener(new ActionListener()
625    {
 
626  0 toggle @Override
627    public void actionPerformed(ActionEvent arg0)
628    {
629  0 fr.ap.alignFrame.hideFeatureColumns(type, true);
630  0 fr.ap.av.sendSelection();
631    }
632    });
633  0 JMenuItem hideOtherCols = new JMenuItem(
634    MessageManager.getString("label.hide_columns_not_containing"));
635  0 hideOtherCols.addActionListener(new ActionListener()
636    {
 
637  0 toggle @Override
638    public void actionPerformed(ActionEvent arg0)
639    {
640  0 fr.ap.alignFrame.hideFeatureColumns(type, false);
641  0 fr.ap.av.sendSelection();
642    }
643    });
644  0 men.add(selCols);
645  0 men.add(clearCols);
646  0 men.add(hideCols);
647  0 men.add(hideOtherCols);
648  0 men.show(table, pt.x, pt.y);
649    }
650   
651    /**
652    * Sort the sequences in the alignment by the number of features for the given
653    * feature types (or all features if null)
654    *
655    * @param featureTypes
656    */
 
657  0 toggle protected void sortByDensity(List<String> featureTypes)
658    {
659  0 af.avc.sortAlignmentByFeatureDensity(featureTypes);
660    }
661   
662    /**
663    * Sort the sequences in the alignment by average score for the given feature
664    * types (or all features if null)
665    *
666    * @param featureTypes
667    */
 
668  0 toggle protected void sortByScore(List<String> featureTypes)
669    {
670  0 af.avc.sortAlignmentByFeatureScore(featureTypes);
671    }
672   
673    /**
674    * Returns true if at least one feature type is visible. Else shows a warning
675    * dialog and returns false.
676    *
677    * @param title
678    * @return
679    */
 
680  0 toggle private boolean canSortBy(String title)
681    {
682  0 if (fr.getDisplayedFeatureTypes().isEmpty())
683    {
684  0 JvOptionPane.showMessageDialog(this,
685    MessageManager.getString("label.no_features_to_sort_by"),
686    title, JvOptionPane.OK_OPTION);
687  0 return false;
688    }
689  0 return true;
690    }
691   
 
692  1 toggle @Override
693    synchronized public void discoverAllFeatureData()
694    {
695  1 Set<String> allGroups = new HashSet<>();
696  1 AlignmentI alignment = af.getViewport().getAlignment();
697   
698  2 for (int i = 0; i < alignment.getHeight(); i++)
699    {
700  1 SequenceI seq = alignment.getSequenceAt(i);
701  1 for (String group : seq.getFeatures().getFeatureGroups(true))
702    {
703  1 if (group != null && !allGroups.contains(group))
704    {
705  1 allGroups.add(group);
706  1 checkGroupState(group);
707    }
708    }
709    }
710   
711  1 resetTable(null);
712   
713  1 validate();
714    }
715   
716    /**
717    * Synchronise gui group list and check visibility of group
718    *
719    * @param group
720    * @return true if group is visible
721    */
 
722  3 toggle private boolean checkGroupState(String group)
723    {
724  3 boolean visible = fr.checkGroupVisibility(group, true);
725   
726  3 for (int g = 0; g < groupPanel.getComponentCount(); g++)
727    {
728  2 if (((JCheckBox) groupPanel.getComponent(g)).getText().equals(group))
729    {
730  2 ((JCheckBox) groupPanel.getComponent(g)).setSelected(visible);
731  2 return visible;
732    }
733    }
734   
735  1 final String grp = group;
736  1 final JCheckBox check = new JCheckBox(group, visible);
737  1 check.setFont(new Font("Serif", Font.BOLD, 12));
738  1 check.setToolTipText(group);
739  1 check.addItemListener(new ItemListener()
740    {
 
741  0 toggle @Override
742    public void itemStateChanged(ItemEvent evt)
743    {
744  0 fr.setGroupVisibility(check.getText(), check.isSelected());
745  0 resetTable(new String[] { grp });
746  0 refreshDisplay();
747    }
748    });
749  1 groupPanel.add(check);
750  1 return visible;
751    }
752   
 
753  2 toggle synchronized void resetTable(String[] groupChanged)
754    {
755  2 if (resettingTable)
756    {
757  0 return;
758    }
759  2 resettingTable = true;
760  2 typeWidth = new Hashtable<>();
761    // TODO: change avWidth calculation to 'per-sequence' average and use long
762    // rather than float
763   
764  2 Set<String> displayableTypes = new HashSet<>();
765  2 Set<String> foundGroups = new HashSet<>();
766   
767    /*
768    * determine which feature types may be visible depending on
769    * which groups are selected, and recompute average width data
770    */
771  4 for (int i = 0; i < af.getViewport().getAlignment().getHeight(); i++)
772    {
773   
774  2 SequenceI seq = af.getViewport().getAlignment().getSequenceAt(i);
775   
776    /*
777    * get the sequence's groups for positional features
778    * and keep track of which groups are visible
779    */
780  2 Set<String> groups = seq.getFeatures().getFeatureGroups(true);
781  2 Set<String> visibleGroups = new HashSet<>();
782  2 for (String group : groups)
783    {
784  2 if (group == null || checkGroupState(group))
785    {
786  2 visibleGroups.add(group);
787    }
788    }
789  2 foundGroups.addAll(groups);
790   
791    /*
792    * get distinct feature types for visible groups
793    * record distinct visible types, and their count and total length
794    */
795  2 Set<String> types = seq.getFeatures().getFeatureTypesForGroups(true,
796    visibleGroups.toArray(new String[visibleGroups.size()]));
797  2 for (String type : types)
798    {
799  10 displayableTypes.add(type);
800  10 float[] avWidth = typeWidth.get(type);
801  10 if (avWidth == null)
802    {
803  10 avWidth = new float[2];
804  10 typeWidth.put(type, avWidth);
805    }
806    // todo this could include features with a non-visible group
807    // - do we greatly care?
808    // todo should we include non-displayable features here, and only
809    // update when features are added?
810  10 avWidth[0] += seq.getFeatures().getFeatureCount(true, type);
811  10 avWidth[1] += seq.getFeatures().getTotalFeatureLength(type);
812    }
813    }
814   
815  2 Object[][] data = new Object[displayableTypes.size()][COLUMN_COUNT];
816  2 int dataIndex = 0;
817   
818  2 if (fr.hasRenderOrder())
819    {
820  2 if (!handlingUpdate)
821    {
822  2 fr.findAllFeatures(groupChanged != null); // prod to update
823    // colourschemes. but don't
824    // affect display
825    // First add the checks in the previous render order,
826    // in case the window has been closed and reopened
827    }
828  2 List<String> frl = fr.getRenderOrder();
829  12 for (int ro = frl.size() - 1; ro > -1; ro--)
830    {
831  10 String type = frl.get(ro);
832   
833  10 if (!displayableTypes.contains(type))
834    {
835  0 continue;
836    }
837   
838  10 data[dataIndex][TYPE_COLUMN] = type;
839  10 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
840  10 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
841  10 data[dataIndex][FILTER_COLUMN] = featureFilter == null
842    ? new FeatureMatcherSet()
843    : featureFilter;
844  10 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(
845    af.getViewport().getFeaturesDisplayed().isVisible(type));
846  10 dataIndex++;
847  10 displayableTypes.remove(type);
848    }
849    }
850   
851    /*
852    * process any extra features belonging only to
853    * a group which was just selected
854    */
855  2 while (!displayableTypes.isEmpty())
856    {
857  0 String type = displayableTypes.iterator().next();
858  0 data[dataIndex][TYPE_COLUMN] = type;
859   
860  0 data[dataIndex][COLOUR_COLUMN] = fr.getFeatureStyle(type);
861  0 if (data[dataIndex][COLOUR_COLUMN] == null)
862    {
863    // "Colour has been updated in another view!!"
864  0 fr.clearRenderOrder();
865  0 return;
866    }
867  0 FeatureMatcherSetI featureFilter = fr.getFeatureFilter(type);
868  0 data[dataIndex][FILTER_COLUMN] = featureFilter == null
869    ? new FeatureMatcherSet()
870    : featureFilter;
871  0 data[dataIndex][SHOW_COLUMN] = Boolean.valueOf(true);
872  0 dataIndex++;
873  0 displayableTypes.remove(type);
874    }
875   
876  2 if (originalData == null)
877    {
878  1 originalData = new Object[data.length][COLUMN_COUNT];
879  6 for (int i = 0; i < data.length; i++)
880    {
881  5 System.arraycopy(data[i], 0, originalData[i], 0, COLUMN_COUNT);
882    }
883    }
884    else
885    {
886  1 updateOriginalData(data);
887    }
888   
889  2 table.setModel(new FeatureTableModel(data));
890  2 table.getColumnModel().getColumn(0).setPreferredWidth(200);
891   
892  2 groupPanel.setLayout(
893    new GridLayout(fr.getFeatureGroupsSize() / 4 + 1, 4));
894  2 pruneGroups(foundGroups);
895  2 groupPanel.validate();
896   
897  2 updateFeatureRenderer(data, groupChanged != null);
898  2 resettingTable = false;
899    }
900   
901    /**
902    * Updates 'originalData' (used for restore on Cancel) if we detect that
903    * changes have been made outwith this dialog
904    * <ul>
905    * <li>a new feature type added (and made visible)</li>
906    * <li>a feature colour changed (in the Amend Features dialog)</li>
907    * </ul>
908    *
909    * @param foundData
910    */
 
911  1 toggle protected void updateOriginalData(Object[][] foundData)
912    {
913    // todo LinkedHashMap instead of Object[][] would be nice
914   
915  1 Object[][] currentData = ((FeatureTableModel) table.getModel())
916    .getData();
917  1 for (Object[] row : foundData)
918    {
919  5 String type = (String) row[TYPE_COLUMN];
920  5 boolean found = false;
921  5 for (Object[] current : currentData)
922    {
923  15 if (type.equals(current[TYPE_COLUMN]))
924    {
925  5 found = true;
926    /*
927    * currently dependent on object equality here;
928    * really need an equals method on FeatureColour
929    */
930  5 if (!row[COLOUR_COLUMN].equals(current[COLOUR_COLUMN]))
931    {
932    /*
933    * feature colour has changed externally - update originalData
934    */
935  5 for (Object[] original : originalData)
936    {
937  15 if (type.equals(original[TYPE_COLUMN]))
938    {
939  5 original[COLOUR_COLUMN] = row[COLOUR_COLUMN];
940  5 break;
941    }
942    }
943    }
944  5 break;
945    }
946    }
947  5 if (!found)
948    {
949    /*
950    * new feature detected - add to original data (on top)
951    */
952  0 Object[][] newData = new Object[originalData.length
953    + 1][COLUMN_COUNT];
954  0 for (int i = 0; i < originalData.length; i++)
955    {
956  0 System.arraycopy(originalData[i], 0, newData[i + 1], 0,
957    COLUMN_COUNT);
958    }
959  0 newData[0] = row;
960  0 originalData = newData;
961    }
962    }
963    }
964   
965    /**
966    * Remove from the groups panel any checkboxes for groups that are not in the
967    * foundGroups set. This enables removing a group from the display when the
968    * last feature in that group is deleted.
969    *
970    * @param foundGroups
971    */
 
972  2 toggle protected void pruneGroups(Set<String> foundGroups)
973    {
974  4 for (int g = 0; g < groupPanel.getComponentCount(); g++)
975    {
976  2 JCheckBox checkbox = (JCheckBox) groupPanel.getComponent(g);
977  2 if (!foundGroups.contains(checkbox.getText()))
978    {
979  0 groupPanel.remove(checkbox);
980    }
981    }
982    }
983   
984    /**
985    * reorder data based on the featureRenderers global priority list.
986    *
987    * @param data
988    */
 
989  1 toggle private void ensureOrder(Object[][] data)
990    {
991  1 boolean sort = false;
992  1 float[] order = new float[data.length];
993  6 for (int i = 0; i < order.length; i++)
994    {
995  5 order[i] = fr.getOrder(data[i][0].toString());
996  5 if (order[i] < 0)
997    {
998  0 order[i] = fr.setOrder(data[i][0].toString(), i / order.length);
999    }
1000  5 if (i > 1)
1001    {
1002  3 sort = sort || order[i - 1] > order[i];
1003    }
1004    }
1005  1 if (sort)
1006    {
1007  0 jalview.util.QuickSort.sort(order, data);
1008    }
1009    }
1010   
1011    /**
1012    * Offers a file chooser dialog, and then loads the feature colours and
1013    * filters from file in XML format and unmarshals to Jalview feature settings
1014    */
 
1015  0 toggle void load()
1016    {
1017  0 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1018    SEQUENCE_FEATURE_COLOURS);
1019  0 chooser.setFileView(new JalviewFileView());
1020  0 chooser.setDialogTitle(
1021    MessageManager.getString("label.load_feature_colours"));
1022  0 chooser.setToolTipText(MessageManager.getString("action.load"));
1023  0 chooser.setResponseHandler(0, new Runnable()
1024    {
 
1025  0 toggle @Override
1026    public void run()
1027    {
1028  0 File file = chooser.getSelectedFile();
1029  0 load(file);
1030    }
1031    });
1032  0 chooser.showOpenDialog(this);
1033    }
1034   
1035    /**
1036    * Loads feature colours and filters from XML stored in the given file
1037    *
1038    * @param file
1039    */
 
1040  1 toggle void load(File file)
1041    {
1042  1 try
1043    {
1044  1 InputStreamReader in = new InputStreamReader(
1045    new FileInputStream(file), "UTF-8");
1046   
1047  1 JAXBContext jc = JAXBContext
1048    .newInstance("jalview.xml.binding.jalview");
1049  1 javax.xml.bind.Unmarshaller um = jc.createUnmarshaller();
1050  1 XMLStreamReader streamReader = XMLInputFactory.newInstance()
1051    .createXMLStreamReader(in);
1052  1 JAXBElement<JalviewUserColours> jbe = um.unmarshal(streamReader,
1053    JalviewUserColours.class);
1054  1 JalviewUserColours jucs = jbe.getValue();
1055   
1056    // JalviewUserColours jucs = JalviewUserColours.unmarshal(in);
1057   
1058    /*
1059    * load feature colours
1060    */
1061  6 for (int i = jucs.getColour().size() - 1; i >= 0; i--)
1062    {
1063  5 Colour newcol = jucs.getColour().get(i);
1064  5 FeatureColourI colour = jalview.project.Jalview2XML
1065    .parseColour(newcol);
1066  5 fr.setColour(newcol.getName(), colour);
1067  5 fr.setOrder(newcol.getName(), i / (float) jucs.getColour().size());
1068    }
1069   
1070    /*
1071    * load feature filters; loaded filters will replace any that are
1072    * currently defined, other defined filters are left unchanged
1073    */
1074  4 for (int i = 0; i < jucs.getFilter().size(); i++)
1075    {
1076  3 Filter filterModel = jucs.getFilter().get(i);
1077  3 String featureType = filterModel.getFeatureType();
1078  3 FeatureMatcherSetI filter = jalview.project.Jalview2XML
1079    .parseFilter(featureType, filterModel.getMatcherSet());
1080  3 if (!filter.isEmpty())
1081    {
1082  3 fr.setFeatureFilter(featureType, filter);
1083    }
1084    }
1085   
1086    /*
1087    * update feature settings table
1088    */
1089  1 if (table != null)
1090    {
1091  1 resetTable(null);
1092  1 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1093  1 ensureOrder(data);
1094  1 updateFeatureRenderer(data, false);
1095  1 table.repaint();
1096    }
1097    } catch (Exception ex)
1098    {
1099  0 System.out.println("Error loading User Colour File\n" + ex);
1100    }
1101    }
1102   
1103    /**
1104    * Offers a file chooser dialog, and then saves the current feature colours
1105    * and any filters to the selected file in XML format
1106    */
 
1107  0 toggle void save()
1108    {
1109  0 JalviewFileChooser chooser = new JalviewFileChooser("fc",
1110    SEQUENCE_FEATURE_COLOURS);
1111  0 chooser.setFileView(new JalviewFileView());
1112  0 chooser.setDialogTitle(
1113    MessageManager.getString("label.save_feature_colours"));
1114  0 chooser.setToolTipText(MessageManager.getString("action.save"));
1115  0 int option = chooser.showSaveDialog(this);
1116  0 if (option == JalviewFileChooser.APPROVE_OPTION)
1117    {
1118  0 File file = chooser.getSelectedFile();
1119  0 save(file);
1120    }
1121    }
1122   
1123    /**
1124    * Saves feature colours and filters to the given file
1125    *
1126    * @param file
1127    */
 
1128  1 toggle void save(File file)
1129    {
1130  1 JalviewUserColours ucs = new JalviewUserColours();
1131  1 ucs.setSchemeName("Sequence Features");
1132  1 try
1133    {
1134  1 PrintWriter out = new PrintWriter(
1135    new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
1136   
1137    /*
1138    * sort feature types by colour order, from 0 (highest)
1139    * to 1 (lowest)
1140    */
1141  1 Set<String> fr_colours = fr.getAllFeatureColours();
1142  1 String[] sortedTypes = fr_colours
1143    .toArray(new String[fr_colours.size()]);
1144  1 Arrays.sort(sortedTypes, new Comparator<String>()
1145    {
 
1146  4 toggle @Override
1147    public int compare(String type1, String type2)
1148    {
1149  4 return Float.compare(fr.getOrder(type1), fr.getOrder(type2));
1150    }
1151    });
1152   
1153    /*
1154    * save feature colours
1155    */
1156  1 for (String featureType : sortedTypes)
1157    {
1158  5 FeatureColourI fcol = fr.getFeatureStyle(featureType);
1159  5 Colour col = jalview.project.Jalview2XML.marshalColour(featureType,
1160    fcol);
1161  5 ucs.getColour().add(col);
1162    }
1163   
1164    /*
1165    * save any feature filters
1166    */
1167  1 for (String featureType : sortedTypes)
1168    {
1169  5 FeatureMatcherSetI filter = fr.getFeatureFilter(featureType);
1170  5 if (filter != null && !filter.isEmpty())
1171    {
1172  3 Iterator<FeatureMatcherI> iterator = filter.getMatchers()
1173    .iterator();
1174  3 FeatureMatcherI firstMatcher = iterator.next();
1175  3 jalview.xml.binding.jalview.FeatureMatcherSet ms = jalview.project.Jalview2XML
1176    .marshalFilter(firstMatcher, iterator, filter.isAnded());
1177  3 Filter filterModel = new Filter();
1178  3 filterModel.setFeatureType(featureType);
1179  3 filterModel.setMatcherSet(ms);
1180  3 ucs.getFilter().add(filterModel);
1181    }
1182    }
1183  1 JAXBContext jaxbContext = JAXBContext
1184    .newInstance(JalviewUserColours.class);
1185  1 Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
1186  1 jaxbMarshaller.marshal(
1187    new ObjectFactory().createJalviewUserColours(ucs), out);
1188   
1189    // jaxbMarshaller.marshal(object, pout);
1190    // marshaller.marshal(object);
1191  1 out.flush();
1192   
1193    // ucs.marshal(out);
1194  1 out.close();
1195    } catch (Exception ex)
1196    {
1197  0 ex.printStackTrace();
1198    }
1199    }
1200   
 
1201  0 toggle public void invertSelection()
1202    {
1203  0 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1204  0 for (int i = 0; i < data.length; i++)
1205    {
1206  0 data[i][SHOW_COLUMN] = !(Boolean) data[i][SHOW_COLUMN];
1207    }
1208  0 updateFeatureRenderer(data, true);
1209  0 table.repaint();
1210    }
1211   
 
1212  0 toggle public void orderByAvWidth()
1213    {
1214  0 if (table == null || table.getModel() == null)
1215    {
1216  0 return;
1217    }
1218  0 Object[][] data = ((FeatureTableModel) table.getModel()).getData();
1219  0 float[] width = new float[data.length];
1220  0 float[] awidth;
1221  0 float max = 0;
1222   
1223  0 for (int i = 0; i < data.length; i++)
1224    {
1225  0 awidth = typeWidth.get(data[i][TYPE_COLUMN]);
1226  0 if (awidth[0] > 0)
1227    {
1228  0 width[i] = awidth[1] / awidth[0];// *awidth[0]*awidth[2]; - better
1229    // weight - but have to make per
1230    // sequence, too (awidth[2])
1231    // if (width[i]==1) // hack to distinguish single width sequences.
1232    }
1233    else
1234    {
1235  0 width[i] = 0;
1236    }
1237  0 if (max < width[i])
1238    {
1239  0 max = width[i];
1240    }
1241    }
1242  0 boolean sort = false;
1243  0 for (int i = 0; i < width.length; i++)
1244    {
1245    // awidth = (float[]) typeWidth.get(data[i][0]);
1246  0 if (width[i] == 0)
1247    {
1248  0 width[i] = fr.getOrder(data[i][TYPE_COLUMN].toString());
1249  0 if (width[i] < 0)
1250    {
1251  0 width[i] = fr.setOrder(data[i][TYPE_COLUMN].toString(),
1252    i / data.length);
1253    }
1254    }
1255    else
1256    {
1257  0 width[i] /= max; // normalize
1258  0 fr.setOrder(data[i][TYPE_COLUMN].toString(), width[i]); // store for
1259    // later
1260    }
1261  0 if (i > 0)
1262    {
1263  0 sort = sort || width[i - 1] > width[i];
1264    }
1265    }
1266  0 if (sort)
1267    {
1268  0 jalview.util.QuickSort.sort(width, data);
1269    // update global priority order
1270    }
1271   
1272  0 updateFeatureRenderer(data, false);
1273  0 table.repaint();
1274    }
1275   
1276    /**
1277    * close ourselves but leave any existing UI handlers (e.g a CDS/Protein tabbed
1278    * feature settings dialog) intact
1279    */
 
1280  0 toggle public void closeOldSettings()
1281    {
1282  0 closeDialog(false);
1283    }
1284   
1285    /**
1286    * close the feature settings dialog (and any containing frame)
1287    */
 
1288  0 toggle public void close()
1289    {
1290  0 closeDialog(true);
1291    }
1292   
 
1293  0 toggle private void closeDialog(boolean closeContainingFrame)
1294    {
1295  0 try
1296    {
1297  0 if (frame != null)
1298    {
1299  0 af.setFeatureSettingsGeometry(frame.getBounds());
1300  0 frame.setClosed(true);
1301    }
1302    else
1303    {
1304  0 SplitContainerI sc = af.getSplitViewContainer();
1305  0 sc.closeFeatureSettings(this, closeContainingFrame);
1306  0 af.featureSettings = null;
1307    }
1308    } catch (Exception exe)
1309    {
1310    }
1311   
1312    }
1313   
 
1314  0 toggle public void updateFeatureRenderer(Object[][] data)
1315    {
1316  0 updateFeatureRenderer(data, true);
1317    }
1318   
1319    /**
1320    * Update the priority order of features; only repaint if this changed the
1321    * order of visible features
1322    *
1323    * @param data
1324    * @param visibleNew
1325    */
 
1326  3 toggle void updateFeatureRenderer(Object[][] data, boolean visibleNew)
1327    {
1328  3 FeatureSettingsBean[] rowData = getTableAsBeans(data);
1329   
1330  3 if (fr.setFeaturePriority(rowData, visibleNew))
1331    {
1332  0 refreshDisplay();
1333    }
1334    }
1335   
1336    /**
1337    * Converts table data into an array of data beans
1338    */
 
1339  3 toggle private FeatureSettingsBean[] getTableAsBeans(Object[][] data)
1340    {
1341  3 FeatureSettingsBean[] rowData = new FeatureSettingsBean[data.length];
1342  18 for (int i = 0; i < data.length; i++)
1343    {
1344  15 String type = (String) data[i][TYPE_COLUMN];
1345  15 FeatureColourI colour = (FeatureColourI) data[i][COLOUR_COLUMN];
1346  15 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) data[i][FILTER_COLUMN];
1347  15 Boolean isShown = (Boolean) data[i][SHOW_COLUMN];
1348  15 rowData[i] = new FeatureSettingsBean(type, colour, theFilter,
1349    isShown);
1350    }
1351  3 return rowData;
1352    }
1353   
 
1354  1 toggle private void jbInit() throws Exception
1355    {
1356  1 this.setLayout(new BorderLayout());
1357   
1358  1 final boolean hasComplement = af.getViewport()
1359    .getCodingComplement() != null;
1360   
1361  1 JPanel settingsPane = new JPanel();
1362  1 settingsPane.setLayout(new BorderLayout());
1363   
1364  1 JPanel bigPanel = new JPanel();
1365  1 bigPanel.setLayout(new BorderLayout());
1366   
1367  1 groupPanel = new JPanel();
1368  1 bigPanel.add(groupPanel, BorderLayout.NORTH);
1369   
1370  1 JButton invert = new JButton(
1371    MessageManager.getString("label.invert_selection"));
1372  1 invert.setFont(JvSwingUtils.getLabelFont());
1373  1 invert.addActionListener(new ActionListener()
1374    {
 
1375  0 toggle @Override
1376    public void actionPerformed(ActionEvent e)
1377    {
1378  0 invertSelection();
1379    }
1380    });
1381   
1382  1 JButton optimizeOrder = new JButton(
1383    MessageManager.getString("label.optimise_order"));
1384  1 optimizeOrder.setFont(JvSwingUtils.getLabelFont());
1385  1 optimizeOrder.addActionListener(new ActionListener()
1386    {
 
1387  0 toggle @Override
1388    public void actionPerformed(ActionEvent e)
1389    {
1390  0 orderByAvWidth();
1391    }
1392    });
1393   
1394  1 final String byScoreLabel = MessageManager.getString("label.seq_sort_by_score");
1395  1 JButton sortByScore = new JButton(byScoreLabel);
1396  1 sortByScore.setFont(JvSwingUtils.getLabelFont());
1397  1 sortByScore.addActionListener(new ActionListener()
1398    {
 
1399  0 toggle @Override
1400    public void actionPerformed(ActionEvent e)
1401    {
1402  0 if (canSortBy(byScoreLabel))
1403    {
1404  0 sortByScore(null);
1405    }
1406    }
1407    });
1408  1 final String byDensityLabel = MessageManager.getString("label.sequence_sort_by_density");
1409  1 JButton sortByDens = new JButton(byDensityLabel);
1410  1 sortByDens.setFont(JvSwingUtils.getLabelFont());
1411  1 sortByDens.addActionListener(new ActionListener()
1412    {
 
1413  0 toggle @Override
1414    public void actionPerformed(ActionEvent e)
1415    {
1416  0 if (canSortBy(byDensityLabel))
1417    {
1418  0 sortByDensity(null);
1419    }
1420    }
1421    });
1422   
1423  1 JButton help = new JButton(MessageManager.getString("action.help"));
1424  1 help.setFont(JvSwingUtils.getLabelFont());
1425  1 help.addActionListener(new ActionListener()
1426    {
 
1427  0 toggle @Override
1428    public void actionPerformed(ActionEvent e)
1429    {
1430  0 try
1431    {
1432  0 Help.showHelpWindow(HelpId.SequenceFeatureSettings);
1433    } catch (HelpSetException e1)
1434    {
1435  0 e1.printStackTrace();
1436    }
1437    }
1438    });
1439    // Cancel for a SplitFrame should just revert changes to the currently displayed
1440    // settings. May want to do this for either or both - so need a splitview
1441    // feature settings cancel/OK.
1442  1 JButton cancel = new JButton(MessageManager
1443  1 .getString(hasComplement ? "action.revert" : "action.cancel"));
1444  1 cancel.setToolTipText(MessageManager.getString(hasComplement
1445    ? "action.undo_changes_to_feature_settings"
1446    : "action.undo_changes_to_feature_settings_and_close_the_dialog"));
1447  1 cancel.setFont(JvSwingUtils.getLabelFont());
1448    // TODO: disable cancel (and apply!) until current settings are different
1449  1 cancel.addActionListener(new ActionListener()
1450    {
 
1451  0 toggle @Override
1452    public void actionPerformed(ActionEvent e)
1453    {
1454  0 revert();
1455  0 refreshDisplay();
1456  0 if (!hasComplement)
1457    {
1458  0 close();
1459    }
1460    }
1461    });
1462    // Cancel for the whole dialog should cancel both CDS and Protein.
1463    // OK for an individual feature settings just applies changes, but dialog
1464    // remains open
1465  1 JButton ok = new JButton(MessageManager
1466  1 .getString(hasComplement ? "action.apply" : "action.ok"));
1467  1 ok.setFont(JvSwingUtils.getLabelFont());
1468  1 ok.addActionListener(new ActionListener()
1469    {
 
1470  0 toggle @Override
1471    public void actionPerformed(ActionEvent e)
1472    {
1473  0 if (!hasComplement)
1474    {
1475  0 close();
1476    }
1477    else
1478    {
1479  0 storeOriginalSettings();
1480    }
1481    }
1482    });
1483   
1484  1 JButton loadColours = new JButton(
1485    MessageManager.getString("label.load_colours"));
1486  1 loadColours.setFont(JvSwingUtils.getLabelFont());
1487  1 loadColours.setToolTipText(
1488    MessageManager.getString("label.load_colours_tooltip"));
1489  1 loadColours.addActionListener(new ActionListener()
1490    {
 
1491  0 toggle @Override
1492    public void actionPerformed(ActionEvent e)
1493    {
1494  0 load();
1495    }
1496    });
1497   
1498  1 JButton saveColours = new JButton(
1499    MessageManager.getString("label.save_colours"));
1500  1 saveColours.setFont(JvSwingUtils.getLabelFont());
1501  1 saveColours.setToolTipText(
1502    MessageManager.getString("label.save_colours_tooltip"));
1503  1 saveColours.addActionListener(new ActionListener()
1504    {
 
1505  0 toggle @Override
1506    public void actionPerformed(ActionEvent e)
1507    {
1508  0 save();
1509    }
1510    });
1511  1 transparency.addChangeListener(new ChangeListener()
1512    {
 
1513  1 toggle @Override
1514    public void stateChanged(ChangeEvent evt)
1515    {
1516  1 if (!inConstruction)
1517    {
1518  0 fr.setTransparency((100 - transparency.getValue()) / 100f);
1519  0 refreshDisplay();
1520    }
1521    }
1522    });
1523   
1524  1 transparency.setMaximum(70);
1525  1 transparency.setToolTipText(
1526    MessageManager.getString("label.transparency_tip"));
1527   
1528  1 boolean nucleotide = af.getViewport().getAlignment().isNucleotide();
1529  1 String text = MessageManager.formatMessage("label.show_linked_features",
1530  1 nucleotide
1531    ? MessageManager.getString("label.protein")
1532    .toLowerCase()
1533    : "CDS");
1534  1 showComplement = new JCheckBox(text);
1535  1 showComplement.addActionListener(new ActionListener()
1536    {
 
1537  0 toggle @Override
1538    public void actionPerformed(ActionEvent e)
1539    {
1540  0 af.getViewport()
1541    .setShowComplementFeatures(showComplement.isSelected());
1542  0 refreshDisplay();
1543    }
1544    });
1545   
1546  1 showComplementOnTop = new JCheckBox(
1547    MessageManager.getString("label.on_top"));
1548  1 showComplementOnTop.addActionListener(new ActionListener()
1549    {
 
1550  0 toggle @Override
1551    public void actionPerformed(ActionEvent e)
1552    {
1553  0 af.getViewport().setShowComplementFeaturesOnTop(
1554    showComplementOnTop.isSelected());
1555  0 refreshDisplay();
1556    }
1557    });
1558   
1559  1 updateComplementButtons();
1560   
1561  1 JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
1562  1 bigPanel.add(lowerPanel, BorderLayout.SOUTH);
1563   
1564  1 JPanel transbuttons = new JPanel(new GridLayout(5, 1));
1565  1 transbuttons.add(optimizeOrder);
1566  1 transbuttons.add(invert);
1567  1 transbuttons.add(sortByScore);
1568  1 transbuttons.add(sortByDens);
1569  1 transbuttons.add(help);
1570   
1571  1 JPanel transPanelLeft = new JPanel(
1572  1 new GridLayout(hasComplement ? 4 : 2, 1));
1573  1 transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
1574  1 transPanelLeft.add(transparency);
1575  1 if (hasComplement)
1576    {
1577  0 JPanel cp = new JPanel(new FlowLayout(FlowLayout.LEFT));
1578  0 cp.add(showComplement);
1579  0 cp.add(showComplementOnTop);
1580  0 transPanelLeft.add(cp);
1581    }
1582  1 lowerPanel.add(transPanelLeft);
1583  1 lowerPanel.add(transbuttons);
1584   
1585  1 JPanel buttonPanel = new JPanel();
1586  1 buttonPanel.add(ok);
1587  1 buttonPanel.add(cancel);
1588  1 buttonPanel.add(loadColours);
1589  1 buttonPanel.add(saveColours);
1590  1 bigPanel.add(scrollPane, BorderLayout.CENTER);
1591  1 settingsPane.add(bigPanel, BorderLayout.CENTER);
1592  1 settingsPane.add(buttonPanel, BorderLayout.SOUTH);
1593  1 this.add(settingsPane);
1594    }
1595   
1596    /**
1597    * Repaints alignment, structure and overview (if shown). If there is a
1598    * complementary view which is showing this view's features, then also
1599    * repaints that.
1600    */
 
1601  0 toggle void refreshDisplay()
1602    {
1603  0 af.alignPanel.paintAlignment(true, true);
1604  0 AlignViewportI complement = af.getViewport().getCodingComplement();
1605  0 if (complement != null && complement.isShowComplementFeatures())
1606    {
1607  0 AlignFrame af2 = Desktop.getAlignFrameFor(complement);
1608  0 af2.alignPanel.paintAlignment(true, true);
1609    }
1610    }
1611   
1612    /**
1613    * Answers a suitable tooltip to show on the colour cell of the table
1614    *
1615    * @param fcol
1616    * @param withHint
1617    * if true include 'click to edit' and similar text
1618    * @return
1619    */
 
1620  9 toggle public static String getColorTooltip(FeatureColourI fcol,
1621    boolean withHint)
1622    {
1623  9 if (fcol == null)
1624    {
1625  1 return null;
1626    }
1627  8 if (fcol.isSimpleColour())
1628    {
1629  2 return withHint ? BASE_TOOLTIP : null;
1630    }
1631  6 String description = fcol.getDescription();
1632  6 description = description.replaceAll("<", "&lt;");
1633  6 description = description.replaceAll(">", "&gt;");
1634  6 StringBuilder tt = new StringBuilder(description);
1635  6 if (withHint)
1636    {
1637  3 tt.append("<br>").append(BASE_TOOLTIP).append("</br>");
1638    }
1639  6 return JvSwingUtils.wrapTooltip(true, tt.toString());
1640    }
1641   
 
1642  16 toggle public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol,
1643    int w, int h)
1644    {
1645  16 boolean thr = false;
1646  16 StringBuilder tx = new StringBuilder();
1647   
1648  16 if (gcol.isColourByAttribute())
1649    {
1650  8 tx.append(FeatureMatcher
1651    .toAttributeDisplayName(gcol.getAttributeName()));
1652    }
1653  8 else if (!gcol.isColourByLabel())
1654    {
1655  4 tx.append(MessageManager.getString("label.score"));
1656    }
1657  16 tx.append(" ");
1658  16 if (gcol.isAboveThreshold())
1659    {
1660  4 thr = true;
1661  4 tx.append(">");
1662    }
1663  16 if (gcol.isBelowThreshold())
1664    {
1665  4 thr = true;
1666  4 tx.append("<");
1667    }
1668  16 if (gcol.isColourByLabel())
1669    {
1670  8 if (thr)
1671    {
1672  0 tx.append(" ");
1673    }
1674  8 if (!gcol.isColourByAttribute())
1675    {
1676  4 tx.append("Label");
1677    }
1678  8 comp.setIcon(null);
1679    }
1680    else
1681    {
1682  8 Color newColor = gcol.getMaxColour();
1683  8 comp.setBackground(newColor);
1684    // System.err.println("Width is " + w / 2);
1685  8 Icon ficon = new FeatureIcon(gcol, comp.getBackground(), w, h, thr);
1686  8 comp.setIcon(ficon);
1687    // tt+="RGB value: Max (" + newColor.getRed() + ", "
1688    // + newColor.getGreen() + ", " + newColor.getBlue()
1689    // + ")\nMin (" + minCol.getRed() + ", " + minCol.getGreen()
1690    // + ", " + minCol.getBlue() + ")");
1691    }
1692  16 comp.setHorizontalAlignment(SwingConstants.CENTER);
1693  16 comp.setText(tx.toString());
1694    }
1695   
1696    // ///////////////////////////////////////////////////////////////////////
1697    // http://java.sun.com/docs/books/tutorial/uiswing/components/table.html
1698    // ///////////////////////////////////////////////////////////////////////
 
1699    class FeatureTableModel extends AbstractTableModel
1700    {
1701    private String[] columnNames = {
1702    MessageManager.getString("label.feature_type"),
1703    MessageManager.getString("action.colour"),
1704    MessageManager.getString("label.configuration"),
1705    MessageManager.getString("label.show") };
1706   
1707    private Object[][] data;
1708   
 
1709  2 toggle FeatureTableModel(Object[][] data)
1710    {
1711  2 this.data = data;
1712    }
1713   
 
1714  2 toggle public Object[][] getData()
1715    {
1716  2 return data;
1717    }
1718   
 
1719  0 toggle public void setData(Object[][] data)
1720    {
1721  0 this.data = data;
1722    }
1723   
 
1724  10 toggle @Override
1725    public int getColumnCount()
1726    {
1727  10 return columnNames.length;
1728    }
1729   
 
1730  0 toggle public Object[] getRow(int row)
1731    {
1732  0 return data[row];
1733    }
1734   
 
1735  80 toggle @Override
1736    public int getRowCount()
1737    {
1738  80 return data.length;
1739    }
1740   
 
1741  8 toggle @Override
1742    public String getColumnName(int col)
1743    {
1744  8 return columnNames[col];
1745    }
1746   
 
1747  40 toggle @Override
1748    public Object getValueAt(int row, int col)
1749    {
1750  40 return data[row][col];
1751    }
1752   
1753    /**
1754    * Answers the class of column c of the table
1755    */
 
1756  40 toggle @Override
1757    public Class<?> getColumnClass(int c)
1758    {
1759  40 switch (c)
1760    {
1761  20 case TYPE_COLUMN:
1762  20 return String.class;
1763  20 case COLOUR_COLUMN:
1764  20 return FeatureColour.class;
1765  0 case FILTER_COLUMN:
1766  0 return FeatureMatcherSet.class;
1767  0 default:
1768  0 return Boolean.class;
1769    }
1770    }
1771   
 
1772  0 toggle @Override
1773    public boolean isCellEditable(int row, int col)
1774    {
1775  0 return col == 0 ? false : true;
1776    }
1777   
 
1778  0 toggle @Override
1779    public void setValueAt(Object value, int row, int col)
1780    {
1781  0 data[row][col] = value;
1782  0 fireTableCellUpdated(row, col);
1783  0 updateFeatureRenderer(data);
1784    }
1785   
1786    }
1787   
 
1788    class ColorRenderer extends JLabel implements TableCellRenderer
1789    {
1790    Border unselectedBorder = null;
1791   
1792    Border selectedBorder = null;
1793   
 
1794  2 toggle public ColorRenderer()
1795    {
1796  2 setOpaque(true); // MUST do this for background to show up.
1797  2 setHorizontalTextPosition(SwingConstants.CENTER);
1798  2 setVerticalTextPosition(SwingConstants.CENTER);
1799    }
1800   
 
1801  20 toggle @Override
1802    public Component getTableCellRendererComponent(JTable tbl, Object color,
1803    boolean isSelected, boolean hasFocus, int row, int column)
1804    {
1805  20 FeatureColourI cellColour = (FeatureColourI) color;
1806  20 setOpaque(true);
1807  20 setBackground(tbl.getBackground());
1808  20 if (!cellColour.isSimpleColour())
1809    {
1810  16 Rectangle cr = tbl.getCellRect(row, column, false);
1811  16 FeatureSettings.renderGraduatedColor(this, cellColour,
1812    (int) cr.getWidth(), (int) cr.getHeight());
1813    }
1814    else
1815    {
1816  4 this.setText("");
1817  4 this.setIcon(null);
1818  4 setBackground(cellColour.getColour());
1819    }
1820  20 if (isSelected)
1821    {
1822  0 if (selectedBorder == null)
1823    {
1824  0 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1825    tbl.getSelectionBackground());
1826    }
1827  0 setBorder(selectedBorder);
1828    }
1829    else
1830    {
1831  20 if (unselectedBorder == null)
1832    {
1833  1 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1834    tbl.getBackground());
1835    }
1836  20 setBorder(unselectedBorder);
1837    }
1838   
1839  20 return this;
1840    }
1841    }
1842   
 
1843    class FilterRenderer extends JLabel implements TableCellRenderer
1844    {
1845    javax.swing.border.Border unselectedBorder = null;
1846   
1847    javax.swing.border.Border selectedBorder = null;
1848   
 
1849  2 toggle public FilterRenderer()
1850    {
1851  2 setOpaque(true); // MUST do this for background to show up.
1852  2 setHorizontalTextPosition(SwingConstants.CENTER);
1853  2 setVerticalTextPosition(SwingConstants.CENTER);
1854    }
1855   
 
1856  0 toggle @Override
1857    public Component getTableCellRendererComponent(JTable tbl,
1858    Object filter, boolean isSelected, boolean hasFocus, int row,
1859    int column)
1860    {
1861  0 FeatureMatcherSetI theFilter = (FeatureMatcherSetI) filter;
1862  0 setOpaque(true);
1863  0 String asText = theFilter.toString();
1864  0 setBackground(tbl.getBackground());
1865  0 this.setText(asText);
1866  0 this.setIcon(null);
1867   
1868  0 if (isSelected)
1869    {
1870  0 if (selectedBorder == null)
1871    {
1872  0 selectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1873    tbl.getSelectionBackground());
1874    }
1875  0 setBorder(selectedBorder);
1876    }
1877    else
1878    {
1879  0 if (unselectedBorder == null)
1880    {
1881  0 unselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5,
1882    tbl.getBackground());
1883    }
1884  0 setBorder(unselectedBorder);
1885    }
1886   
1887  0 return this;
1888    }
1889    }
1890   
1891    /**
1892    * update comp using rendering settings from gcol
1893    *
1894    * @param comp
1895    * @param gcol
1896    */
 
1897  0 toggle public static void renderGraduatedColor(JLabel comp, FeatureColourI gcol)
1898    {
1899  0 int w = comp.getWidth(), h = comp.getHeight();
1900  0 if (w < 20)
1901    {
1902  0 w = (int) comp.getPreferredSize().getWidth();
1903  0 h = (int) comp.getPreferredSize().getHeight();
1904  0 if (w < 20)
1905    {
1906  0 w = 80;
1907  0 h = 12;
1908    }
1909    }
1910  0 renderGraduatedColor(comp, gcol, w, h);
1911    }
1912   
1913    @SuppressWarnings("serial")
 
1914    class ColorEditor extends AbstractCellEditor
1915    implements TableCellEditor, ActionListener
1916    {
1917    FeatureColourI currentColor;
1918   
1919    FeatureTypeSettings chooser;
1920   
1921    String type;
1922   
1923    JButton button;
1924   
1925    protected static final String EDIT = "edit";
1926   
1927    int rowSelected = 0;
1928   
 
1929  2 toggle public ColorEditor()
1930    {
1931    // Set up the editor (from the table's point of view),
1932    // which is a button.
1933    // This button brings up the color chooser dialog,
1934    // which is the editor from the user's point of view.
1935  2 button = new JButton();
1936  2 button.setActionCommand(EDIT);
1937  2 button.addActionListener(this);
1938  2 button.setBorderPainted(false);
1939    }
1940   
1941    /**
1942    * Handles events from the editor button, and from the colour/filters
1943    * dialog's OK button
1944    */
 
1945  0 toggle @Override
1946    public void actionPerformed(ActionEvent e)
1947    {
1948  0 if (button == e.getSource())
1949    {
1950  0 if (currentColor.isSimpleColour())
1951    {
1952    /*
1953    * simple colour chooser
1954    */
1955  0 String ttl = MessageManager
1956    .formatMessage("label.select_colour_for", type);
1957  0 ColourChooserListener listener = new ColourChooserListener()
1958    {
 
1959  0 toggle @Override
1960    public void colourSelected(Color c)
1961    {
1962  0 currentColor = new FeatureColour(c);
1963  0 table.setValueAt(currentColor, rowSelected, COLOUR_COLUMN);
1964  0 fireEditingStopped();
1965    }
1966   
 
1967  0 toggle @Override
1968    public void cancel()
1969    {
1970  0 fireEditingStopped();
1971    }
1972    };
1973  0 JalviewColourChooser.showColourChooser(button, ttl,
1974    currentColor.getColour(), listener);
1975    }
1976    else
1977    {
1978    /*
1979    * variable colour and filters dialog
1980    */
1981  0 chooser = new FeatureTypeSettings(fr, type);
1982  0 if (!Platform.isJS())
1983    /**
1984    * Java only
1985    *
1986    * @j2sIgnore
1987    */
1988    {
1989  0 chooser.setRequestFocusEnabled(true);
1990  0 chooser.requestFocus();
1991    }
1992  0 chooser.addActionListener(this);
1993  0 fireEditingStopped();
1994    }
1995    }
1996    else
1997    {
1998    /*
1999    * after OK in variable colour dialog, any changes to colour
2000    * (or filters!) are already set in FeatureRenderer, so just
2001    * update table data without triggering updateFeatureRenderer
2002    */
2003  0 currentColor = fr.getFeatureColours().get(type);
2004  0 FeatureMatcherSetI currentFilter = fr.getFeatureFilter(type);
2005  0 if (currentFilter == null)
2006    {
2007  0 currentFilter = new FeatureMatcherSet();
2008    }
2009  0 Object[] data = ((FeatureTableModel) table.getModel())
2010    .getData()[rowSelected];
2011  0 data[COLOUR_COLUMN] = currentColor;
2012  0 data[FILTER_COLUMN] = currentFilter;
2013  0 fireEditingStopped();
2014    // SwingJS needs an explicit repaint() here,
2015    // rather than relying upon no validation having
2016    // occurred since the stopEditing call was made.
2017    // Its laying out has not been stopped by the modal frame
2018  0 table.validate();
2019  0 table.repaint();
2020    }
2021    }
2022   
2023    /**
2024    * Override allows access to this method from anonymous inner classes
2025    */
 
2026  0 toggle @Override
2027    protected void fireEditingStopped()
2028    {
2029  0 super.fireEditingStopped();
2030    }
2031   
2032    // Implement the one CellEditor method that AbstractCellEditor doesn't.
 
2033  0 toggle @Override
2034    public Object getCellEditorValue()
2035    {
2036  0 return currentColor;
2037    }
2038   
2039    // Implement the one method defined by TableCellEditor.
 
2040  0 toggle @Override
2041    public Component getTableCellEditorComponent(JTable theTable,
2042    Object value, boolean isSelected, int row, int column)
2043    {
2044  0 currentColor = (FeatureColourI) value;
2045  0 this.rowSelected = row;
2046  0 type = table.getValueAt(row, TYPE_COLUMN).toString();
2047  0 button.setOpaque(true);
2048  0 button.setBackground(FeatureSettings.this.getBackground());
2049  0 if (!currentColor.isSimpleColour())
2050    {
2051  0 JLabel btn = new JLabel();
2052  0 btn.setSize(button.getSize());
2053  0 FeatureSettings.renderGraduatedColor(btn, currentColor);
2054  0 button.setBackground(btn.getBackground());
2055  0 button.setIcon(btn.getIcon());
2056  0 button.setText(btn.getText());
2057    }
2058    else
2059    {
2060  0 button.setText("");
2061  0 button.setIcon(null);
2062  0 button.setBackground(currentColor.getColour());
2063    }
2064  0 return button;
2065    }
2066    }
2067   
2068    /**
2069    * The cell editor for the Filter column. It displays the text of any filters
2070    * for the feature type in that row (in full as a tooltip, possible
2071    * abbreviated as display text). On click in the cell, opens the Feature
2072    * Display Settings dialog at the Filters tab.
2073    */
2074    @SuppressWarnings("serial")
 
2075    class FilterEditor extends AbstractCellEditor
2076    implements TableCellEditor, ActionListener
2077    {
2078   
2079    FeatureMatcherSetI currentFilter;
2080   
2081    Point lastLocation;
2082   
2083    String type;
2084   
2085    JButton button;
2086   
2087    protected static final String EDIT = "edit";
2088   
2089    int rowSelected = 0;
2090   
 
2091  2 toggle public FilterEditor()
2092    {
2093  2 button = new JButton();
2094  2 button.setActionCommand(EDIT);
2095  2 button.addActionListener(this);
2096  2 button.setBorderPainted(false);
2097    }
2098   
2099    /**
2100    * Handles events from the editor button
2101    */
 
2102  0 toggle @Override
2103    public void actionPerformed(ActionEvent e)
2104    {
2105  0 if (button == e.getSource())
2106    {
2107  0 FeatureTypeSettings chooser = new FeatureTypeSettings(fr, type);
2108  0 chooser.addActionListener(this);
2109  0 chooser.setRequestFocusEnabled(true);
2110  0 chooser.requestFocus();
2111  0 if (lastLocation != null)
2112    {
2113    // todo open at its last position on screen
2114  0 chooser.setBounds(lastLocation.x, lastLocation.y,
2115    chooser.getWidth(), chooser.getHeight());
2116  0 chooser.validate();
2117    }
2118  0 fireEditingStopped();
2119    }
2120  0 else if (e.getSource() instanceof Component)
2121    {
2122   
2123    /*
2124    * after OK in variable colour dialog, any changes to filter
2125    * (or colours!) are already set in FeatureRenderer, so just
2126    * update table data without triggering updateFeatureRenderer
2127    */
2128  0 FeatureColourI currentColor = fr.getFeatureColours().get(type);
2129  0 currentFilter = fr.getFeatureFilter(type);
2130  0 if (currentFilter == null)
2131    {
2132  0 currentFilter = new FeatureMatcherSet();
2133    }
2134   
2135  0 Object[] data = ((FeatureTableModel) table.getModel())
2136    .getData()[rowSelected];
2137  0 data[COLOUR_COLUMN] = currentColor;
2138  0 data[FILTER_COLUMN] = currentFilter;
2139  0 fireEditingStopped();
2140    // SwingJS needs an explicit repaint() here,
2141    // rather than relying upon no validation having
2142    // occurred since the stopEditing call was made.
2143    // Its laying out has not been stopped by the modal frame
2144  0 table.validate();
2145  0 table.repaint();
2146    }
2147    }
2148   
 
2149  0 toggle @Override
2150    public Object getCellEditorValue()
2151    {
2152  0 return currentFilter;
2153    }
2154   
 
2155  0 toggle @Override
2156    public Component getTableCellEditorComponent(JTable theTable,
2157    Object value, boolean isSelected, int row, int column)
2158    {
2159  0 currentFilter = (FeatureMatcherSetI) value;
2160  0 this.rowSelected = row;
2161  0 type = table.getValueAt(row, TYPE_COLUMN).toString();
2162  0 button.setOpaque(true);
2163  0 button.setBackground(FeatureSettings.this.getBackground());
2164  0 button.setText(currentFilter.toString());
2165  0 button.setIcon(null);
2166  0 return button;
2167    }
2168    }
2169   
 
2170  0 toggle public boolean isOpen()
2171    {
2172  0 if (af.getSplitViewContainer() != null)
2173    {
2174  0 return af.getSplitViewContainer().isFeatureSettingsOpen();
2175    }
2176  0 return frame != null && !frame.isClosed();
2177    }
2178   
 
2179  0 toggle @Override
2180    public void revert()
2181    {
2182  0 fr.setTransparency(originalTransparency);
2183  0 fr.setFeatureFilters(originalFilters);
2184  0 updateFeatureRenderer(originalData);
2185  0 af.getViewport().setViewStyle(originalViewStyle);
2186  0 updateTransparencySliderFromFR();
2187  0 updateComplementButtons();
2188  0 refreshDisplay();
2189    }
2190    }
2191   
 
2192    class FeatureIcon implements Icon
2193    {
2194    FeatureColourI gcol;
2195   
2196    Color backg;
2197   
2198    boolean midspace = false;
2199   
2200    int width = 50, height = 20;
2201   
2202    int s1, e1; // start and end of midpoint band for thresholded symbol
2203   
2204    Color mpcolour = Color.white;
2205   
 
2206  8 toggle FeatureIcon(FeatureColourI gfc, Color bg, int w, int h, boolean mspace)
2207    {
2208  8 gcol = gfc;
2209  8 backg = bg;
2210  8 width = w;
2211  8 height = h;
2212  8 midspace = mspace;
2213  8 if (midspace)
2214    {
2215  8 s1 = width / 3;
2216  8 e1 = s1 * 2;
2217    }
2218    else
2219    {
2220  0 s1 = width / 2;
2221  0 e1 = s1;
2222    }
2223    }
2224   
 
2225  8 toggle @Override
2226    public int getIconWidth()
2227    {
2228  8 return width;
2229    }
2230   
 
2231  8 toggle @Override
2232    public int getIconHeight()
2233    {
2234  8 return height;
2235    }
2236   
 
2237  8 toggle @Override
2238    public void paintIcon(Component c, Graphics g, int x, int y)
2239    {
2240   
2241  8 if (gcol.isColourByLabel())
2242    {
2243  0 g.setColor(backg);
2244  0 g.fillRect(0, 0, width, height);
2245    // need an icon here.
2246  0 g.setColor(gcol.getMaxColour());
2247   
2248  0 g.setFont(new Font("Verdana", Font.PLAIN, 9));
2249   
2250    // g.setFont(g.getFont().deriveFont(
2251    // AffineTransform.getScaleInstance(
2252    // width/g.getFontMetrics().stringWidth("Label"),
2253    // height/g.getFontMetrics().getHeight())));
2254   
2255  0 g.drawString(MessageManager.getString("label.label"), 0, 0);
2256   
2257    }
2258    else
2259    {
2260  8 Color minCol = gcol.getMinColour();
2261  8 g.setColor(minCol);
2262  8 g.fillRect(0, 0, s1, height);
2263  8 if (midspace)
2264    {
2265  8 g.setColor(Color.white);
2266  8 g.fillRect(s1, 0, e1 - s1, height);
2267    }
2268  8 g.setColor(gcol.getMaxColour());
2269    // g.fillRect(0, e1, width - e1, height); // BH 2018
2270  8 g.fillRect(e1, 0, width - e1, height);
2271    }
2272    }
2273    }