}
+task cloverReportHTML (type: JavaExec) {
+ inputs.dir "${buildDir}/clover"
+ outputs.dir "${reportsDir}/clover"
+
+ classpath configurations.cloverRuntime
+ maxHeapSize "${cloverReportJVMHeap}"
+ jvmArgs += "${cloverReportJVMArgs}"
+ main = "com.atlassian.clover.reporters.html.HtmlReporter"
+ args "--initstring", "${buildDir}/clover/clover.db", "-o", "${reportsDir}/clover"
+ "${cloverReportHTMLOptions}".split(",").each {
+ args+= it.trim()
+ }
+}
+
+task cloverReportXML (type: JavaExec) {
+ inputs.dir "${buildDir}/clover"
+ outputs.dir "${reportsDir}/clover"
+ maxHeapSize "${cloverReportJVMHeap}"
+ jvmArgs "${cloverReportJVMArgs}"
+ classpath configurations.cloverRuntime
+ main = "com.atlassian.clover.reporters.xml.XMLReporter"
+ args "--initstring", "${buildDir}/clover/clover.db", "-o", "${reportsDir}/clover/clover.xml"
+
+ "${cloverReportXMLOptions}".split(",").each {
+ args+= it.trim()
+ }
+}
task cloverReport {
group = "Verification"
- description = "Createst the Clover report"
+ description = "Creates the Clover report"
inputs.dir "${buildDir}/clover"
outputs.dir "${reportsDir}/clover"
onlyIf {
file("${buildDir}/clover/clover.db").exists()
}
- doFirst {
- def argsList = ["--initstring", "${buildDir}/clover/clover.db",
- "-o", "${reportsDir}/clover"]
- String[] args = argsList.toArray()
- com.atlassian.clover.reporters.html.HtmlReporter.runReport(args)
-
- // and generate ${reportsDir}/clover/clover.xml
- args = ["--initstring", "${buildDir}/clover/clover.db",
- "-o", "${reportsDir}/clover/clover.xml"].toArray()
- com.atlassian.clover.reporters.xml.XMLReporter.runReport(args)
- }
+ dependsOn cloverReportXML
+ dependsOn cloverReportHTML
}
// end clover bits
in the `jalview` root folder.
+## Enabling Code Coverage with OpenClover
+
+Bytecode instrumentation tasks are enabled by specifying 'true' (or just a non-whitespace non-numeric word) in the 'clover' property. This adds the 'openclover' plugin to the build script's classpath, making it possible to track code execution during test which can be viewed as an HTML report published at build/reports/clover/index.html.
+
+```gradle -Pclover=true test cloverReport```
+
+#### Troubleshooting report generation
+
+The build forks a new JVM process to run the clover report generation tools (both XML and HTML reports are generated by default). The following properties can be used to specify additional options or adjust JVM memory settings. Default values for these options are:
+
+##### JVM Memory settings - increase if out of memory errors are reported
+
+```cloverReportJVMHeap = 2g```
+
+##### -Dfile.encoding=UTF-8 is an essential parameters for report generation. Add additional ones separated by a space.
+
+```cloverReportJVMArgs = -Dfile.encoding=UTF-8```
+
+##### Add -v to debug velocity html generation errors, or -d to track more detailed issues with the coverage database
+
+```cloverReportHTMLOptions = ```
+
+##### -v for verbose, -d for debug level messages (as above)
+
+```cloverReportXMLOptions = ```
+
+
+_Note_ do not forget to include the -Dfile.encoding=UTF-8 option: this is essential for some platforms in order for Clover to correctly parse some Jalview source files that contain characters that are UTF-8 encoded.
+
+
---
[Jalview Development Team](mailto:help@jalview.org)
examplesDir = examples
clover = false
use_clover = false
+cloverReportJVMHeap = 2g
+cloverReportJVMArgs = -Dfile.encoding=UTF-8
+cloverReportHTMLOptions =
+cloverReportXMLOptions =
cloverClassesDir = clover-classes
cloverSourcesInstrDir = sources-instr
packageDir = dist
<em>25/2/2020</em></strong></td>
<td align="left" valign="top">
<ul>
+ <li><!-- JAL-3121 -->Feature attributes from VCF files can be exported and re-imported as GFF3 files
+ </li>
<li>
<!-- JAL-3376 -->Record "fixed column" values POS,
ID, QUAL, FILTER from VCF as Feature Attributes
action.deselect_all = Deselect all
action.invert_selection = Invert selection
action.using_jmol = Using Jmol
+action.undo_changes_to_feature_settings = Undo all unapplied changes to feature settings
+action.undo_changes_to_feature_settings_and_close_the_dialog = Undo all pending changes and close the feature settings dialog
action.link = Link
action.group_link = Group Link
action.show_chain = Show Chain
label.protein = Protein
label.nucleotides = Nucleotides
label.proteins = Proteins
+label.CDS = CDS
label.to_new_alignment = To New Alignment
label.to_this_alignment = Add To This Alignment
label.apply_colour_to_all_groups = Apply Colour To All Groups
label.generating_annotations_for_params = Generating annotations for - {0}
label.varna_params = VARNA - {0}
label.sequence_feature_settings = Sequence Feature Settings
+label.sequence_feature_settings_for = Sequence Feature Settings for {0}
+label.sequence_feature_settings_for_view = Sequence Feature Settings for view "{0}"
+label.sequence_feature_settings_for_CDS_and_Protein = Sequence Feature Settings for CDS and Protein
label.pairwise_aligned_sequences = Pairwise Aligned Sequences
label.original_data_for_params = Original Data for {0}
label.points_for_params = Points for {0}
label.click_to_edit = Click to edit, right-click for menu
label.by_annotation_tooltip = Annotation Colour is configured from the main Colour menu
label.show_linked_features = Show {0} features
+label.show_linked_feature_settings = Open {0} settings
label.on_top = on top
label.include_linked_features = Include {0} features
label.include_linked_tooltip = Include visible {0} features<br>converted to local sequence coordinates
\ No newline at end of file
*/
package jalview.analysis;
-import static jalview.io.gff.GffConstants.CLINICAL_SIGNIFICANCE;
-
import jalview.commands.RemoveGapColCommand;
import jalview.datamodel.AlignedCodon;
import jalview.datamodel.AlignedCodonFrame;
import jalview.datamodel.SequenceGroup;
import jalview.datamodel.SequenceI;
import jalview.datamodel.features.SequenceFeatures;
-import jalview.io.gff.Gff3Helper;
import jalview.io.gff.SequenceOntologyI;
import jalview.schemes.ResidueProperties;
import jalview.util.Comparison;
import jalview.util.IntRangeComparator;
import jalview.util.MapList;
import jalview.util.MappingUtils;
-import jalview.util.StringUtils;
-import java.io.UnsupportedEncodingException;
-import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
cdsSeqs.add(cdsSeq);
- if (!dataset.getSequences().contains(cdsSeqDss))
- {
- // check if this sequence is a newly created one
- // so needs adding to the dataset
- dataset.addSequence(cdsSeqDss);
- }
-
/*
- * add a mapping from CDS to the (unchanged) mapped to range
+ * build the mapping from CDS to protein
*/
List<int[]> cdsRange = Collections
.singletonList(new int[]
MapList cdsToProteinMap = new MapList(cdsRange,
mapList.getToRanges(), mapList.getFromRatio(),
mapList.getToRatio());
- AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame();
- cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct,
- cdsToProteinMap);
- /*
- * guard against duplicating the mapping if repeating this action
- */
- if (!mappings.contains(cdsToProteinMapping))
+ if (!dataset.getSequences().contains(cdsSeqDss))
{
- mappings.add(cdsToProteinMapping);
+ /*
+ * if this sequence is a newly created one, add it to the dataset
+ * and made a CDS to protein mapping (if sequence already exists,
+ * CDS-to-protein mapping _is_ the transcript-to-protein mapping)
+ */
+ dataset.addSequence(cdsSeqDss);
+ AlignedCodonFrame cdsToProteinMapping = new AlignedCodonFrame();
+ cdsToProteinMapping.addMap(cdsSeqDss, proteinProduct,
+ cdsToProteinMap);
+
+ /*
+ * guard against duplicating the mapping if repeating this action
+ */
+ if (!mappings.contains(cdsToProteinMapping))
+ {
+ mappings.add(cdsToProteinMapping);
+ }
}
propagateDBRefsToCDS(cdsSeqDss, dnaSeq.getDatasetSequence(),
{
List<SequenceI> alignedSequences = alignedDatasets
.get(seq.getDatasetSequence());
+ if (alignedSequences.isEmpty())
+ {
+ /*
+ * defensive check - shouldn't happen! (JAL-3536)
+ */
+ continue;
+ }
SequenceI alignedSeq = alignedSequences.get(0);
/*
import jalview.commands.CommandI;
import jalview.schemes.ColourSchemeI;
+import java.awt.Rectangle;
+
/**
* Interface implemented by gui implementations managing a Jalview Alignment
* View
* @return
*/
FeatureSettingsControllerI getFeatureSettingsUI();
+
+ /**
+ * displays the Feature Settigns control panel for the alignment view - if one
+ * exists it is closed and re-opened.
+ *
+ * @return the current feature settings controller
+ */
+ FeatureSettingsControllerI showFeatureSettingsUI();
+
+ /**
+ * record the last position of a feature settings dialog before it was closed
+ *
+ * @param bounds
+ */
+ void setFeatureSettingsGeometry(Rectangle bounds);
+
+ /**
+ *
+ * @return last position of feature settings for this alignment view GUI
+ */
+ Rectangle getFeatureSettingsGeometry();
}
*/
package jalview.api;
+import jalview.controller.FeatureSettingsControllerGuiI;
import jalview.datamodel.AlignmentI;
/**
*/
String getComplementTitle(Object af);
+ /**
+ * get the 'other' alignFrame in the SplitFrame
+ *
+ * @param alignFrame
+ * @return the complement alignFrame - or null if alignFrame wasn't held by this
+ * frame
+ */
+ AlignViewControllerGuiI getComplementAlignFrame(
+ AlignViewControllerGuiI alignFrame);
+
+ /**
+ * add the given UI to the splitframe's feature settings UI holder
+ *
+ * @param featureSettings
+ * @return
+ */
+ void addFeatureSettingsUI(
+ FeatureSettingsControllerGuiI featureSettings);
+
+ /**
+ * Request to close all feature settings originating from a particular panel.
+ *
+ * @param featureSettings
+ * @param closeContainingFrame
+ * - if false then the tab containing the feature
+ * settings will be 'reset' ready for a new
+ * feature settings
+ */
+ void closeFeatureSettings(FeatureSettingsControllerI featureSettings,
+ boolean closeContainingFrame);
+
+ /**
+ *
+ * @return true if a feature settings panel is currently open
+ */
+ boolean isFeatureSettingsOpen();
+
}
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.Panel;
+import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusEvent;
}
else if (source == featureSettings)
{
- new FeatureSettings(alignPanel);
+ showFeatureSettingsUI();
}
else if (source == alProperties)
{
return alignPanel.av.featureSettings;
}
+ @Override
+ public FeatureSettingsControllerI showFeatureSettingsUI()
+ {
+ return new FeatureSettings(alignPanel);
+ }
+
+ private Rectangle fs_bounds = null;
+
+ @Override
+ public void setFeatureSettingsGeometry(Rectangle bounds)
+ {
+ fs_bounds = bounds;
+ }
+
+ @Override
+ public Rectangle getFeatureSettingsGeometry()
+ {
+ return fs_bounds;
+ }
+
}
*/
package jalview.controller;
+import jalview.api.AlignViewControllerGuiI;
+
public interface FeatureSettingsControllerGuiI
{
+ AlignViewControllerGuiI getAlignframe();
+
+ void featureSettings_isClosed();
+
+ /**
+ * undo any changes made to feature settings whilst the dialog has been visible,
+ * since the last 'apply'
+ */
+ void revert();
+
}
closeView(alignPanel);
}
}
-
if (closeAllTabs)
{
+ if (featureSettings != null && featureSettings.isOpen())
+ {
+ featureSettings.close();
+ featureSettings = null;
+ }
/*
* this will raise an INTERNAL_FRAME_CLOSED event and this method will
* be called recursively, with the frame now in 'closed' state
@Override
public void featureSettings_actionPerformed(ActionEvent e)
{
+ showFeatureSettingsUI();
+ }
+
+ @Override
+ public FeatureSettingsControllerI showFeatureSettingsUI()
+ {
if (featureSettings != null)
{
- featureSettings.close();
+ featureSettings.closeOldSettings();
featureSettings = null;
}
if (!showSeqFeatures.isSelected())
showSeqFeatures_actionPerformed(null);
}
featureSettings = new FeatureSettings(this);
+ return featureSettings;
}
/**
{
if (parseFeaturesFile(file, sourceType))
{
- alignPanel.paintAlignment(true, true);
+ SplitFrame splitFrame = (SplitFrame) getSplitViewContainer();
+ if (splitFrame != null)
+ {
+ splitFrame.repaint();
+ }
+ else
+ {
+ alignPanel.paintAlignment(true, true);
+ }
}
}
else
viewport = alignPanel.av;
avc.setViewportAndAlignmentPanel(viewport, alignPanel);
setMenusFromViewport(viewport);
+ if (featureSettings != null && featureSettings.isOpen()
+ && featureSettings.fr.getViewport() != viewport)
+ {
+ if (viewport.isShowSequenceFeatures())
+ {
+ // refresh the featureSettings to reflect UI change
+ showFeatureSettingsUI();
+ }
+ else
+ {
+ // close feature settings for this view.
+ featureSettings.close();
+ }
+ }
+
}
/*
}
}
+
+ private Rectangle lastFeatureSettingsBounds = null;
+ @Override
+ public void setFeatureSettingsGeometry(Rectangle bounds)
+ {
+ lastFeatureSettingsBounds = bounds;
+ }
+
+ @Override
+ public Rectangle getFeatureSettingsGeometry()
+ {
+ return lastFeatureSettingsBounds;
+ }
}
class PrintThread extends Thread
boolean antiAlias = false;
- private Rectangle explodedGeometry;
+ private Rectangle explodedGeometry = null;
- private String viewName;
+ private String viewName = null;
/*
* Flag set true on the view that should 'gather' multiple views of the same
return;
}
+ // FIXME: ideally should use UI interface API
+ FeatureSettings viewFeatureSettings = (af.featureSettings != null
+ && af.featureSettings.isOpen())
+ ? af.featureSettings
+ : null;
+ Rectangle fsBounds = af.getFeatureSettingsGeometry();
for (int i = 0; i < size; i++)
{
AlignmentPanel ap = af.alignPanels.get(i);
+
AlignFrame newaf = new AlignFrame(ap);
+ // transfer reference for existing feature settings to new alignFrame
+ if (ap == af.alignPanel)
+ {
+ if (viewFeatureSettings != null && viewFeatureSettings.fr.ap == ap)
+ {
+ newaf.featureSettings = viewFeatureSettings;
+ }
+ newaf.setFeatureSettingsGeometry(fsBounds);
+ }
+
/*
* Restore the view's last exploded frame geometry if known. Multiple
* views from one exploded frame share and restore the same (frame)
addInternalFrame(newaf, af.getTitle(), AlignFrame.DEFAULT_WIDTH,
AlignFrame.DEFAULT_HEIGHT);
+ // and materialise a new feature settings dialog instance for the new alignframe
+ // (closes the old as if 'OK' was pressed)
+ if (ap == af.alignPanel && newaf.featureSettings != null
+ && newaf.featureSettings.isOpen()
+ && af.alignPanel.getAlignViewport().isShowSequenceFeatures())
+ {
+ newaf.showFeatureSettingsUI();
+ }
}
+ af.featureSettings = null;
af.alignPanels.clear();
af.closeMenuItem_actionPerformed(true);
source.viewport.setExplodedGeometry(source.getBounds());
JInternalFrame[] frames = desktop.getAllFrames();
String viewId = source.viewport.getSequenceSetId();
-
for (int t = 0; t < frames.length; t++)
{
if (frames[t] instanceof AlignFrame && frames[t] != source)
if (gatherThis)
{
+ if (af.featureSettings != null && af.featureSettings.isOpen())
+ {
+ if (source.featureSettings == null)
+ {
+ // preserve the feature settings geometry for this frame
+ source.featureSettings = af.featureSettings;
+ source.setFeatureSettingsGeometry(
+ af.getFeatureSettingsGeometry());
+ }
+ else
+ {
+ // close it and forget
+ af.featureSettings.close();
+ }
+ }
af.alignPanels.clear();
af.closeMenuItem_actionPerformed(true);
}
}
}
+ // refresh the feature setting UI for the source frame if it exists
+ if (source.featureSettings != null
+ && source.featureSettings.isOpen())
+ {
+ source.showFeatureSettingsUI();
+ }
}
*/
package jalview.gui;
+import jalview.api.AlignViewControllerGuiI;
import jalview.api.AlignViewportI;
import jalview.api.FeatureColourI;
import jalview.api.FeatureSettingsControllerI;
+import jalview.api.SplitContainerI;
import jalview.api.ViewStyleI;
+import jalview.controller.FeatureSettingsControllerGuiI;
import jalview.datamodel.AlignmentI;
import jalview.datamodel.SequenceI;
import jalview.datamodel.features.FeatureMatcher;
import java.util.Set;
import javax.help.HelpSetException;
+import javax.swing.AbstractButton;
import javax.swing.AbstractCellEditor;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.xml.stream.XMLStreamReader;
public class FeatureSettings extends JPanel
- implements FeatureSettingsControllerI
+ implements FeatureSettingsControllerI, FeatureSettingsControllerGuiI
{
private static final String SEQUENCE_FEATURE_COLOURS = MessageManager
.getString("label.sequence_feature_colours");
*/
Map<String, float[]> typeWidth = null;
+ private void storeOriginalSettings()
+ {
+ // save transparency for restore on Cancel
+ originalTransparency = fr.getTransparency();
+
+ updateTransparencySliderFromFR();
+
+ originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
+ originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
+ }
+
+ private void updateTransparencySliderFromFR()
+ {
+ boolean incon = inConstruction;
+ inConstruction = true;
+
+ int transparencyAsPercent = (int) (fr.getTransparency() * 100);
+ transparency.setValue(100 - transparencyAsPercent);
+ inConstruction = incon;
+ }
/**
* Constructor
*
this.af = alignFrame;
fr = af.getFeatureRenderer();
- // save transparency for restore on Cancel
- originalTransparency = fr.getTransparency();
- int originalTransparencyAsPercent = (int) (originalTransparency * 100);
- transparency.setMaximum(100 - originalTransparencyAsPercent);
-
- originalFilters = new HashMap<>(fr.getFeatureFilters()); // shallow copy
- originalViewStyle = new ViewStyle(af.viewport.getViewStyle());
+ storeOriginalSettings();
try
{
}
discoverAllFeatureData();
- final PropertyChangeListener change;
final FeatureSettings fs = this;
fr.addPropertyChangeListener(change = new PropertyChangeListener()
{
}
});
-
- frame = new JInternalFrame();
- frame.setContentPane(this);
- if (Platform.isAMac())
+ SplitContainerI splitframe = af.getSplitViewContainer();
+ if (splitframe != null)
{
- Desktop.addInternalFrame(frame,
- MessageManager.getString("label.sequence_feature_settings"),
- 600, 480);
+ frame = null; // keeps eclipse happy
+ splitframe.addFeatureSettingsUI(this);
}
else
{
- Desktop.addInternalFrame(frame,
- MessageManager.getString("label.sequence_feature_settings"),
- 600, 450);
- }
- frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
+ frame = new JInternalFrame();
+ frame.setContentPane(this);
+ Rectangle bounds = af.getFeatureSettingsGeometry();
+ String title;
+ if (af.getAlignPanels().size() > 1 || Desktop.getAlignmentPanels(
+ af.alignPanel.av.getSequenceSetId()).length > 1)
+ {
+ title = MessageManager.formatMessage(
+ "label.sequence_feature_settings_for_view",
+ af.alignPanel.getViewName());
+ }
+ else
+ {
+ title = MessageManager.getString("label.sequence_feature_settings");
+ }
+ if (bounds == null)
+ {
+ if (Platform.isAMac())
+ {
+ Desktop.addInternalFrame(frame, title, 600, 480);
+ }
+ else
+ {
+ Desktop.addInternalFrame(frame, title, 600, 450);
+ }
+ }
+ else
+ {
+ Desktop.addInternalFrame(frame, title,
+ false, bounds.width, bounds.height);
+ frame.setBounds(bounds);
+ frame.setVisible(true);
+ }
+ frame.setMinimumSize(new Dimension(MIN_WIDTH, MIN_HEIGHT));
- frame.addInternalFrameListener(
- new javax.swing.event.InternalFrameAdapter()
- {
- @Override
- public void internalFrameClosed(
- javax.swing.event.InternalFrameEvent evt)
+ frame.addInternalFrameListener(
+ new javax.swing.event.InternalFrameAdapter()
{
- fr.removePropertyChangeListener(change);
- };
- });
- frame.setLayer(JLayeredPane.PALETTE_LAYER);
+ @Override
+ public void internalFrameClosed(
+ javax.swing.event.InternalFrameEvent evt)
+ {
+ featureSettings_isClosed();
+ };
+ });
+ frame.setLayer(JLayeredPane.PALETTE_LAYER);
+ }
inConstruction = false;
}
+ PropertyChangeListener change;
+
+ private JCheckBox showComplementOnTop;
+
+ private AbstractButton showComplement;
+
+ private void updateComplementButtons()
+ {
+ showComplement.setSelected(af.getViewport().isShowComplementFeatures());
+ showComplementOnTop
+ .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
+ }
+
+ @Override
+ public AlignViewControllerGuiI getAlignframe()
+ {
+ return af;
+ }
+
+ @Override
+ public void featureSettings_isClosed()
+ {
+ fr.removePropertyChangeListener(change);
+ change = null;
+ }
+
protected void popupSort(final int rowSelected, final String type,
final Object typeCol, final Map<String, float[][]> minmax, int x,
int y)
table.repaint();
}
+ /**
+ * close ourselves but leave any existing UI handlers (e.g a CDS/Protein tabbed
+ * feature settings dialog) intact
+ */
+ public void closeOldSettings()
+ {
+ closeDialog(false);
+ }
+
+ /**
+ * close the feature settings dialog (and any containing frame)
+ */
public void close()
{
+ closeDialog(true);
+ }
+
+ private void closeDialog(boolean closeContainingFrame)
+ {
try
{
- frame.setClosed(true);
+ if (frame != null)
+ {
+ af.setFeatureSettingsGeometry(frame.getBounds());
+ frame.setClosed(true);
+ }
+ else
+ {
+ SplitContainerI sc = af.getSplitViewContainer();
+ sc.closeFeatureSettings(this, closeContainingFrame);
+ af.featureSettings = null;
+ }
} catch (Exception exe)
{
}
{
this.setLayout(new BorderLayout());
+ final boolean hasComplement = af.getViewport()
+ .getCodingComplement() != null;
+
JPanel settingsPane = new JPanel();
settingsPane.setLayout(new BorderLayout());
}
}
});
-
- JButton cancel = new JButton(MessageManager.getString("action.cancel"));
+ // Cancel for a SplitFrame should just revert changes to the currently displayed
+ // settings. May want to do this for either or both - so need a splitview
+ // feature settings cancel/OK.
+ JButton cancel = new JButton(MessageManager
+ .getString(hasComplement ? "action.revert" : "action.cancel"));
+ cancel.setToolTipText(MessageManager.getString(hasComplement
+ ? "action.undo_changes_to_feature_settings"
+ : "action.undo_changes_to_feature_settings_and_close_the_dialog"));
cancel.setFont(JvSwingUtils.getLabelFont());
+ // TODO: disable cancel (and apply!) until current settings are different
cancel.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- fr.setTransparency(originalTransparency);
- fr.setFeatureFilters(originalFilters);
- updateFeatureRenderer(originalData);
- af.getViewport().setViewStyle(originalViewStyle);
- close();
+ revert();
+ refreshDisplay();
+ if (!hasComplement)
+ {
+ close();
+ }
}
});
-
- JButton ok = new JButton(MessageManager.getString("action.ok"));
+ // Cancel for the whole dialog should cancel both CDS and Protein.
+ // OK for an individual feature settings just applies changes, but dialog
+ // remains open
+ JButton ok = new JButton(MessageManager
+ .getString(hasComplement ? "action.apply" : "action.ok"));
ok.setFont(JvSwingUtils.getLabelFont());
ok.addActionListener(new ActionListener()
{
@Override
public void actionPerformed(ActionEvent e)
{
- close();
+ if (!hasComplement)
+ {
+ close();
+ }
+ else
+ {
+ storeOriginalSettings();
+ }
}
});
? MessageManager.getString("label.protein")
.toLowerCase()
: "CDS");
- JCheckBox showComplement = new JCheckBox(text);
- showComplement.setSelected(af.getViewport().isShowComplementFeatures());
+ showComplement = new JCheckBox(text);
showComplement.addActionListener(new ActionListener()
{
@Override
}
});
- JCheckBox showComplementOnTop = new JCheckBox(
+ showComplementOnTop = new JCheckBox(
MessageManager.getString("label.on_top"));
- showComplementOnTop
- .setSelected(af.getViewport().isShowComplementFeaturesOnTop());
showComplementOnTop.addActionListener(new ActionListener()
{
@Override
refreshDisplay();
}
});
-
+ // JButton viewComplementSettings = new JButton(MessageManager
+ // .formatMessage("label.show_linked_feature_settings",
+ // nucleotide
+ // ? MessageManager.getString("label.protein")
+ // .toLowerCase()
+ // : "CDS"));
+ // viewComplementSettings.addActionListener(new ActionListener()
+ // {
+ //
+ // @Override
+ // public void actionPerformed(ActionEvent e)
+ // {
+ // AlignViewControllerGuiI complAf = af.getSplitViewContainer()
+ // .getComplementAlignFrame(af);
+ // FeatureSettings complFeatureSettings = (FeatureSettings) complAf
+ // .getFeatureSettingsUI();
+ // if (complFeatureSettings != null)
+ // {
+ // complFeatureSettings.frame.setVisible(true);
+ // try
+ // {
+ // complFeatureSettings.frame.setSelected(true);
+ // return;
+ // } catch (Exception q)
+ // {
+ // }
+ // }
+ // {
+ // complAf.showFeatureSettingsUI();
+ // }
+ // }
+ // });
JPanel lowerPanel = new JPanel(new GridLayout(1, 2));
bigPanel.add(lowerPanel, BorderLayout.SOUTH);
transbuttons.add(sortByDens);
transbuttons.add(help);
- boolean hasComplement = af.getViewport().getCodingComplement() != null;
JPanel transPanelLeft = new JPanel(
- new GridLayout(hasComplement ? 3 : 2, 1));
+ new GridLayout(hasComplement ? 4 : 2, 1));
transPanelLeft.add(new JLabel(" Colour transparency" + ":"));
transPanelLeft.add(transparency);
if (hasComplement)
return button;
}
}
+
+ public boolean isOpen()
+ {
+ if (af.getSplitViewContainer() != null)
+ {
+ return af.getSplitViewContainer().isFeatureSettingsOpen();
+ }
+ return frame != null && !frame.isClosed();
+ }
+
+ @Override
+ public void revert()
+ {
+ fr.setTransparency(originalTransparency);
+ fr.setFeatureFilters(originalFilters);
+ updateFeatureRenderer(originalData);
+ af.getViewport().setViewStyle(originalViewStyle);
+ updateTransparencySliderFromFR();
+ updateComplementButtons();
+ refreshDisplay();
+ }
}
class FeatureIcon implements Icon
*/
package jalview.gui;
+import jalview.api.AlignViewControllerGuiI;
+import jalview.api.FeatureSettingsControllerI;
import jalview.api.SplitContainerI;
+import jalview.controller.FeatureSettingsControllerGuiI;
import jalview.datamodel.AlignmentI;
import jalview.jbgui.GAlignFrame;
import jalview.jbgui.GSplitFrame;
import jalview.structure.StructureSelectionManager;
+import jalview.util.MessageManager;
import jalview.util.Platform;
import jalview.viewmodel.AlignmentViewport;
+import java.awt.BorderLayout;
import java.awt.Component;
+import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import javax.swing.AbstractAction;
import javax.swing.InputMap;
+import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JInternalFrame;
+import javax.swing.JLayeredPane;
import javax.swing.JMenuItem;
+import javax.swing.JPanel;
+import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
private static final long serialVersionUID = 1L;
+ /**
+ * geometry for Feature Settings Holder
+ */
+ private static final int FS_MIN_WIDTH = 400;
+
+ private static final int FS_MIN_HEIGHT = 400;
+
public SplitFrame(GAlignFrame top, GAlignFrame bottom)
{
super(top, bottom);
{ (AlignFrame) getTopFrame(), (AlignFrame) getBottomFrame() });
}
+ @Override
+ public AlignFrame getComplementAlignFrame(
+ AlignViewControllerGuiI alignFrame)
+ {
+ if (getTopFrame() == alignFrame)
+ {
+ return (AlignFrame) getBottomFrame();
+ }
+ if (getBottomFrame() == alignFrame)
+ {
+ return (AlignFrame) getTopFrame();
+ }
+ // we didn't know anything about this frame...
+ return null;
+ }
+
/**
* Replace Cmd-F Find action with our version. This is necessary because the
* 'default' Finder searches in the first AlignFrame it finds. We need it to
}
super.setSelected(selected);
}
-}
+
+ /**
+ * holds the frame for feature settings, so Protein and DNA tabs can be managed
+ */
+ JInternalFrame featureSettingsUI;
+
+ JTabbedPane featureSettingsPanels;
+
+ @Override
+ public void addFeatureSettingsUI(
+ FeatureSettingsControllerGuiI featureSettings)
+ {
+ boolean showInternalFrame = false;
+ if (featureSettingsUI == null || featureSettingsPanels == null)
+ {
+ showInternalFrame = true;
+ featureSettingsPanels = new JTabbedPane();
+ featureSettingsPanels.addChangeListener(new ChangeListener()
+ {
+
+ @Override
+ public void stateChanged(ChangeEvent e)
+ {
+ if (e.getSource() != featureSettingsPanels
+ || featureSettingsUI == null
+ || featureSettingsUI.isClosed()
+ || !featureSettingsUI.isVisible())
+ {
+ // not our tabbed pane
+ return;
+ }
+ int tab = featureSettingsPanels.getSelectedIndex();
+ if (tab < 0 || featureSettingsPanels
+ .getSelectedComponent() instanceof FeatureSettingsControllerGuiI)
+ {
+ // no tab selected or already showing a feature settings GUI
+ return;
+ }
+ getAlignFrames().get(tab).showFeatureSettingsUI();
+ }
+ });
+ featureSettingsUI = new JInternalFrame(MessageManager.getString(
+ "label.sequence_feature_settings_for_CDS_and_Protein"));
+ featureSettingsPanels.setOpaque(true);
+
+ JPanel dialog = new JPanel();
+ dialog.setOpaque(true);
+ dialog.setLayout(new BorderLayout());
+ dialog.add(featureSettingsPanels, BorderLayout.CENTER);
+ JPanel buttons = new JPanel();
+ JButton ok = new JButton(MessageManager.getString("action.ok"));
+ ok.addActionListener(new ActionListener()
+ {
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ try
+ {
+ featureSettingsUI.setClosed(true);
+ } catch (PropertyVetoException pv)
+ {
+ pv.printStackTrace();
+ }
+ }
+ });
+ JButton cancel = new JButton(
+ MessageManager.getString("action.cancel"));
+ cancel.addActionListener(new ActionListener()
+ {
+
+ @Override
+ public void actionPerformed(ActionEvent e)
+ {
+ try
+ {
+ for (Component fspanel : featureSettingsPanels.getComponents())
+ {
+ if (fspanel instanceof FeatureSettingsControllerGuiI)
+ {
+ ((FeatureSettingsControllerGuiI) fspanel).revert();
+ }
+ }
+ featureSettingsUI.setClosed(true);
+ } catch (Exception pv)
+ {
+ pv.printStackTrace();
+ }
+ }
+ });
+ buttons.add(ok);
+ buttons.add(cancel);
+ dialog.add(buttons, BorderLayout.SOUTH);
+ featureSettingsUI.setContentPane(dialog);
+ createDummyTabs();
+ }
+ if (featureSettingsPanels
+ .indexOfTabComponent((Component) featureSettings) > -1)
+ {
+ // just show the feature settings !
+ featureSettingsPanels
+ .setSelectedComponent((Component) featureSettings);
+ return;
+ }
+ // otherwise replace the dummy tab with the given feature settings
+ int pos = getAlignFrames().indexOf(featureSettings.getAlignframe());
+ // if pos==-1 then alignFrame isn't managed by this splitframe
+ if (pos == 0)
+ {
+ featureSettingsPanels.removeTabAt(0);
+ featureSettingsPanels.insertTab(tabName[0], null,
+ (Component) featureSettings,
+ MessageManager.formatMessage(
+ "label.sequence_feature_settings_for", tabName[0]),
+ 0);
+ }
+ if (pos == 1)
+ {
+ featureSettingsPanels.removeTabAt(1);
+ featureSettingsPanels.insertTab(tabName[1], null,
+ (Component) featureSettings,
+ MessageManager.formatMessage(
+ "label.sequence_feature_settings_for", tabName[1]),
+ 1);
+ }
+ featureSettingsPanels.setSelectedComponent((Component) featureSettings);
+
+ // TODO: JAL-3535 - construct a feature settings title including names of
+ // currently selected CDS and Protein names
+
+ if (showInternalFrame)
+ {
+ if (Platform.isAMac())
+ {
+ Desktop.addInternalFrame(featureSettingsUI,
+ MessageManager.getString(
+ "label.sequence_feature_settings_for_CDS_and_Protein"),
+ 600, 480);
+ }
+ else
+ {
+ Desktop.addInternalFrame(featureSettingsUI,
+ MessageManager.getString(
+ "label.sequence_feature_settings_for_CDS_and_Protein"),
+ 600, 450);
+ }
+ featureSettingsUI
+ .setMinimumSize(new Dimension(FS_MIN_WIDTH, FS_MIN_HEIGHT));
+
+ featureSettingsUI.addInternalFrameListener(
+ new javax.swing.event.InternalFrameAdapter()
+ {
+ @Override
+ public void internalFrameClosed(
+ javax.swing.event.InternalFrameEvent evt)
+ {
+ for (int tab = 0; tab < featureSettingsPanels
+ .getTabCount();)
+ {
+ FeatureSettingsControllerGuiI fsettings = (FeatureSettingsControllerGuiI) featureSettingsPanels
+ .getTabComponentAt(tab);
+ if (fsettings != null)
+ {
+ featureSettingsPanels.removeTabAt(tab);
+ fsettings.featureSettings_isClosed();
+ }
+ else
+ {
+ tab++;
+ }
+ }
+ featureSettingsPanels = null;
+ featureSettingsUI = null;
+ };
+ });
+ featureSettingsUI.setLayer(JLayeredPane.PALETTE_LAYER);
+ }
+ }
+
+ /**
+ * tab names for feature settings
+ */
+ private String[] tabName = new String[] {
+ MessageManager.getString("label.CDS"),
+ MessageManager.getString("label.protein") };
+
+ /**
+ * create placeholder tabs which materialise the feature settings for a given
+ * view. Also reinitialises any tabs containing stale feature settings
+ */
+ private void createDummyTabs()
+ {
+ for (int tabIndex = 0; tabIndex < 2; tabIndex++)
+ {
+ JPanel dummyTab = new JPanel();
+ featureSettingsPanels.addTab(tabName[tabIndex], dummyTab);
+ }
+ }
+
+ private void replaceWithDummyTab(FeatureSettingsControllerI toClose)
+ {
+ Component dummyTab = null;
+ for (int tabIndex = 0; tabIndex < 2; tabIndex++)
+ {
+ if (featureSettingsPanels.getTabCount() > tabIndex)
+ {
+ dummyTab = featureSettingsPanels.getTabComponentAt(tabIndex);
+ if (dummyTab instanceof FeatureSettingsControllerGuiI
+ && !dummyTab.isVisible())
+ {
+ featureSettingsPanels.removeTabAt(tabIndex);
+ // close the feature Settings tab
+ ((FeatureSettingsControllerGuiI) dummyTab)
+ .featureSettings_isClosed();
+ // create a dummy tab in its place
+ dummyTab = new JPanel();
+ featureSettingsPanels.insertTab(tabName[tabIndex], null, dummyTab,
+ MessageManager.formatMessage(
+ "label.sequence_feature_settings_for",
+ tabName[tabIndex]),
+ tabIndex);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void closeFeatureSettings(
+ FeatureSettingsControllerI featureSettings,
+ boolean closeContainingFrame)
+ {
+ if (featureSettingsUI != null)
+ {
+ if (closeContainingFrame)
+ {
+ try
+ {
+ featureSettingsUI.setClosed(true);
+ } catch (Exception x)
+ {
+ }
+ featureSettingsUI = null;
+ }
+ else
+ {
+ replaceWithDummyTab(featureSettings);
+ }
+ }
+ }
+
+ @Override
+ public boolean isFeatureSettingsOpen()
+ {
+ return featureSettingsUI != null && !featureSettingsUI.isClosed();
+ }
+}
\ No newline at end of file
public static String formatMessage(String key, Object... params)
{
- return MessageFormat.format(rb.getString(key), params);
+ try
+ {
+ return MessageFormat.format(rb.getString(key), params);
+ } catch (Exception e)
+ {
+ log.warning("I18N missing: " + loc + "\t" + key);
+ // e.printStackTrace();
+
+ }
+ String value = "[missing key] " + key + "";
+ for (Object p : params)
+ {
+ value += " '" + p.toString() + "'";
+ }
+ return value;
}
public static String formatMessage(String key, String[] params)
{
- return MessageFormat.format(rb.getString(key), (Object[]) params);
+ return formatMessage(key, (Object[]) params);
}
/**