From: Ben Soares Date: Thu, 31 Aug 2023 21:11:40 +0000 (+0100) Subject: Merge branch 'improvement/JAL-3830_remove_dock_icon_in_headless_mode' into merge... X-Git-Tag: Release_2_11_3_0~8^2~30 X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=81d1b5ee333dee61382971eca41792d6a64a29fc;hp=c4820a850ccb7171fd29206655bc327af7226e6b;p=jalview.git Merge branch 'improvement/JAL-3830_remove_dock_icon_in_headless_mode' into merge/big_merge_of_bens_stuff_before_2_11_3_0 --- diff --git a/THIRDPARTYLIBS b/THIRDPARTYLIBS index 3ec73d3..e52f3f6 100644 --- a/THIRDPARTYLIBS +++ b/THIRDPARTYLIBS @@ -42,7 +42,7 @@ jetty-http-9.2.10.v20150310.jar jetty-io-9.2.10.v20150310.jar jetty-server-9.2.10.v20150310.jar jetty-util-9.2.10.v20150310.jar -jfreesvg-2.1.jar GPL v3 licensed library from the JFree suite - http://www.jfree.org/jfreesvg/ +jfreesvg-3.4.3.jar GPL v3 licensed library from the JFree suite - last release with Java 1.8 compatibility http://www.jfree.org/jfreesvg/ JGoogleAnalytics_0.3.jar APL 2.0 License - http://code.google.com/p/jgoogleanalytics/ jhall.jar Jmol-NO_LOG4J-14.31.53.jar GPL/LGPLv2 built manually from commit https://github.com/BobHanson/Jmol-SwingJS/commit/a6a2fb767e3fc2a73e72d926a11fd93a0e4c9f23 (excluded jspecview/application to compile) @@ -58,7 +58,8 @@ miglayout-4.0-swing.jar BSD - http://www.migcalendar.com/miglayout/versions/4.0/ regex.jar saaj.jar servlet-api-3.1.jar -slf4j-api-1.7.32.jar MIT license - https://opensource.org/licenses/mit-license.php +slf4j-api-1.7.36.jar MIT license - https://opensource.org/licenses/mit-license.php - downloaded from https://repo1.maven.org/maven2/org/slf4j/ +slf4j-nop-1.7.36.jar MIT license - https://opensource.org/licenses/mit-license.php - downloaded from https://repo1.maven.org/maven2/org/slf4j/ vamsas-client.jar VARNAv3-93.jar GPL licenced software by K�vin Darty, Alain Denise and Yann Ponty - http://varna.lri.fr wsdl4j.jar diff --git a/build.gradle b/build.gradle index 7ad6f99..c4ed582 100644 --- a/build.gradle +++ b/build.gradle @@ -1834,6 +1834,10 @@ tasks.withType(Test).matching {t -> t.getName().startsWith("testTask")}.all { te info.events = [ TestLogEvent.FAILED ] } + if (OperatingSystem.current().isMacOsX()) { + testTask.systemProperty "apple.awt.UIElement", "true" + testTask.environment "JAVA_TOOL_OPTIONS", "-Dapple.awt.UIElement=true" + } ignoreFailures = true // Always try to run all tests for all modules diff --git a/examples/argfiles/test_fab41.txt b/examples/argfiles/test_fab41.txt index e6f7627..b7f0d45 100644 --- a/examples/argfiles/test_fab41.txt +++ b/examples/argfiles/test_fab41.txt @@ -10,4 +10,4 @@ --paematrix=[label=pAE R4-M5]./examples/test_fab41.result/test_fab41_unrelaxed_rank_4_model_5_scores.json --structure=./examples/test_fab41.result/test_fab41_unrelaxed_rank_5_model_1.pdb --paematrix=[label=pAE R5-M1]./examples/test_fab41.result/test_fab41_unrelaxed_rank_5_model_1_scores.json ---image=output1.html +--image=[textrenderer=text]output1.html diff --git a/examples/argfiles/test_fab41_nostructureviewers.txt b/examples/argfiles/test_fab41_nostructureviewers.txt new file mode 100644 index 0000000..ab52cdf --- /dev/null +++ b/examples/argfiles/test_fab41_nostructureviewers.txt @@ -0,0 +1,14 @@ +--open=./examples/test_fab41.result/sample.a2m +--colour=gecos:flower +--gui +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb +--paematrix=[label=pAE R1-M3]./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3_scores.json +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_2_model_4.pdb +--paematrix=[label=pAE R2-M4]./examples/test_fab41.result/test_fab41_unrelaxed_rank_2_model_4_scores.json +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_3_model_2.pdb +--paematrix=[label=pAE R3-M2]./examples/test_fab41.result/test_fab41_unrelaxed_rank_3_model_2_scores.json +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_4_model_5.pdb +--paematrix=[label=pAE R4-M5]./examples/test_fab41.result/test_fab41_unrelaxed_rank_4_model_5_scores.json +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_5_model_1.pdb +--paematrix=[label=pAE R5-M1]./examples/test_fab41.result/test_fab41_unrelaxed_rank_5_model_1_scores.json +--image=[textrenderer=text]output1.html diff --git a/help/markdown/releases/release-2_11_3_0.md b/help/markdown/releases/release-2_11_3_0.md index b51f463..a433bc6 100644 --- a/help/markdown/releases/release-2_11_3_0.md +++ b/help/markdown/releases/release-2_11_3_0.md @@ -48,6 +48,11 @@ channel: "release" ### Other improvements + +- Secondary structure annotation glyphs are rendered anti-aliasing when enabled +- Helix and Sheet glyphs vertically centered with respect to grey coil secondary structure annotation track +- Lower line of the sequence group border does not align with vertical and background residue box +- Updated JFreeSVG (https://www.jfree.org/jfreesvg) from 2.1 to 3.4.3 - Name of alignment and view included in overview window's title - "add reference annotation" add all positions in reference annotation tracks, not just positions in the currently highlighted columns/selection range - EMBL-EBI SIFTS file downloads now use split directories @@ -75,6 +80,7 @@ known issue ? 'Reload' for a jalview project results in all wi - Create separate gradle test task for some tests - Allow gradle build to create suffixed DEVELOP-... builds with channel appbase +- Jalview bio.tools description maintained under jalview's git repo and bundled with source release ## Issues Resolved - Jmol view not always centred on structures when multiple structures are viewed diff --git a/j11lib/flatlaf-3.0.jar b/j11lib/flatlaf-3.0.jar deleted file mode 100644 index 75d90d3..0000000 Binary files a/j11lib/flatlaf-3.0.jar and /dev/null differ diff --git a/j11lib/flatlaf-3.1.1.jar b/j11lib/flatlaf-3.1.1.jar deleted file mode 100644 index 2b13cdd..0000000 Binary files a/j11lib/flatlaf-3.1.1.jar and /dev/null differ diff --git a/j11lib/flatlaf-3.2.jar b/j11lib/flatlaf-3.2.jar new file mode 100644 index 0000000..450c1e8 Binary files /dev/null and b/j11lib/flatlaf-3.2.jar differ diff --git a/j11lib/flatlaf-extras-3.0.jar b/j11lib/flatlaf-extras-3.0.jar deleted file mode 100644 index 1f6bbc3..0000000 Binary files a/j11lib/flatlaf-extras-3.0.jar and /dev/null differ diff --git a/j8lib/flatlaf-extras-3.1.1.jar b/j11lib/flatlaf-extras-3.2.jar similarity index 52% rename from j8lib/flatlaf-extras-3.1.1.jar rename to j11lib/flatlaf-extras-3.2.jar index 9d91bd0..ccdbf94 100644 Binary files a/j8lib/flatlaf-extras-3.1.1.jar and b/j11lib/flatlaf-extras-3.2.jar differ diff --git a/j11lib/jfreesvg-2.1.jar b/j11lib/jfreesvg-2.1.jar deleted file mode 100644 index 91d453c..0000000 Binary files a/j11lib/jfreesvg-2.1.jar and /dev/null differ diff --git a/j11lib/jfreesvg-3.4.3.jar b/j11lib/jfreesvg-3.4.3.jar new file mode 100644 index 0000000..cfd1463 Binary files /dev/null and b/j11lib/jfreesvg-3.4.3.jar differ diff --git a/j11lib/slf4j-api-1.7.32.jar b/j11lib/slf4j-api-1.7.32.jar deleted file mode 100644 index b16a078..0000000 Binary files a/j11lib/slf4j-api-1.7.32.jar and /dev/null differ diff --git a/j11lib/slf4j-api-1.7.36.jar b/j11lib/slf4j-api-1.7.36.jar new file mode 100644 index 0000000..7d3ce68 Binary files /dev/null and b/j11lib/slf4j-api-1.7.36.jar differ diff --git a/j11lib/slf4j-nop-1.7.36.jar b/j11lib/slf4j-nop-1.7.36.jar new file mode 100644 index 0000000..734ad96 Binary files /dev/null and b/j11lib/slf4j-nop-1.7.36.jar differ diff --git a/j8lib/flatlaf-3.1.1.jar b/j8lib/flatlaf-3.1.1.jar deleted file mode 100644 index 2b13cdd..0000000 Binary files a/j8lib/flatlaf-3.1.1.jar and /dev/null differ diff --git a/j8lib/flatlaf-3.2.jar b/j8lib/flatlaf-3.2.jar new file mode 100644 index 0000000..450c1e8 Binary files /dev/null and b/j8lib/flatlaf-3.2.jar differ diff --git a/j11lib/flatlaf-extras-3.1.1.jar b/j8lib/flatlaf-extras-3.2.jar similarity index 52% rename from j11lib/flatlaf-extras-3.1.1.jar rename to j8lib/flatlaf-extras-3.2.jar index 9d91bd0..ccdbf94 100644 Binary files a/j11lib/flatlaf-extras-3.1.1.jar and b/j8lib/flatlaf-extras-3.2.jar differ diff --git a/j8lib/jfreesvg-2.1.jar b/j8lib/jfreesvg-2.1.jar deleted file mode 100644 index 91d453c..0000000 Binary files a/j8lib/jfreesvg-2.1.jar and /dev/null differ diff --git a/j8lib/jfreesvg-3.4.3.jar b/j8lib/jfreesvg-3.4.3.jar new file mode 100644 index 0000000..cfd1463 Binary files /dev/null and b/j8lib/jfreesvg-3.4.3.jar differ diff --git a/j8lib/slf4j-api-1.7.32.jar b/j8lib/slf4j-api-1.7.32.jar deleted file mode 100644 index b16a078..0000000 Binary files a/j8lib/slf4j-api-1.7.32.jar and /dev/null differ diff --git a/j8lib/slf4j-api-1.7.36.jar b/j8lib/slf4j-api-1.7.36.jar new file mode 100644 index 0000000..7d3ce68 Binary files /dev/null and b/j8lib/slf4j-api-1.7.36.jar differ diff --git a/j8lib/slf4j-nop-1.7.36.jar b/j8lib/slf4j-nop-1.7.36.jar new file mode 100644 index 0000000..734ad96 Binary files /dev/null and b/j8lib/slf4j-nop-1.7.36.jar differ diff --git a/resources/lang/Messages.properties b/resources/lang/Messages.properties index 45ed727..0c4f165 100644 --- a/resources/lang/Messages.properties +++ b/resources/lang/Messages.properties @@ -1463,4 +1463,4 @@ action.show_tree_for_matrix_tooltip = Opens a tree viewer to display the average action.cluster_matrix = Cluster matrix action.clustering_matrix_for = Calculating tree for matrix {0} and clustering at {1} action.cluster_matrix_tooltip = Computes an average distance tree for the matrix and displays it - +label.all_known_alignment_files = All known alignment files diff --git a/resources/lang/Messages_es.properties b/resources/lang/Messages_es.properties index 5dd8b3d..1f256cb 100644 --- a/resources/lang/Messages_es.properties +++ b/resources/lang/Messages_es.properties @@ -1437,3 +1437,4 @@ label.add_pae_matrix_file = A label.nothing_selected = Nada seleccionado prompt.analytics_title = Jalview Estadísticas de Uso prompt.analytics = ¿Quiere ayudar a mejorar Jalview habilitando la recopilación de estadísticas de uso con análisis Plausible?\nPuede habilitar o deshabilitar el seguimiento de uso en las preferencias. +label.all_known_alignment_files = Todos los archivos de alineación conocidos diff --git a/src/jalview/bin/Commands.java b/src/jalview/bin/Commands.java index 17a1af2..06684e7 100644 --- a/src/jalview/bin/Commands.java +++ b/src/jalview/bin/Commands.java @@ -6,7 +6,6 @@ import java.net.URISyntaxException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -166,13 +165,9 @@ public class Commands if (avm == null) return true; - /* - * // script to execute after all loading is completed one way or another String - * groovyscript = m.get(Arg.GROOVY) == null ? null : - * m.get(Arg.GROOVY).getValue(); String file = m.get(Arg.OPEN) == null ? null : - * m.get(Arg.OPEN).getValue(); String data = null; FileFormatI format = null; - * DataSourceType protocol = null; - */ + // set wrap scope here so it can be applied after structures are opened + boolean wrap = false; + if (avm.containsArg(Arg.APPEND) || avm.containsArg(Arg.OPEN)) { commandArgsProvided = true; @@ -256,11 +251,6 @@ public class Commands af = fileLoader.LoadFileWaitTillLoaded(openFile, protocol, format); - // wrap alignment? - boolean wrap = ArgParser.getFromSubValArgOrPref(avm, Arg.WRAP, sv, - null, "WRAP_ALIGNMENT", false); - af.getCurrentView().setWrapAlignment(wrap); - // colour alignment? String colour = ArgParser.getFromSubValArgOrPref(avm, av, Arg.COLOUR, sv, null, "DEFAULT_COLOUR_PROT", ""); @@ -365,6 +355,12 @@ public class Commands false, false); } + // wrap alignment? do this last for formatting reasons + wrap = ArgParser.getFromSubValArgOrPref(avm, Arg.WRAP, sv, null, + "WRAP_ALIGNMENT", false); + // af.setWrapFormat(wrap) is applied after structures are opened for + // annotation reasons + // store the AlignFrame for this id afMap.put(id, af); @@ -556,20 +552,7 @@ public class Commands String sViewer = ArgParser.getFromSubValArgOrPref(avm, Arg.STRUCTUREVIEWER, Position.AFTER, av, subVals, null, null, "jmol"); - ViewerType viewerType = null; - if (!"none".equals(sViewer)) - { - for (ViewerType v : EnumSet.allOf(ViewerType.class)) - { - String name = v.name().toLowerCase(Locale.ROOT) - .replaceAll(" ", ""); - if (sViewer.equals(name)) - { - viewerType = v; - break; - } - } - } + ViewerType viewerType = ViewerType.getFromString(sViewer); // TODO use ssFromStructure StructureViewer sv = StructureChooser @@ -689,6 +672,15 @@ public class Commands } } + if (wrap) + { + AlignFrame af = afMap.get(id); + if (af != null) + { + af.setWrapFormat(wrap, true); + } + } + /* boolean doShading = avm.getBoolean(Arg.TEMPFAC_SHADING); if (doShading) @@ -807,7 +799,7 @@ public class Commands case "biojs": Console.debug( - "Creating BioJS MSA Viwer HTML file: " + fileName); + "Outputting BioJS MSA Viwer HTML file: " + fileName); try { BioJsHTMLOutput.refreshVersionInfo( @@ -821,12 +813,12 @@ public class Commands break; case "eps": - Console.debug("Creating EPS file: " + fileName); - af.createEPS(file, name); + Console.debug("Outputting EPS file: " + fileName); + af.createEPS(file, renderer); break; case "imagemap": - Console.debug("Creating ImageMap file: " + fileName); + Console.debug("Outputting ImageMap file: " + fileName); af.createImageMap(file, name); break; @@ -997,7 +989,7 @@ public class Commands seq = al.getSequenceAt(subVals.getIndex()); } } - else if (idAv != null) + if (seq == null && idAv != null) { seq = al.findName(idAv.getValue()); } diff --git a/src/jalview/bin/Jalview.java b/src/jalview/bin/Jalview.java index e229079..810d835 100755 --- a/src/jalview/bin/Jalview.java +++ b/src/jalview/bin/Jalview.java @@ -1501,7 +1501,7 @@ public class Jalview UIManager.put("TabbedPane.tabType", "card"); UIManager.put("TabbedPane.showTabSeparators", true); UIManager.put("TabbedPane.showContentSeparator", true); - UIManager.put("TabbedPane.tabSeparatorsFullHeight", true); + // UIManager.put("TabbedPane.tabSeparatorsFullHeight", true); UIManager.put("TabbedPane.tabsOverlapBorder", true); UIManager.put("TabbedPane.hasFullBorder", true); UIManager.put("TabbedPane.tabLayoutPolicy", "scroll"); diff --git a/src/jalview/gui/AlignExportOptions.java b/src/jalview/gui/AlignExportOptions.java index c776d82..6ca21c7 100644 --- a/src/jalview/gui/AlignExportOptions.java +++ b/src/jalview/gui/AlignExportOptions.java @@ -32,6 +32,7 @@ import javax.swing.JPanel; import jalview.api.AlignExportSettingsI; import jalview.api.AlignViewportI; +import jalview.bin.Jalview; import jalview.io.FileFormatI; import jalview.util.MessageManager; @@ -72,12 +73,9 @@ public class AlignExportOptions extends JPanel public static boolean isNeeded(AlignViewportI viewport, FileFormatI format) { - if (viewport.hasHiddenColumns() || viewport.hasHiddenRows() - || format.isComplexAlignFile()) - { - return true; - } - return false; + return !Jalview.getInstance().isHeadlessMode() + && (viewport.hasHiddenColumns() || viewport.hasHiddenRows() + || format.isComplexAlignFile()); } /** diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index eb98d98..d225c2e 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -1669,6 +1669,8 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, closeAllTabs = true; } + Desktop.closeModal(this); + try { if (alignPanels != null) @@ -1698,6 +1700,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, 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 @@ -3183,11 +3186,20 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, @Override public void wrapMenuItem_actionPerformed(ActionEvent e) { - scaleAbove.setVisible(wrapMenuItem.isSelected()); - scaleLeft.setVisible(wrapMenuItem.isSelected()); - scaleRight.setVisible(wrapMenuItem.isSelected()); - viewport.setWrapAlignment(wrapMenuItem.isSelected()); + setWrapFormat(wrapMenuItem.isSelected(), false); + } + + public void setWrapFormat(boolean b, boolean setMenuItem) + { + scaleAbove.setVisible(b); + scaleLeft.setVisible(b); + scaleRight.setVisible(b); + viewport.setWrapAlignment(b); alignPanel.updateLayout(); + if (setMenuItem) + { + wrapMenuItem.setSelected(b); + } } @Override @@ -3525,7 +3537,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, } JInternalFrame frame = new JInternalFrame(); - + frame.setFrameIcon(null); frame.getContentPane().add(new JScrollPane(pane)); Desktop.addInternalFrame(frame, MessageManager @@ -3553,12 +3565,12 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, return alignPanel.overviewPanel; } JInternalFrame frame = new JInternalFrame(); + frame.setFrameIcon(null); final OverviewPanel overview = new OverviewPanel(alignPanel, frame, showHidden); frame.setContentPane(overview); Desktop.addInternalFrame(frame, "", true, frame.getWidth(), frame.getHeight(), true, true); - frame.setFrameIcon(null); frame.pack(); frame.setLayer(JLayeredPane.PALETTE_LAYER); final AlignmentPanel thePanel = this.alignPanel; @@ -3832,6 +3844,7 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, else { JInternalFrame frame = new JInternalFrame(); + frame.setFrameIcon(null); frame.setContentPane(new PairwiseAlignPanel(viewport)); Desktop.addInternalFrame(frame, MessageManager.getString("action.pairwise_alignment"), 600, diff --git a/src/jalview/gui/AlignViewport.java b/src/jalview/gui/AlignViewport.java index 5613f16..ef9e575 100644 --- a/src/jalview/gui/AlignViewport.java +++ b/src/jalview/gui/AlignViewport.java @@ -45,7 +45,6 @@ import jalview.bin.Console; import jalview.commands.CommandI; import jalview.datamodel.AlignedCodonFrame; import jalview.datamodel.Alignment; -import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.ContactMatrixI; diff --git a/src/jalview/gui/AlignmentPanel.java b/src/jalview/gui/AlignmentPanel.java index 3de02bc..ee71483 100644 --- a/src/jalview/gui/AlignmentPanel.java +++ b/src/jalview/gui/AlignmentPanel.java @@ -50,6 +50,7 @@ import jalview.api.AlignmentViewPanel; import jalview.bin.Cache; import jalview.bin.Console; import jalview.bin.Jalview; +import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; import jalview.datamodel.SearchResultsI; @@ -269,10 +270,7 @@ public class AlignmentPanel extends GAlignmentPanel implements Dimension r = null; if (av.getIdWidth() < 0) { - int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300); - int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3); - int maxwidth = Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth); - r = calculateIdWidth(maxwidth); + r = calculateDefaultAlignmentIdWidth(); av.setIdWidth(r.width); } else @@ -294,6 +292,14 @@ public class AlignmentPanel extends GAlignmentPanel implements return r; } + public Dimension calculateDefaultAlignmentIdWidth() + { + int afwidth = (alignFrame != null ? alignFrame.getWidth() : 300); + int idWidth = Math.min(afwidth - 200, 2 * afwidth / 3); + int maxwidth = Math.max(IdwidthAdjuster.MIN_ID_WIDTH, idWidth); + return calculateIdWidth(-1, false, false); + } + /** * Calculate the width of the alignment labels based on the displayed names * and any bounds on label width set in preferences. @@ -305,6 +311,12 @@ public class AlignmentPanel extends GAlignmentPanel implements */ protected Dimension calculateIdWidth(int maxwidth) { + return calculateIdWidth(maxwidth, true, false); + } + + public Dimension calculateIdWidth(int maxwidth, + boolean includeAnnotations, boolean visibleOnly) + { Container c = new Container(); FontMetrics fm = c.getFontMetrics( @@ -324,18 +336,29 @@ public class AlignmentPanel extends GAlignmentPanel implements } // Also check annotation label widths - i = 0; - - if (al.getAlignmentAnnotation() != null) + if (includeAnnotations && al.getAlignmentAnnotation() != null) { - fm = c.getFontMetrics(getAlabels().getFont()); - - while (i < al.getAlignmentAnnotation().length) + if (Jalview.isHeadlessMode()) { - String label = al.getAlignmentAnnotation()[i].label; - int stringWidth = fm.stringWidth(label); + AnnotationLabels aal = this.getAlabels(); + int stringWidth = aal.drawLabels(null, false, idWidth, false, fm); idWidth = Math.max(idWidth, stringWidth); - i++; + } + else + { + fm = c.getFontMetrics(getAlabels().getFont()); + + for (i = 0; i < al.getAlignmentAnnotation().length; i++) + { + AlignmentAnnotation aa = al.getAlignmentAnnotation()[i]; + if (visibleOnly && !aa.visible) + { + continue; + } + String label = aa.label; + int stringWidth = fm.stringWidth(label); + idWidth = Math.max(idWidth, stringWidth); + } } } @@ -567,21 +590,29 @@ public class AlignmentPanel extends GAlignmentPanel implements // not be called directly by programs. // I note that addNotify() is called in several areas of Jalview. - int annotationHeight = getAnnotationPanel().adjustPanelHeight(); - annotationHeight = getAnnotationPanel() - .adjustForAlignFrame(adjustPanelHeight, annotationHeight); + AnnotationPanel ap = getAnnotationPanel(); + int annotationHeight = ap.adjustPanelHeight(); + annotationHeight = ap.adjustForAlignFrame(adjustPanelHeight, + annotationHeight); hscroll.addNotify(); - annotationScroller.setPreferredSize( - new Dimension(annotationScroller.getWidth(), annotationHeight)); - Dimension e = idPanel.getSize(); - alabels.setSize(new Dimension(e.width, annotationHeight)); + int idWidth = e.width; + boolean manuallyAdjusted = this.getIdPanel().getIdCanvas() + .manuallyAdjusted(); + annotationScroller.setPreferredSize(new Dimension( + manuallyAdjusted ? idWidth : annotationScroller.getWidth(), + annotationHeight)); + + alabels.setPreferredSize(new Dimension(idWidth, annotationHeight)); annotationSpaceFillerHolder.setPreferredSize(new Dimension( - annotationSpaceFillerHolder.getWidth(), annotationHeight)); + manuallyAdjusted ? idWidth + : annotationSpaceFillerHolder.getWidth(), + annotationHeight)); annotationScroller.validate(); annotationScroller.addNotify(); + ap.validate(); } /** @@ -597,9 +628,10 @@ public class AlignmentPanel extends GAlignmentPanel implements boolean wrap = av.getWrapAlignment(); ViewportRanges ranges = av.getRanges(); ranges.setStartSeq(0); - scalePanelHolder.setVisible(!wrap); + // scalePanelHolder.setVisible(!wrap); hscroll.setVisible(!wrap); - idwidthAdjuster.setVisible(!wrap); + // Allow idPanel width adjustment in wrap mode + idwidthAdjuster.setVisible(true); if (wrap) { @@ -633,7 +665,7 @@ public class AlignmentPanel extends GAlignmentPanel implements } } - idSpaceFillerPanel1.setVisible(!wrap); + // idSpaceFillerPanel1.setVisible(!wrap); repaint(); } @@ -854,10 +886,27 @@ public class AlignmentPanel extends GAlignmentPanel implements public void paintComponent(Graphics g) { invalidate(); // needed so that the id width adjuster works correctly - Dimension d = getIdPanel().getIdCanvas().getPreferredSize(); - idPanelHolder.setPreferredSize(d); - hscrollFillerPanel.setPreferredSize(new Dimension(d.width, 12)); + int idWidth = d.width; + + // check wrapped alignment as at least 1 residue width + if (av.getWrapAlignment()) + { + SeqCanvas sc = this.getSeqPanel().seqCanvas; + if (sc != null && sc.getWidth() < sc.getMinimumWrappedCanvasWidth()) + { + // need to make some adjustments + idWidth -= (sc.getMinimumWrappedCanvasWidth() - sc.getWidth()); + av.setIdWidth(idWidth); + av.getAlignPanel().getIdPanel().getIdCanvas() + .setManuallyAdjusted(true); + + validateAnnotationDimensions(false); + } + } + + idPanelHolder.setPreferredSize(new Dimension(idWidth, d.height)); + hscrollFillerPanel.setPreferredSize(new Dimension(idWidth, 12)); validate(); // needed so that the id width adjuster works correctly @@ -1172,10 +1221,12 @@ public class AlignmentPanel extends GAlignmentPanel implements } int w = getIdPanel().getWidth(); + w = this.calculateIdWidth(-1, true, true).width; return (w > 0 ? w : calculateIdWidth().width); } - void makeAlignmentImage(ImageMaker.TYPE type, File file, String renderer) throws ImageOutputException + void makeAlignmentImage(ImageMaker.TYPE type, File file, String renderer) + throws ImageOutputException { makeAlignmentImage(type, file, renderer, BitmapImageSizing.nullBitmapImageSizing()); @@ -1196,6 +1247,7 @@ public class AlignmentPanel extends GAlignmentPanel implements final int borderBottomOffset = 5; AlignmentDimension aDimension = getAlignmentDimension(); + // todo use a lambda function in place of callback here? ImageWriterI writer = new ImageWriterI() { @@ -1266,7 +1318,8 @@ public class AlignmentPanel extends GAlignmentPanel implements } - public void makePNGImageMap(File imgMapFile, String imageName) throws ImageOutputException + public void makePNGImageMap(File imgMapFile, String imageName) + throws ImageOutputException { // /////ONLY WORKS WITH NON WRAPPED ALIGNMENTS // //////////////////////////////////////////// @@ -1391,7 +1444,8 @@ public class AlignmentPanel extends GAlignmentPanel implements } catch (Exception ex) { - throw new ImageOutputException("couldn't write ImageMap due to unexpected error",ex); + throw new ImageOutputException( + "couldn't write ImageMap due to unexpected error", ex); } } // /////////END OF IMAGE MAP @@ -1407,8 +1461,7 @@ public class AlignmentPanel extends GAlignmentPanel implements { int seqPanelWidth = getSeqPanel().seqCanvas.getWidth(); - if (System.getProperty("java.awt.headless") != null - && System.getProperty("java.awt.headless").equals("true")) + if (Jalview.isHeadlessMode()) { seqPanelWidth = alignFrame.getWidth() - getVisibleIdWidth() - vscroll.getPreferredSize().width diff --git a/src/jalview/gui/AnnotationLabels.java b/src/jalview/gui/AnnotationLabels.java index d26ba89..20e1b1b 100755 --- a/src/jalview/gui/AnnotationLabels.java +++ b/src/jalview/gui/AnnotationLabels.java @@ -20,6 +20,7 @@ */ package jalview.gui; +import java.awt.Canvas; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; @@ -50,6 +51,8 @@ import javax.swing.ToolTipManager; import jalview.analysis.AlignSeq; import jalview.analysis.AlignmentUtils; +import jalview.bin.Cache; +import jalview.bin.Jalview; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; @@ -85,7 +88,7 @@ public class AnnotationLabels extends JPanel /** * height in pixels for allowing height adjuster to be active */ - private static int HEIGHT_ADJUSTER_HEIGHT = 10; + public static int HEIGHT_ADJUSTER_HEIGHT = 10; private static final Font font = new Font("Arial", Font.PLAIN, 11); @@ -113,6 +116,8 @@ public class AnnotationLabels extends JPanel private static final String COPYCONS_SEQ = MessageManager .getString("label.copy_consensus_sequence"); + private static final String ADJUST_ANNOTATION_LABELS_WIDTH_PREF = "ADJUST_ANNOTATION_LABELS_WIDTH"; + private final boolean debugRedraw = false; private AlignmentPanel ap; @@ -131,6 +136,10 @@ public class AnnotationLabels extends JPanel private boolean resizePanel = false; + private int annotationIdWidth = -1; + + public static final String RESIZE_MARGINS_MARK_PREF = "RESIZE_MARGINS_MARK"; + /** * Creates a new AnnotationLabels object * @@ -138,7 +147,6 @@ public class AnnotationLabels extends JPanel */ public AnnotationLabels(AlignmentPanel ap) { - this.ap = ap; av = ap.av; ToolTipManager.sharedInstance().registerComponent(this); @@ -418,65 +426,74 @@ public class AnnotationLabels extends JPanel pop.add(consclipbrd); } - addColourOrFilterByOptions(ap,aa[selectedRow],pop); - + addColourOrFilterByOptions(ap, aa[selectedRow], pop); + if (aa[selectedRow].graph == AlignmentAnnotation.CONTACT_MAP) { - addContactMatrixOptions(ap,aa[selectedRow],pop); - // Set/adjust threshold for grouping ? - // colour alignment by this [type] - // select/hide columns by this row - - } + addContactMatrixOptions(ap, aa[selectedRow], pop); + // Set/adjust threshold for grouping ? + // colour alignment by this [type] + // select/hide columns by this row + } - + } + pop.show(this, evt.getX(), evt.getY()); } static void addColourOrFilterByOptions(final AlignmentPanel ap, - final AlignmentAnnotation alignmentAnnotation, final JPopupMenu pop) + final AlignmentAnnotation alignmentAnnotation, + final JPopupMenu pop) { JMenuItem item; - item = new JMenuItem(MessageManager.getString("label.colour_by_annotation")); + item = new JMenuItem( + MessageManager.getString("label.colour_by_annotation")); item.addActionListener(new ActionListener() { - + @Override public void actionPerformed(ActionEvent e) { - AnnotationColourChooser.displayFor(ap.av, ap,alignmentAnnotation,false); + AnnotationColourChooser.displayFor(ap.av, ap, alignmentAnnotation, + false); }; }); pop.add(item); - if (alignmentAnnotation.sequenceRef!=null) + if (alignmentAnnotation.sequenceRef != null) { - item = new JMenuItem(MessageManager.getString("label.colour_by_annotation")+" ("+MessageManager.getString("label.per_seq")+")"); + item = new JMenuItem( + MessageManager.getString("label.colour_by_annotation") + " (" + + MessageManager.getString("label.per_seq") + ")"); item.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - AnnotationColourChooser.displayFor(ap.av, ap,alignmentAnnotation,true); + AnnotationColourChooser.displayFor(ap.av, ap, alignmentAnnotation, + true); }; }); pop.add(item); } - item = new JMenuItem(MessageManager.getString("action.select_by_annotation")); + item = new JMenuItem( + MessageManager.getString("action.select_by_annotation")); item.addActionListener(new ActionListener() { - + @Override public void actionPerformed(ActionEvent e) { - AnnotationColumnChooser.displayFor(ap.av,ap,alignmentAnnotation); + AnnotationColumnChooser.displayFor(ap.av, ap, alignmentAnnotation); }; }); pop.add(item); } + static void addContactMatrixOptions(final AlignmentPanel ap, - final AlignmentAnnotation alignmentAnnotation, final JPopupMenu pop) + final AlignmentAnnotation alignmentAnnotation, + final JPopupMenu pop) { - + final ContactMatrixI cm = ap.av.getContactMatrix(alignmentAnnotation); JMenuItem item; if (cm != null) @@ -485,10 +502,13 @@ public class AnnotationLabels extends JPanel if (cm.hasGroups()) { - JCheckBoxMenuItem chitem = new JCheckBoxMenuItem(MessageManager.getString("action.show_groups_on_matrix")); - chitem.setToolTipText(MessageManager.getString("action.show_groups_on_matrix_tooltip")); - boolean showGroups = alignmentAnnotation.isShowGroupsForContactMatrix(); - final AlignmentAnnotation sel_row=alignmentAnnotation; + JCheckBoxMenuItem chitem = new JCheckBoxMenuItem( + MessageManager.getString("action.show_groups_on_matrix")); + chitem.setToolTipText(MessageManager + .getString("action.show_groups_on_matrix_tooltip")); + boolean showGroups = alignmentAnnotation + .isShowGroupsForContactMatrix(); + final AlignmentAnnotation sel_row = alignmentAnnotation; chitem.setState(showGroups); chitem.addActionListener(new ActionListener() { @@ -507,8 +527,10 @@ public class AnnotationLabels extends JPanel } if (cm.hasTree()) { - item = new JMenuItem(MessageManager.getString("action.show_tree_for_matrix")); - item.setToolTipText(MessageManager.getString("action.show_tree_for_matrix_tooltip")); + item = new JMenuItem( + MessageManager.getString("action.show_tree_for_matrix")); + item.setToolTipText(MessageManager + .getString("action.show_tree_for_matrix_tooltip")); item.addActionListener(new ActionListener() { @@ -524,8 +546,10 @@ public class AnnotationLabels extends JPanel } else { - item = new JMenuItem(MessageManager.getString("action.cluster_matrix")); - item.setToolTipText(MessageManager.getString("action.cluster_matrix_tooltip")); + item = new JMenuItem( + MessageManager.getString("action.cluster_matrix")); + item.setToolTipText( + MessageManager.getString("action.cluster_matrix_tooltip")); item.addActionListener(new ActionListener() { @Override @@ -537,7 +561,11 @@ public class AnnotationLabels extends JPanel public void run() { final long progBar; - ap.alignFrame.setProgressBar(MessageManager.formatMessage("action.clustering_matrix_for",cm.getAnnotDescr(),5f), progBar = System.currentTimeMillis()); + ap.alignFrame.setProgressBar( + MessageManager.formatMessage( + "action.clustering_matrix_for", + cm.getAnnotDescr(), 5f), + progBar = System.currentTimeMillis()); cm.setGroupSet(GroupSet.makeGroups(cm, true)); cm.randomlyReColourGroups(); cm.transferGroupColorsTo(alignmentAnnotation); @@ -1141,7 +1169,6 @@ public class AnnotationLabels extends JPanel } drawComponent(g2, true, width); - } /** @@ -1171,32 +1198,146 @@ public class AnnotationLabels extends JPanel * @param width * Width for scaling labels */ - public void drawComponent(Graphics g, boolean clip, int width) + public void drawComponent(Graphics g, boolean clip, int givenWidth) { - if (av.getFont().getSize() < 10) + int width = givenWidth; + IdwidthAdjuster iwa = null; + if (ap != null) { - g.setFont(font); + iwa = ap.idwidthAdjuster; + if ((Cache.getDefault(ADJUST_ANNOTATION_LABELS_WIDTH_PREF, true) + || Jalview.isHeadlessMode())) + { + Graphics2D g2d = (Graphics2D) g; + Graphics dummy = g2d.create(); + int newAnnotationIdWidth = drawLabels(dummy, clip, width, false, + null); + dummy.dispose(); + Dimension d = ap.calculateDefaultAlignmentIdWidth(); + int alignmentIdWidth = d.width; + if (iwa != null && !iwa.manuallyAdjusted()) + { + // If no manual adjustment to ID column with has been made then adjust + // width match widest of alignment or annotation id widths + boolean allowShrink = Cache.getDefault("ALLOW_SHRINK_ID_WIDTH", + false); + width = Math.max(alignmentIdWidth, newAnnotationIdWidth); + if (clip && width < givenWidth && !allowShrink) + { + width = givenWidth; + } + } + else if (newAnnotationIdWidth != annotationIdWidth + && newAnnotationIdWidth > givenWidth + && newAnnotationIdWidth > alignmentIdWidth) + { + // otherwise if the annotation id width has become larger than the + // current id width, increase + width = newAnnotationIdWidth; + annotationIdWidth = newAnnotationIdWidth; + } + // set the width if it's changed + if (width != ap.av.getIdWidth()) + { + iwa.setWidth(width); + } + } } else { - g.setFont(av.getFont()); + int newAnnotationIdWidth = drawLabels(g, clip, width, false, null); + width = Math.max(newAnnotationIdWidth, givenWidth); + } + drawLabels(g, clip, width, true, null); + } + + /** + * Render the full set of annotation Labels for the alignment at the given + * cursor. If actuallyDraw is false or g is null then no actual drawing will + * occur, but the widest label width will be returned. If g is null then + * fmetrics must be supplied. + * + * Returns the width of the annotation labels. + * + * @param g + * Graphics2D instance (needed for font scaling) + * @param clip + * - true indicates that only current visible area needs to be + * rendered + * @param width + * Width for scaling labels + * @param fmetrics + * FontMetrics if Graphics object g is null + */ + public int drawLabels(Graphics g0, boolean clip, int width, + boolean actuallyDraw, FontMetrics fmetrics) + { + if (clip) + { + clip = Cache.getDefault("MOVE_SEQUENCE_ID_WITH_VISIBLE_ANNOTATIONS", + true); + } + Graphics g = null; + // create a dummy Graphics object if not drawing and one is supplied + if (g0 != null) + { + if (!actuallyDraw) + { + Graphics2D g2d = (Graphics2D) g0; + g = g2d.create(); + } + else + { + g = g0; + } } + int actualWidth = 0; + if (g != null) + { + if (av.getFont().getSize() < 10) + { + g.setFont(font); + } + else + { + g.setFont(av.getFont()); + } + } + + FontMetrics fm = fmetrics == null ? g.getFontMetrics(g.getFont()) + : fmetrics; + if (actuallyDraw) + { + g.setColor(Color.white); + g.fillRect(0, 0, getWidth(), getHeight()); + + if (!Cache.getDefault(RESIZE_MARGINS_MARK_PREF, false) + && !av.getWrapAlignment()) + { + g.setColor(Color.LIGHT_GRAY); + g.drawLine(0, HEIGHT_ADJUSTER_HEIGHT / 4, HEIGHT_ADJUSTER_WIDTH / 4, + HEIGHT_ADJUSTER_HEIGHT / 4); + g.drawLine(0, 3 * HEIGHT_ADJUSTER_HEIGHT / 4, + HEIGHT_ADJUSTER_WIDTH / 4, 3 * HEIGHT_ADJUSTER_HEIGHT / 4); - FontMetrics fm = g.getFontMetrics(g.getFont()); - g.setColor(Color.white); - g.fillRect(0, 0, getWidth(), getHeight()); + } + } - g.translate(0, getScrollOffset()); - g.setColor(Color.black); + if (actuallyDraw) + { + g.translate(0, getScrollOffset()); + g.setColor(Color.black); + } SequenceI lastSeqRef = null; String lastLabel = null; AlignmentAnnotation[] aa = av.getAlignment().getAlignmentAnnotation(); - int fontHeight = g.getFont().getSize(); + int fontHeight = g != null ? g.getFont().getSize() + : fm.getFont().getSize(); int y = 0; int x = 0; int graphExtras = 0; int offset = 0; - Font baseFont = g.getFont(); + Font baseFont = g != null ? g.getFont() : fm.getFont(); FontMetrics baseMetrics = fm; int ofontH = fontHeight; int sOffset = 0; @@ -1260,8 +1401,10 @@ public class AnnotationLabels extends JPanel continue; } } - g.setColor(Color.black); - + if (actuallyDraw && g != null) + { + g.setColor(Color.black); + } offset = -aa[i].height / 2; if (aa[i].hasText) @@ -1306,7 +1449,9 @@ public class AnnotationLabels extends JPanel vertBar = true; } } - x = width - fm.stringWidth(label) - 3; + + int labelWidth = fm.stringWidth(label) + 3; + x = width - labelWidth; if (aa[i].graphGroup > -1) { @@ -1339,10 +1484,15 @@ public class AnnotationLabels extends JPanel s = ((float) fontHeight) / (float) ofontH; Font f = baseFont .deriveFont(AffineTransform.getScaleInstance(s, s)); - g.setFont(f); - fm = g.getFontMetrics(); - graphExtras = (aa[i].height - (groupSize * (fontHeight + 8))) - / 2; + Canvas c = new Canvas(); + fm = c.getFontMetrics(f); + if (actuallyDraw && g != null) + { + g.setFont(f); + // fm = g.getFontMetrics(); + graphExtras = (aa[i].height + - (groupSize * (fontHeight + 8))) / 2; + } } } if (visible) @@ -1351,58 +1501,81 @@ public class AnnotationLabels extends JPanel { if (aa[gg].graphGroup == aa[i].graphGroup) { - x = width - fm.stringWidth(aa[gg].label) - 3; - g.drawString(aa[gg].label, x, y - graphExtras); - - if (aa[gg]._linecolour != null) + labelWidth = fm.stringWidth(aa[gg].label) + 3; + x = width - labelWidth; + if (actuallyDraw && g != null) { + g.drawString(aa[gg].label, x, y - graphExtras); - g.setColor(aa[gg]._linecolour); - g.drawLine(x, y - graphExtras + 3, - x + fm.stringWidth(aa[gg].label), - y - graphExtras + 3); - } + if (aa[gg]._linecolour != null) + { + + g.setColor(aa[gg]._linecolour); + g.drawLine(x, y - graphExtras + 3, + x + fm.stringWidth(aa[gg].label), + y - graphExtras + 3); + } - g.setColor(Color.black); + g.setColor(Color.black); + } graphExtras += fontHeight + 8; } } } - g.setFont(baseFont); + if (actuallyDraw && g != null) + { + g.setFont(baseFont); + } fm = baseMetrics; fontHeight = ofontH; } else { - if (vertBar) + if (actuallyDraw && g != null) { - g.drawLine(width - 3, y + offset - fontHeight, width - 3, - (int) (y - 1.5 * aa[i].height - offset - fontHeight)); - // g.drawLine(20, y + offset, x - 20, y + offset); + if (vertBar) + { + g.drawLine(width - 3, y + offset - fontHeight, width - 3, + (int) (y - 1.5 * aa[i].height - offset - fontHeight)); + // g.drawLine(20, y + offset, x - 20, y + offset); + } + g.drawString(label, x, y + offset); } - g.drawString(label, x, y + offset); } lastSeqRef = aa[i].sequenceRef; + + if (labelWidth > actualWidth) + { + actualWidth = labelWidth; + } } } if (!resizePanel && dragEvent != null && aa != null) { - g.setColor(Color.lightGray); - g.drawString( - (aa[selectedRow].sequenceRef == null ? "" - : aa[selectedRow].sequenceRef.getName()) - + aa[selectedRow].label, - dragEvent.getX(), dragEvent.getY() - getScrollOffset()); + if (actuallyDraw && g != null) + { + g.setColor(Color.lightGray); + g.drawString( + (aa[selectedRow].sequenceRef == null ? "" + : aa[selectedRow].sequenceRef.getName()) + + aa[selectedRow].label, + dragEvent.getX(), dragEvent.getY() - getScrollOffset()); + } } if (!av.getWrapAlignment() && ((aa == null) || (aa.length < 1))) { - g.drawString(MessageManager.getString("label.right_click"), 2, 8); - g.drawString(MessageManager.getString("label.to_add_annotation"), 2, - 18); + if (actuallyDraw && g != null) + { + g.drawString(MessageManager.getString("label.right_click"), 2, 8); + g.drawString(MessageManager.getString("label.to_add_annotation"), 2, + 18); + } } + + return actualWidth; } public int getScrollOffset() diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index efefa21..035da25 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -51,6 +51,7 @@ import java.awt.event.WindowEvent; import java.awt.geom.AffineTransform; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; import java.io.File; import java.io.FileWriter; import java.io.IOException; @@ -63,6 +64,7 @@ import java.util.Hashtable; import java.util.List; import java.util.ListIterator; import java.util.Locale; +import java.util.Map; import java.util.Vector; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -572,15 +574,16 @@ public class Desktop extends jalview.jbgui.GDesktop } // Thread off a new instance of the file chooser - this reduces the time - // it - // takes to open it later on. + // it takes to open it later on. new Thread(new Runnable() { @Override public void run() { jalview.bin.Console.debug("Filechooser init thread started."); - String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT"); + String fileFormat = FileLoader.getUseDefaultFileFormat() + ? Cache.getProperty("DEFAULT_FILE_FORMAT") + : null; JalviewFileChooser.forRead(Cache.getProperty("LAST_DIRECTORY"), fileFormat); jalview.bin.Console.debug("Filechooser init thread finished."); @@ -1267,7 +1270,9 @@ public class Desktop extends jalview.jbgui.GDesktop @Override public void inputLocalFileMenuItem_actionPerformed(AlignViewport viewport) { - String fileFormat = Cache.getProperty("DEFAULT_FILE_FORMAT"); + String fileFormat = FileLoader.getUseDefaultFileFormat() + ? Cache.getProperty("DEFAULT_FILE_FORMAT") + : null; JalviewFileChooser chooser = JalviewFileChooser.forRead( Cache.getProperty("LAST_DIRECTORY"), fileFormat, BackupFiles.getEnabled()); @@ -3672,17 +3677,19 @@ public class Desktop extends jalview.jbgui.GDesktop } /** - * checks if any progress bars are being displayed in any of the windows managed by the desktop + * checks if any progress bars are being displayed in any of the windows + * managed by the desktop + * * @return */ public boolean operationsAreInProgress() { JInternalFrame[] frames = getAllFrames(); - for (JInternalFrame frame:frames) + for (JInternalFrame frame : frames) { if (frame instanceof IProgressIndicator) { - if (((IProgressIndicator)frame).operationInProgress()) + if (((IProgressIndicator) frame).operationInProgress()) { return true; } @@ -3690,4 +3697,37 @@ public class Desktop extends jalview.jbgui.GDesktop } return operationInProgress(); } + + /** + * keep track of modal JvOptionPanes open as modal dialogs for AlignFrames. + * The way the modal JInternalFrame is made means it cannot be a child of an + * AlignFrame, so closing the AlignFrame might leave the modal open :( + */ + private static Map alignFrameModalMap = new HashMap<>(); + + protected static void addModal(AlignFrame af, JInternalFrame jif) + { + alignFrameModalMap.put(af, jif); + } + + protected static void closeModal(AlignFrame af) + { + if (!alignFrameModalMap.containsKey(af)) + { + return; + } + JInternalFrame jif = alignFrameModalMap.get(af); + if (jif != null) + { + try + { + jif.setClosed(true); + } catch (PropertyVetoException e) + { + e.printStackTrace(); + } + } + alignFrameModalMap.remove(af); + } + } diff --git a/src/jalview/gui/EditNameDialog.java b/src/jalview/gui/EditNameDialog.java index 4b8a1fd..6d5cb46 100644 --- a/src/jalview/gui/EditNameDialog.java +++ b/src/jalview/gui/EditNameDialog.java @@ -129,6 +129,6 @@ public class EditNameDialog JvOptionPane.frameDialog(panel, title, JvOptionPane.PLAIN_MESSAGE, options, ok, actions, false); - */ + */ } } diff --git a/src/jalview/gui/IdCanvas.java b/src/jalview/gui/IdCanvas.java index c94dee0..eb0715a 100755 --- a/src/jalview/gui/IdCanvas.java +++ b/src/jalview/gui/IdCanvas.java @@ -22,6 +22,7 @@ package jalview.gui; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; @@ -382,6 +383,12 @@ public class IdCanvas extends JPanel implements ViewportListenerI void drawIdsWrapped(Graphics2D g, AlignViewport alignViewport, int startSeq, int pageHeight) { + drawIdsWrapped(g, alignViewport, startSeq, pageHeight, -1); + } + + void drawIdsWrapped(Graphics2D g, AlignViewport alignViewport, + int startSeq, int pageHeight, int idWidth) + { int alignmentWidth = alignViewport.getAlignment().getWidth(); final int alheight = alignViewport.getAlignment().getHeight(); @@ -428,8 +435,23 @@ public class IdCanvas extends JPanel implements ViewportListenerI if (labels != null && alignViewport.isShowAnnotation()) { + int getWidth = getWidth(); + int thisIdWidth = getWidth; g.translate(0, ypos + (alheight * charHeight)); - labels.drawComponent(g, getWidth()); + if (!manuallyAdjusted()) + { + int getAnnotationsIdWidth = labels.drawLabels(g, false, -1, false, + null); + thisIdWidth = idWidth < 0 ? getAnnotationsIdWidth : idWidth; + if (thisIdWidth > getWidth) + { + this.setPreferredSize( + new Dimension(thisIdWidth, this.getHeight())); + this.repaint(); + alignViewport.setIdWidth(thisIdWidth); + } + } + labels.drawComponent(g, false, thisIdWidth); g.translate(0, -ypos - (alheight * charHeight)); } @@ -585,4 +607,16 @@ public class IdCanvas extends JPanel implements ViewportListenerI repaint(); } } + + private boolean manuallyAdjusted = false; + + public boolean manuallyAdjusted() + { + return manuallyAdjusted; + } + + public void setManuallyAdjusted(boolean b) + { + manuallyAdjusted = b; + } } diff --git a/src/jalview/gui/IdwidthAdjuster.java b/src/jalview/gui/IdwidthAdjuster.java index 4ba0699..4596e1f 100755 --- a/src/jalview/gui/IdwidthAdjuster.java +++ b/src/jalview/gui/IdwidthAdjuster.java @@ -20,8 +20,6 @@ */ package jalview.gui; -import jalview.api.AlignViewportI; - import java.awt.Color; import java.awt.Cursor; import java.awt.Graphics; @@ -31,6 +29,9 @@ import java.awt.event.MouseMotionListener; import javax.swing.JPanel; +import jalview.api.AlignViewportI; +import jalview.bin.Cache; + /** * DOCUMENT ME! * @@ -136,6 +137,18 @@ public class IdwidthAdjuster extends JPanel return; } + /* + * don't allow residue width to be < 1 in wrapped format + */ + if (viewport.getWrapAlignment()) + { + SeqCanvas sc = ap.getSeqPanel().seqCanvas; + if (sc != null && sc.getWrappedCanvasWidth(sc.getWidth() - dif) < 1) + { + return; + } + } + oldX = evt.getX(); /* @@ -146,9 +159,29 @@ public class IdwidthAdjuster extends JPanel return; } viewport.setIdWidth(newWidth); + ap.validateAnnotationDimensions(false); + ap.paintAlignment(true, false); + + ap.getIdPanel().getIdCanvas().setManuallyAdjusted(true); + } + + public void setWidth(int newWidth) + { + if (newWidth < MIN_ID_WIDTH + || ap.getIdPanel().getIdCanvas().manuallyAdjusted()) + { + return; + } + final AlignViewportI viewport = ap.getAlignViewport(); + viewport.setIdWidth(newWidth); ap.paintAlignment(true, false); } + public boolean manuallyAdjusted() + { + return ap.getIdPanel().getIdCanvas().manuallyAdjusted(); + } + @Override public void mouseMoved(MouseEvent evt) { @@ -167,8 +200,21 @@ public class IdwidthAdjuster extends JPanel @Override public void paintComponent(Graphics g) { + int width = getWidth(); + int height = getHeight(); g.setColor(Color.white); - g.fillRect(0, 0, getWidth(), getHeight()); + g.fillRect(0, 0, width, height); + + if (!Cache.getDefault(AnnotationLabels.RESIZE_MARGINS_MARK_PREF, false)) + // && !ap.getAlignViewport().getWrapAlignment()) // now allowing adjustment + // in wrap mode + { + int spacer = Math.max(2, AnnotationLabels.HEIGHT_ADJUSTER_HEIGHT / 4); + g.setColor(Color.LIGHT_GRAY); + g.drawLine(width - 3 * spacer, 0, width - 3 * spacer, height / 2); + g.drawLine(width - spacer, 0, width - spacer, height / 2); + } + setCursor(Cursor.getPredefinedCursor(Cursor.W_RESIZE_CURSOR)); } } diff --git a/src/jalview/gui/ImageExporter.java b/src/jalview/gui/ImageExporter.java index 4ea30d9..8d28b1b 100644 --- a/src/jalview/gui/ImageExporter.java +++ b/src/jalview/gui/ImageExporter.java @@ -34,6 +34,7 @@ import jalview.util.ImageMaker; import jalview.util.ImageMaker.TYPE; import jalview.util.MessageManager; import jalview.util.Platform; +import jalview.util.StringUtils; import jalview.util.imagemaker.BitmapImageSizing; /** @@ -111,7 +112,8 @@ public class ImageExporter } public void doExport(File file, Component parent, int width, int height, - String imageSource, String renderer, BitmapImageSizing userBis) throws ImageOutputException + String imageSource, String renderer, BitmapImageSizing userBis) + throws ImageOutputException { final long messageId = System.currentTimeMillis(); setStatus( @@ -126,8 +128,9 @@ public class ImageExporter { if (Desktop.instance.isInBatchMode()) { - // defensive error report - we could wait for user input.. I guess ? - throw(new ImageOutputException("Need an output file to render to when exporting images in batch mode!")); + // defensive error report - we could wait for user input.. I guess ? + throw (new ImageOutputException( + "Need an output file to render to when exporting images in batch mode!")); } JalviewFileChooser chooser = imageType.getFileChooser(); chooser.setFileView(new JalviewFileView()); @@ -164,9 +167,9 @@ public class ImageExporter renderStyle = "Text"; } AtomicBoolean textSelected = new AtomicBoolean( - !"Lineart".equals(renderStyle)); - if ((imageType == TYPE.EPS || imageType == TYPE.SVG) - && LineartOptions.PROMPT_EACH_TIME.equals(renderStyle) + !StringUtils.equalsIgnoreCase("lineart", renderStyle)); + if ((imageType == TYPE.EPS || imageType == TYPE.SVG) && StringUtils + .equalsIgnoreCase(LineartOptions.PROMPT_EACH_TIME, renderStyle) && !Jalview.isHeadlessMode()) { final File chosenFile = file; @@ -188,8 +191,8 @@ public class ImageExporter else { /* - * character rendering not required, or preference already set - * - just do the export + * character rendering not required, or preference already set + * or we're in headless mode - just do the export */ exportImage(file, !textSelected.get(), width, height, messageId, userBis); @@ -226,8 +229,8 @@ public class ImageExporter messageId); } catch (Exception e) { - jalview.bin.Console.error(String.format("Error creating %s file: %s", type, - e.toString()),e); + jalview.bin.Console.error(String.format("Error creating %s file: %s", + type, e.toString()), e); setStatus(MessageManager.formatMessage("info.error_creating_file", type), messageId); } diff --git a/src/jalview/gui/JvOptionPane.java b/src/jalview/gui/JvOptionPane.java index b4c3256..5b926c3 100644 --- a/src/jalview/gui/JvOptionPane.java +++ b/src/jalview/gui/JvOptionPane.java @@ -32,10 +32,12 @@ import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseMotionAdapter; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.beans.PropertyVetoException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -50,8 +52,11 @@ import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JInternalFrame; import javax.swing.JLayeredPane; +import javax.swing.JMenu; +import javax.swing.JMenuBar; import javax.swing.JOptionPane; import javax.swing.JPanel; +import javax.swing.JRootPane; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.event.InternalFrameEvent; @@ -1071,9 +1076,16 @@ public class JvOptionPane extends JOptionPane if (parentComponent != this && !(parentComponent == null && Desktop.instance == null)) { + // note the parent goes back to a JRootPane so is probably + // Desktop.getDesktop() JInternalFrame jif = this.createInternalFrame( parentComponent != null ? parentComponent : Desktop.instance, title); + // connect to the alignFrame using a map in Desktop + if (parentComponent instanceof AlignFrame) + { + Desktop.addModal((AlignFrame) parentComponent, jif); + } jif.setFrameIcon(null); jif.addInternalFrameListener(new InternalFrameListener() { @@ -1129,7 +1141,13 @@ public class JvOptionPane extends JOptionPane private void internalDialogHandleResponse() { - String responseString = (String) this.getValue(); + Object value = this.getValue(); + if (value == null + || (value instanceof Integer && (Integer) value == -1)) + { + return; + } + String responseString = value.toString(); int response = ourOptions.indexOf(responseString); if (!Platform.isJS()) @@ -1499,11 +1517,26 @@ public class JvOptionPane extends JOptionPane lp.add(modalInterceptor); f.toFront(); + // disable the main menu bar if in Linux + JMenuBar menubar = null; + if (Platform.isLinux()) + { + JRootPane rootpane = Desktop.getDesktop().getRootPane(); + menubar = rootpane.getJMenuBar(); + } + // We need to explicitly dispatch events when we are blocking the event // dispatch thread. EventQueue queue = Toolkit.getDefaultToolkit().getSystemEventQueue(); try { + if (menubar != null) + { + // don't allow clicks on main menu on linux due to a hanging bug. + // see JAL-4214. + setMenusEnabled(menubar, false); + } + while (!f.isClosed()) { if (EventQueue.isDispatchThread()) @@ -1515,11 +1548,25 @@ public class JvOptionPane extends JOptionPane // EventQueue.dispatchEvent() directly, because it is // protected, unfortunately. if (ev instanceof ActiveEvent) + { ((ActiveEvent) ev).dispatch(); - else if (ev.getSource() instanceof Component) + } + else if (ev instanceof KeyEvent && ((KeyEvent) ev).isControlDown() + && menubar != null) + { + // temporarily enable menus to send Ctrl+? KeyEvents + setMenusEnabled(menubar, true); ((Component) ev.getSource()).dispatchEvent(ev); + setMenusEnabled(menubar, false); + } else if (ev.getSource() instanceof MenuComponent) + { ((MenuComponent) ev.getSource()).dispatchEvent(ev); + } + else if (ev.getSource() instanceof Component) + { + ((Component) ev.getSource()).dispatchEvent(ev); + } // Other events are ignored as per spec in // EventQueue.dispatchEvent } @@ -1534,14 +1581,34 @@ public class JvOptionPane extends JOptionPane // If we get interrupted, then leave the modal state. } finally { + // re-enable the main menu bar + if (menubar != null) + { + setMenusEnabled(menubar, true); + } + // Clean up the modal interceptor. lp.remove(modalInterceptor); + // unpaint the frame + f.setVisible(false); + + // close the frame + try + { + f.setClosed(true); + } catch (PropertyVetoException e) + { + f.doDefaultCloseAction(); + } + // Remove the internal frame from its parent, so it is no longer // lurking around and clogging memory. Container parent = f.getParent(); if (parent != null) + { parent.remove(f); + } } } @@ -1612,4 +1679,14 @@ public class JvOptionPane extends JOptionPane return jvop; } + + private static void setMenusEnabled(JMenuBar menubar, boolean b) + { + for (int i = 0; i < menubar.getMenuCount(); i++) + { + JMenu menu = menubar.getMenu(i); + menu.setEnabled(b); + } + } + } diff --git a/src/jalview/gui/PopupMenu.java b/src/jalview/gui/PopupMenu.java index 7e86704..109c140 100644 --- a/src/jalview/gui/PopupMenu.java +++ b/src/jalview/gui/PopupMenu.java @@ -906,6 +906,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener if (Platform.isJS()) { details = new JInternalFrame(); + details.setFrameIcon(null); JPanel panel = new JPanel(new BorderLayout()); panel.setOpaque(true); panel.setBackground(Color.white); @@ -1834,6 +1835,7 @@ public class PopupMenu extends JPopupMenu implements ColourChangeListener pane.setBackground(Color.WHITE); pane.add(textLabel, BorderLayout.NORTH); frame = new JInternalFrame(); + frame.setFrameIcon(null); frame.getContentPane().add(new JScrollPane(pane)); } else diff --git a/src/jalview/gui/SeqCanvas.java b/src/jalview/gui/SeqCanvas.java index 4f82557..39039eb 100755 --- a/src/jalview/gui/SeqCanvas.java +++ b/src/jalview/gui/SeqCanvas.java @@ -550,6 +550,20 @@ public class SeqCanvas extends JPanel implements ViewportListenerI return (canvasWidth - labelWidthEast - labelWidthWest) / charWidth; } + public int getMinimumWrappedCanvasWidth() + { + int charWidth = av.getCharWidth(); + FontMetrics fm = getFontMetrics(av.getFont()); + int labelWidth = 0; + if (av.getScaleRightWrapped() || av.getScaleLeftWrapped()) + { + labelWidth = getLabelWidth(fm); + } + labelWidthEast = av.getScaleRightWrapped() ? labelWidth : 0; + labelWidthWest = av.getScaleLeftWrapped() ? labelWidth : 0; + return labelWidthEast + labelWidthWest + charWidth; + } + /** * Returns a pixel width sufficient to show the largest sequence coordinate * (end position) in the alignment, calculated as the FontMetrics width of @@ -1375,8 +1389,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI } else if (inGroup) { - drawVerticals(g, sx, xwidth, visWidth, oldY, sy); - drawHorizontals(g, sx, xwidth, visWidth, top, bottom); + drawVerticals(g, sx, xwidth, visWidth, oldY, bottom); + drawHorizontals(g, sx, xwidth, visWidth, top, bottom+1); // reset top and bottom top = -1; @@ -1387,8 +1401,8 @@ public class SeqCanvas extends JPanel implements ViewportListenerI if (inGroup) { sy = verticalOffset + ((i - startSeq) * charHeight); - drawVerticals(g, sx, xwidth, visWidth, oldY, sy); - drawHorizontals(g, sx, xwidth, visWidth, top, bottom); + drawVerticals(g, sx, xwidth, visWidth, oldY, bottom); + drawHorizontals(g, sx, xwidth, visWidth, top, bottom+1); } } } diff --git a/src/jalview/gui/SplitFrame.java b/src/jalview/gui/SplitFrame.java index 08d6e03..73744b3 100644 --- a/src/jalview/gui/SplitFrame.java +++ b/src/jalview/gui/SplitFrame.java @@ -900,6 +900,7 @@ public class SplitFrame extends GSplitFrame implements SplitContainerI }); featureSettingsUI = new JInternalFrame(MessageManager.getString( "label.sequence_feature_settings_for_CDS_and_Protein")); + featureSettingsUI.setFrameIcon(null); featureSettingsPanels.setOpaque(true); JPanel dialog = new JPanel(); diff --git a/src/jalview/gui/StructureChooser.java b/src/jalview/gui/StructureChooser.java index 6221f31..d00b1c2 100644 --- a/src/jalview/gui/StructureChooser.java +++ b/src/jalview/gui/StructureChooser.java @@ -71,7 +71,6 @@ import jalview.gui.structurechooser.PDBStructureChooserQuerySource; import jalview.gui.structurechooser.StructureChooserQuerySource; import jalview.gui.structurechooser.ThreeDBStructureChooserQuerySource; import jalview.io.DataSourceType; -import jalview.io.FileFormatException; import jalview.io.JalviewFileChooser; import jalview.io.JalviewFileView; import jalview.jbgui.FilterOption; @@ -85,7 +84,6 @@ import jalview.util.StringUtils; import jalview.ws.DBRefFetcher; import jalview.ws.DBRefFetcher.FetchFinishedListenerI; import jalview.ws.datamodel.alphafold.PAEContactMatrix; -import jalview.ws.dbsources.EBIAlfaFold; import jalview.ws.seqfetcher.DbSourceProxy; import jalview.ws.sifts.SiftsSettings; @@ -1801,7 +1799,9 @@ public class StructureChooser extends GStructureChooser sc.mainFrame.dispose(); if (showRefAnnotations) + { showReferenceAnnotationsForSequence(ap.alignFrame, seq); + } return sv; } diff --git a/src/jalview/gui/StructureViewer.java b/src/jalview/gui/StructureViewer.java index 6cef665..ad3fc6a 100644 --- a/src/jalview/gui/StructureViewer.java +++ b/src/jalview/gui/StructureViewer.java @@ -21,9 +21,11 @@ package jalview.gui; import java.util.ArrayList; +import java.util.EnumSet; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Map.Entry; @@ -66,7 +68,27 @@ public class StructureViewer public enum ViewerType { - JMOL, CHIMERA, CHIMERAX, PYMOL + JMOL, CHIMERA, CHIMERAX, PYMOL; + + public static ViewerType getFromString(String viewerString) + { + ViewerType viewerType = null; + if (!"none".equals(viewerString)) + { + for (ViewerType v : EnumSet.allOf(ViewerType.class)) + { + String name = v.name().toLowerCase(Locale.ROOT).replaceAll(" ", + ""); + if (viewerString.equals(name)) + { + viewerType = v; + break; + } + } + } + return viewerType; + } + }; /** diff --git a/src/jalview/io/FileLoader.java b/src/jalview/io/FileLoader.java index 49571dc..bf79fa4 100755 --- a/src/jalview/io/FileLoader.java +++ b/src/jalview/io/FileLoader.java @@ -74,6 +74,8 @@ public class FileLoader implements Runnable private File selectedFile; + private static boolean useDefaultFileFormat = false; + /** * default constructor always raised errors in GUI dialog boxes */ @@ -642,6 +644,8 @@ public class FileLoader implements Runnable } this.setShouldBeSaved(); + // after first file loaded we revert to assuming a default file format + useDefaultFileFormat = true; } /** @@ -684,4 +688,9 @@ public class FileLoader implements Runnable QuitHandler.Message.UNSAVED_ALIGNMENTS); } + public static boolean getUseDefaultFileFormat() + { + return useDefaultFileFormat; + } + } diff --git a/src/jalview/io/JalviewFileChooser.java b/src/jalview/io/JalviewFileChooser.java index b97df13..3f44596 100755 --- a/src/jalview/io/JalviewFileChooser.java +++ b/src/jalview/io/JalviewFileChooser.java @@ -40,15 +40,19 @@ import java.util.StringTokenizer; import java.util.Vector; import javax.swing.BoxLayout; -import javax.swing.DefaultListCellRenderer; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JFileChooser; +import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; +import javax.swing.ListCellRenderer; import javax.swing.SpringLayout; +import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.TitledBorder; import javax.swing.filechooser.FileFilter; import javax.swing.plaf.basic.BasicFileChooserUI; @@ -262,6 +266,31 @@ public class JalviewFileChooser extends JFileChooser // file filters to fix bug on Mac OSX setAcceptAllFileFilterUsed(acceptAny); + // add a "All known alignment files" option + List allExtensions = new ArrayList<>(); + for (String[] format : formats) + { + String[] extensions = format[0].split(","); + for (String ext : extensions) + { + if (!allExtensions.contains(ext)) + { + allExtensions.add(ext); + } + } + } + allExtensions.sort(null); + JalviewFileFilter alljvf = new JalviewFileFilter( + allExtensions.toArray(new String[] {}), + MessageManager.getString("label.all_known_alignment_files")); + alljvf.setExtensionListInDescription(false); + addChoosableFileFilter(alljvf); + + if (selected == null) + { + chosen = alljvf; + } + for (String[] format : formats) { JalviewFileFilter jvf = new JalviewFileFilter(format[0], format[1]); @@ -524,6 +553,11 @@ public class JalviewFileChooser extends JFileChooser } } + if (!file.isAbsolute() && file.exists()) + { + file = file.getAbsoluteFile(); + } + setSelectedFile(file); } } @@ -552,10 +586,7 @@ public class JalviewFileChooser extends JFileChooser } list = new JList<>(recent); - - DefaultListCellRenderer dlcr = new DefaultListCellRenderer(); - dlcr.setHorizontalAlignment(DefaultListCellRenderer.RIGHT); - list.setCellRenderer(dlcr); + list.setCellRenderer(new recentlyOpenedCellRenderer()); list.addMouseListener(new MouseAdapter() { @@ -566,8 +597,11 @@ public class JalviewFileChooser extends JFileChooser } }); - this.setBorder(new javax.swing.border.TitledBorder( - MessageManager.getString("label.recently_opened"))); + TitledBorder recentlyOpenedBorder = new TitledBorder( + MessageManager.getString("label.recently_opened")); + recentlyOpenedBorder.setTitleFont( + recentlyOpenedBorder.getTitleFont().deriveFont(10f)); + this.setBorder(recentlyOpenedBorder); final JScrollPane scroller = new JScrollPane(list); @@ -577,18 +611,11 @@ public class JalviewFileChooser extends JFileChooser layout.putConstraint(SpringLayout.NORTH, scroller, 5, SpringLayout.NORTH, this); - if (Platform.isAMacAndNotJS()) - { - scroller.setPreferredSize(new Dimension(500, 100)); - } - else - { - scroller.setPreferredSize(new Dimension(530, 200)); - } - + // one size okay for all + scroller.setPreferredSize(new Dimension(280, 105)); this.add(scroller); - javax.swing.SwingUtilities.invokeLater(new Runnable() + SwingUtilities.invokeLater(new Runnable() { @Override public void run() @@ -602,6 +629,77 @@ public class JalviewFileChooser extends JFileChooser } + class recentlyOpenedCellRenderer extends JLabel + implements ListCellRenderer + { + private final static int maxChars = 46; + + private final static String ellipsis = "..."; + + @Override + public Component getListCellRendererComponent( + JList list, String value, int index, + boolean isSelected, boolean cellHasFocus) + { + String filename = value.toString(); + String displayFilename; + if (filename.length() > maxChars) + { + StringBuilder displayFileSB = new StringBuilder(); + File file = new File(filename); + displayFileSB.append(file.getName()); + if (file.getParent() != null) + { + File parent = file; + boolean spaceleft = true; + while (spaceleft && parent.getParent() != null) + { + parent = parent.getParentFile(); + String name = parent.getName(); + displayFileSB.insert(0, File.separator); + if (displayFileSB.length() + name.length() < maxChars - 1) + { + displayFileSB.insert(0, name); + } + else + { + displayFileSB.insert(0, ellipsis); + spaceleft = false; + } + } + if (spaceleft && filename.startsWith(File.separator) + && !(displayFileSB.charAt(0) == File.separatorChar)) + { + displayFileSB.insert(0, File.separator); + } + } + displayFilename = displayFileSB.toString(); + } + else + { + displayFilename = filename; + } + this.setText(displayFilename.toString()); + this.setToolTipText(filename); + if (isSelected) + { + setBackground(list.getSelectionBackground()); + setForeground(list.getSelectionForeground()); + } + else + { + setBackground(list.getBackground()); + setForeground(list.getForeground()); + } + this.setHorizontalAlignment(SwingConstants.TRAILING); + this.setEnabled(list.isEnabled()); + this.setFont(list.getFont().deriveFont(12f)); + this.setOpaque(true); + return this; + } + + } + /* @Override public JalviewFileChooser setResponseHandler(Object response, diff --git a/src/jalview/io/JalviewFileFilter.java b/src/jalview/io/JalviewFileFilter.java index 10dc9ea..5e9a242 100755 --- a/src/jalview/io/JalviewFileFilter.java +++ b/src/jalview/io/JalviewFileFilter.java @@ -20,12 +20,11 @@ */ package jalview.io; -import java.util.Locale; - import java.io.File; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.Locale; import java.util.Map; import java.util.StringTokenizer; @@ -175,7 +174,7 @@ public class JalviewFileFilter extends FileFilter while (extensions.hasNext()) { - fullDescription += (", " + extensions.next()); + fullDescription += (", ." + extensions.next()); } } diff --git a/src/jalview/jbgui/GAlignFrame.java b/src/jalview/jbgui/GAlignFrame.java index c753dc1..7daeb37 100755 --- a/src/jalview/jbgui/GAlignFrame.java +++ b/src/jalview/jbgui/GAlignFrame.java @@ -57,7 +57,6 @@ import jalview.bin.Cache; import jalview.gui.JvSwingUtils; import jalview.gui.Preferences; import jalview.io.FileFormats; -import jalview.io.exceptions.ImageOutputException; import jalview.schemes.ResidueColourScheme; import jalview.util.MessageManager; import jalview.util.Platform; @@ -221,6 +220,7 @@ public class GAlignFrame extends JInternalFrame { try { + setFrameIcon(null); // for Web-page embedding using id=align-frame-div setName("jalview-alignment"); @@ -1979,19 +1979,19 @@ public class GAlignFrame extends JInternalFrame protected void createPNG_actionPerformed(ActionEvent object) { // TODO Auto-generated method stub - + } protected void createEPS_actionPerformed(ActionEvent object) { // TODO Auto-generated method stub - + } protected void createSVG_actionPerformed(ActionEvent object) { // TODO Auto-generated method stub - + } protected void copyHighlightedColumns_actionPerformed( @@ -2487,7 +2487,6 @@ public class GAlignFrame extends JInternalFrame { } - protected void font_actionPerformed(ActionEvent e) { } @@ -2501,7 +2500,6 @@ public class GAlignFrame extends JInternalFrame } - protected void loadTreeMenuItem_actionPerformed(ActionEvent e) { diff --git a/src/jalview/jbgui/GAlignmentPanel.java b/src/jalview/jbgui/GAlignmentPanel.java index 6594e2d..2f0c75e 100755 --- a/src/jalview/jbgui/GAlignmentPanel.java +++ b/src/jalview/jbgui/GAlignmentPanel.java @@ -76,7 +76,7 @@ public class GAlignmentPanel extends JPanel protected JPanel scalePanelHolder = newJPanel(); - protected JPanel idPanelHolder = newJPanel(); + public JPanel idPanelHolder = newJPanel(); protected JPanel idSpaceFillerPanel1 = newJPanel(); diff --git a/src/jalview/renderer/AnnotationRenderer.java b/src/jalview/renderer/AnnotationRenderer.java index 65631e3..d943d39 100644 --- a/src/jalview/renderer/AnnotationRenderer.java +++ b/src/jalview/renderer/AnnotationRenderer.java @@ -27,16 +27,23 @@ import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; +import java.awt.RenderingHints; +import java.awt.Stroke; import java.awt.geom.AffineTransform; import java.awt.image.ImageObserver; import java.util.BitSet; import java.util.Hashtable; +import org.jfree.graphics2d.svg.SVGGraphics2D; +import org.jibble.epsgraphics.EpsGraphics2D; + import jalview.analysis.AAFrequency; import jalview.analysis.CodingUtils; import jalview.analysis.Rna; import jalview.analysis.StructureFrequency; import jalview.api.AlignViewportI; +import jalview.bin.Cache; +import jalview.bin.Console; import jalview.datamodel.AlignmentAnnotation; import jalview.datamodel.Annotation; import jalview.datamodel.ColumnSelection; @@ -88,6 +95,10 @@ public class AnnotationRenderer private boolean av_ignoreGapsConsensus; + private boolean vectorRendition = false; + + private boolean glyphLineDrawn = false; + /** * attributes set from AwtRenderPanelI */ @@ -189,7 +200,7 @@ public class AnnotationRenderer * if new annotation with a closing base pair half of the stem, * display a backward arrow */ - g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX }, + fillPolygon(g, new int[] { lastSSX + 5, lastSSX + 5, lastSSX }, new int[] { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3); @@ -209,7 +220,7 @@ public class AnnotationRenderer * if annotation ending with an opeing base pair half of the stem, * display a forward arrow */ - g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 }, + fillPolygon(g, new int[] { x2 - 5, x2 - 5, x2 }, new int[] { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, 3); @@ -221,7 +232,7 @@ public class AnnotationRenderer } } // draw arrow body - g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7); + fillRect(g, x1, y + 4 + iconOffset, x2 - x1, 7); } void drawNotCanonicalAnnot(Graphics g, Color nonCanColor, @@ -229,9 +240,8 @@ public class AnnotationRenderer int iconOffset, int startRes, int column, boolean validRes, boolean validEnd) { - // jalview.bin.Console.outPrintln(nonCanColor); + // Console.info(nonCanColor); - g.setColor(nonCanColor); int sCol = (lastSSX / charWidth) + hiddenColumns.visibleToAbsoluteColumn(startRes); int x1 = lastSSX; @@ -245,9 +255,16 @@ public class AnnotationRenderer boolean diffdownstream = !validRes || !validEnd || row_annotations[column] == null || !dc.equals(row_annotations[column].displayCharacter); - // jalview.bin.Console.outPrintln("Column "+column+" diff up: "+diffupstream+" + // Console.info("Column "+column+" diff up: + // "+diffupstream+" // down:"+diffdownstream); // If a closing base pair half of the stem, display a backward arrow + if (diffupstream || diffdownstream) + { + // draw glyphline under arrow + drawGlyphLine(g, lastSSX, x, y, iconOffset); + } + g.setColor(nonCanColor); if (column > 0 && Rna.isClosingParenthesis(dc)) { @@ -255,9 +272,10 @@ public class AnnotationRenderer // if (validRes && column>1 && row_annotations[column-2]!=null && // dc.equals(row_annotations[column-2].displayCharacter)) { - g.fillPolygon(new int[] { lastSSX + 5, lastSSX + 5, lastSSX }, + fillPolygon(g, new int[] { lastSSX + 5, lastSSX + 5, lastSSX }, new int[] - { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, + { y + iconOffset + 1, y + 13 + iconOffset, + y + 7 + iconOffset }, 3); x1 += 5; } @@ -272,9 +290,10 @@ public class AnnotationRenderer // display a forward arrow if (diffdownstream) { - g.fillPolygon(new int[] { x2 - 5, x2 - 5, x2 }, + fillPolygon(g, new int[] { x2 - 6, x2 - 6, x2 - 1 }, new int[] - { y + iconOffset, y + 14 + iconOffset, y + 8 + iconOffset }, + { y + iconOffset + 1, y + 13 + iconOffset, + y + 7 + iconOffset }, 3); x2 -= 5; } @@ -284,7 +303,8 @@ public class AnnotationRenderer } } // draw arrow body - g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 7); + unsetAntialias(g); + fillRect(g, x1, y + 4 + iconOffset, x2 - x1, 6); } // public void updateFromAnnotationPanel(FontMetrics annotFM, AlignViewportI @@ -447,6 +467,12 @@ public class AnnotationRenderer AlignViewportI av, Graphics g, int activeRow, int startRes, int endRes) { + if (g instanceof EpsGraphics2D || g instanceof SVGGraphics2D) + { + this.setVectorRendition(true); + } + Graphics2D g2d = (Graphics2D) g; + long stime = System.currentTimeMillis(); boolean usedFaded = false; // NOTES: @@ -592,9 +618,13 @@ public class AnnotationRenderer * * continue; } */ + // first pass sets up state for drawing continuation from left-hand // column // of startRes + + // flag used for vector rendition + this.glyphLineDrawn = false; x = (startRes == 0) ? 0 : -1; while (x < endRes - startRes) { @@ -626,6 +656,7 @@ public class AnnotationRenderer : null; if (x > -1) { + unsetAntialias(g); if (activeRow == i) { g.setColor(Color.red); @@ -634,24 +665,24 @@ public class AnnotationRenderer { if (columnSelection.contains(column)) { - g.fillRect(x * charWidth, y, charWidth, charHeight); + fillRect(g, x * charWidth, y, charWidth, charHeight); } } } if (row.getInvalidStrucPos() > x) { g.setColor(Color.orange); - g.fillRect(x * charWidth, y, charWidth, charHeight); + fillRect(g, x * charWidth, y, charWidth, charHeight); } else if (row.getInvalidStrucPos() == x) { g.setColor(Color.orange.darker()); - g.fillRect(x * charWidth, y, charWidth, charHeight); + fillRect(g, x * charWidth, y, charWidth, charHeight); } if (validCharWidth && validRes && displayChar != null && (displayChar.length() > 0)) { - Graphics2D gg = ((Graphics2D) g); + // Graphics2D gg = (g); float fmWidth = fm.charsWidth(displayChar.toCharArray(), 0, displayChar.length()); @@ -674,11 +705,11 @@ public class AnnotationRenderer if (row_annotations[column].colour == null) { - gg.setColor(Color.black); + g2d.setColor(Color.black); } else { - gg.setColor(row_annotations[column].colour); + g2d.setColor(row_annotations[column].colour); } /* @@ -691,19 +722,20 @@ public class AnnotationRenderer /* * translate to drawing position _before_ applying any scaling */ - gg.translate(xPos, yPos); + g2d.translate(xPos, yPos); if (scaledToFit) { /* * use a scaling transform to make the label narrower * (JalviewJS doesn't have Font.deriveFont(AffineTransform)) */ - gg.transform( + g2d.transform( AffineTransform.getScaleInstance(fmScaling, 1.0)); } + setAntialias(g); if (column == 0 || row.graph > 0) { - gg.drawString(displayChar, 0, 0); + g2d.drawString(displayChar, 0, 0); } else if (row_annotations[column - 1] == null || (labelAllCols || !displayChar.equals( @@ -711,7 +743,7 @@ public class AnnotationRenderer || (displayChar.length() < 2 && row_annotations[column].secondaryStructure == ' '))) { - gg.drawString(displayChar, 0, 0); + g2d.drawString(displayChar, 0, 0); } if (scaledToFit) { @@ -719,10 +751,10 @@ public class AnnotationRenderer * undo scaling before translating back * (restoring saved transform does NOT work in JS PDFGraphics!) */ - gg.transform(AffineTransform + g2d.transform(AffineTransform .getScaleInstance(1D / fmScaling, 1.0)); } - gg.translate(-xPos, -yPos); + g2d.translate(-xPos, -yPos); } } if (row.hasIcons) @@ -784,7 +816,8 @@ public class AnnotationRenderer { // int nb_annot = x - temp; - // jalview.bin.Console.outPrintln("\t type :"+lastSS+"\t x :"+x+"\t nbre + // Console.info("\t type :"+lastSS+"\t x + // :"+x+"\t nbre // annot :"+nb_annot); switch (lastSS) { @@ -878,10 +911,17 @@ public class AnnotationRenderer // temp = x; break; default: - g.setColor(Color.gray); - g.fillRect(lastSSX, y + 6 + iconOffset, - (x * charWidth) - lastSSX, 2); - // temp = x; + if (isVectorRendition()) + { + // draw single full width glyphline + drawGlyphLine(g, lastSSX, endRes - x, y, iconOffset); + // disable more glyph lines + this.glyphLineDrawn = true; + } + else + { + drawGlyphLine(g, lastSSX, x, y, iconOffset); + } break; } } @@ -1006,14 +1046,23 @@ public class AnnotationRenderer case 'y': case 'Z': case 'z': - // jalview.bin.Console.outPrintln(lastSS); + // Console.info(lastSS); Color nonCanColor = getNotCanonicalColor(lastSS); drawNotCanonicalAnnot(g, nonCanColor, row_annotations, lastSSX, x, y, iconOffset, startRes, column, validRes, validEnd); break; default: - drawGlyphLine(g, row_annotations, lastSSX, x, y, iconOffset, - startRes, column, validRes, validEnd); + if (isVectorRendition()) + { + // draw single full width glyphline + drawGlyphLine(g, lastSSX, endRes - x, y, iconOffset); + // disable more glyph lines + this.glyphLineDrawn = true; + } + else + { + drawGlyphLine(g, lastSSX, x, y, iconOffset); + } break; } } @@ -1091,7 +1140,7 @@ public class AnnotationRenderer } else { - jalview.bin.Console.errPrintln( + Console.warn( "rendered with " + renderer.getClass().toString()); } } @@ -1122,17 +1171,15 @@ public class AnnotationRenderer { if (clipst) { - jalview.bin.Console.errPrintln( - "Start clip at : " + yfrom + " (index " + f_i + ")"); + Console.warn("Start clip at : " + yfrom + " (index " + f_i + ")"); } if (clipend) { - jalview.bin.Console.errPrintln( - "End clip at : " + yto + " (index " + f_to + ")"); + Console.warn("End clip at : " + yto + " (index " + f_to + ")"); } } ; - jalview.bin.Console.errPrintln("Annotation Rendering time:" + Console.warn("Annotation Rendering time:" + (System.currentTimeMillis() - stime)); } ; @@ -1150,10 +1197,15 @@ public class AnnotationRenderer // private Color sdNOTCANONICAL_COLOUR; - void drawGlyphLine(Graphics g, Annotation[] row, int lastSSX, int x, - int y, int iconOffset, int startRes, int column, boolean validRes, - boolean validEnd) + void drawGlyphLine(Graphics g, int lastSSX, int x, int y, int iconOffset) { + if (glyphLineDrawn) + { + // if we've drawn a single long glyphline for an export, don't draw the + // bits + return; + } + unsetAntialias(g); g.setColor(GLYPHLINE_COLOR); g.fillRect(lastSSX, y + 6 + iconOffset, (x * charWidth) - lastSSX, 2); } @@ -1163,45 +1215,52 @@ public class AnnotationRenderer int lastSSX, int x, int y, int iconOffset, int startRes, int column, boolean validRes, boolean validEnd) { - g.setColor(SHEET_COLOUR); - if (!validEnd || !validRes || row == null || row[column] == null || row[column].secondaryStructure != 'E') { - g.fillRect(lastSSX, y + 4 + iconOffset, (x * charWidth) - lastSSX - 4, - 7); - g.fillPolygon( + // draw the glyphline underneath + drawGlyphLine(g, lastSSX, x, y, iconOffset); + + g.setColor(SHEET_COLOUR); + fillRect(g, lastSSX, y + 4 + iconOffset, + (x * charWidth) - lastSSX - 4, 6); + fillPolygon(g, new int[] - { (x * charWidth) - 4, (x * charWidth) - 4, (x * charWidth) }, + { (x * charWidth) - 6, (x * charWidth) - 6, + (x * charWidth - 1) }, new int[] - { y + iconOffset, y + 14 + iconOffset, y + 7 + iconOffset }, + { y + iconOffset + 1, y + 13 + iconOffset, + y + 7 + iconOffset }, 3); } else { - g.fillRect(lastSSX, y + 4 + iconOffset, (x + 1) * charWidth - lastSSX, - 7); + g.setColor(SHEET_COLOUR); + fillRect(g, lastSSX, y + 4 + iconOffset, (x * charWidth) - lastSSX, + 6); } - } void drawHelixAnnot(Graphics g, Annotation[] row, int lastSSX, int x, int y, int iconOffset, int startRes, int column, boolean validRes, boolean validEnd) { - g.setColor(HELIX_COLOUR); - int sCol = (lastSSX / charWidth) + hiddenColumns.visibleToAbsoluteColumn(startRes); int x1 = lastSSX; int x2 = (x * charWidth); - if (USE_FILL_ROUND_RECT) + if (USE_FILL_ROUND_RECT || isVectorRendition()) { + // draw glyph line behind helix (visible in EPS or SVG output) + drawGlyphLine(g, lastSSX, x, y, iconOffset); + + g.setColor(HELIX_COLOUR); + setAntialias(g); int ofs = charWidth / 2; // Off by 1 offset when drawing rects and ovals // to offscreen image on the MAC - g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1, 8, 8, 8); + fillRoundRect(g, lastSSX, y + 3 + iconOffset, x2 - x1 - 1, 8, 8, 8); if (sCol == 0 || row[sCol - 1] == null || row[sCol - 1].secondaryStructure != 'H') { @@ -1209,8 +1268,8 @@ public class AnnotationRenderer else { // g.setColor(Color.orange); - g.fillRoundRect(lastSSX, y + 4 + iconOffset, x2 - x1 - ofs + 1, 8, - 0, 0); + fillRoundRect(g, lastSSX, y + 3 + iconOffset, x2 - x1 - ofs, 8, 0, + 0); } if (!validRes || row[column] == null || row[column].secondaryStructure != 'H') @@ -1220,30 +1279,38 @@ public class AnnotationRenderer else { // g.setColor(Color.magenta); - g.fillRoundRect(lastSSX + ofs, y + 4 + iconOffset, - x2 - x1 - ofs + 1, 8, 0, 0); - + fillRoundRect(g, lastSSX + ofs, y + 3 + iconOffset, x2 - x1 - ofs, + 8, 0, 0); } return; } - if (sCol == 0 || row[sCol - 1] == null - || row[sCol - 1].secondaryStructure != 'H') + boolean leftEnd = sCol == 0 || row[sCol - 1] == null + || row[sCol - 1].secondaryStructure != 'H'; + boolean rightEnd = !validRes || row[column] == null + || row[column].secondaryStructure != 'H'; + + if (leftEnd || rightEnd) + { + drawGlyphLine(g, lastSSX, x, y, iconOffset); + } + g.setColor(HELIX_COLOUR); + + if (leftEnd) { - g.fillArc(lastSSX, y + 4 + iconOffset, charWidth, 8, 90, 180); + fillArc(g, lastSSX, y + 3 + iconOffset, charWidth, 8, 90, 180); x1 += charWidth / 2; } - if (!validRes || row[column] == null - || row[column].secondaryStructure != 'H') + if (rightEnd) { - g.fillArc((x * charWidth) - charWidth, y + 4 + iconOffset, charWidth, - 8, 270, 180); + fillArc(g, ((x - 1) * charWidth), y + 3 + iconOffset, charWidth, 8, + 270, 180); x2 -= charWidth / 2; } - g.fillRect(x1, y + 4 + iconOffset, x2 - x1, 8); + fillRect(g, x1, y + 3 + iconOffset, x2 - x1, 8); } void drawLineGraph(Graphics g, AlignmentAnnotation _aa, @@ -1254,6 +1321,13 @@ public class AnnotationRenderer { return; } + Stroke roundStroke = new BasicStroke(1, BasicStroke.CAP_ROUND, + BasicStroke.JOIN_ROUND); + Stroke squareStroke = new BasicStroke(1, BasicStroke.CAP_SQUARE, + BasicStroke.JOIN_MITER); + Graphics2D g2d = (Graphics2D) g; + Stroke prevStroke = g2d.getStroke(); + g2d.setStroke(roundStroke); int x = 0; @@ -1280,7 +1354,8 @@ public class AnnotationRenderer } g.setColor(Color.gray); - g.drawLine(x - charWidth, y2, (eRes - sRes + 1) * charWidth, y2); + drawLine(g, squareStroke, x * charWidth - charWidth, y2, + (eRes - sRes) * charWidth, y2); eRes = Math.min(eRes, aa_annotations.length); @@ -1322,7 +1397,7 @@ public class AnnotationRenderer // standalone value y1 = y - (int) (((aa_annotations[column].value - min) / range) * graphHeight); - g.drawLine(x * charWidth + charWidth / 4, y1, + drawLine(g, x * charWidth + charWidth / 4, y1, x * charWidth + 3 * charWidth / 4, y1); x++; continue; @@ -1339,7 +1414,7 @@ public class AnnotationRenderer y2 = y - (int) (((aa_annotations[column].value - min) / range) * graphHeight); - g.drawLine(x * charWidth - charWidth / 2, y1, + drawLine(g, (x - 1) * charWidth + charWidth / 2, y1, x * charWidth + charWidth / 2, y2); x++; } @@ -1348,14 +1423,14 @@ public class AnnotationRenderer { g.setColor(_aa.threshold.colour); Graphics2D g2 = (Graphics2D) g; - g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, + Stroke s = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 3f, new float[] - { 5f, 3f }, 0f)); + { 5f, 3f }, 0f); y2 = (int) (y - ((_aa.threshold.value - min) / range) * graphHeight); - g.drawLine(0, y2, (eRes - sRes) * charWidth, y2); - g2.setStroke(new BasicStroke()); + drawLine(g, s, 0, y2, (eRes - sRes) * charWidth, y2); } + g2d.setStroke(prevStroke); } @SuppressWarnings("unused") @@ -1382,7 +1457,7 @@ public class AnnotationRenderer g.setColor(Color.gray); - g.drawLine(x, y2, (eRes - sRes) * charWidth, y2); + drawLine(g, x, y2, (eRes - sRes) * charWidth, y2); int column; int aaMax = aa_annotations.length - 1; @@ -1420,11 +1495,11 @@ public class AnnotationRenderer { if (y1 - y2 > 0) { - g.fillRect(x * charWidth, y2, charWidth, y1 - y2); + fillRect(g, x * charWidth, y2, charWidth, y1 - y2); } else { - g.fillRect(x * charWidth, y1, charWidth, y2 - y1); + fillRect(g, x * charWidth, y1, charWidth, y2 - y1); } } // draw profile if available @@ -1455,7 +1530,8 @@ public class AnnotationRenderer // lm is not necessary - we can just use fm - could be off by no more // than 0.5 px // LineMetrics lm = g.getFontMetrics(ofont).getLineMetrics("Q", g); - // jalview.bin.Console.outPrintln(asc + " " + dec + " " + (asc - lm.getAscent()) + // Console.info(asc + " " + dec + " " + (asc - + // lm.getAscent()) // + " " + (dec - lm.getDescent())); double asc = fm.getAscent(); @@ -1580,15 +1656,13 @@ public class AnnotationRenderer if (_aa.threshold != null) { g.setColor(_aa.threshold.colour); - Graphics2D g2 = (Graphics2D) g; - g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, + Stroke s = new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND, 3f, new float[] - { 5f, 3f }, 0f)); + { 5f, 3f }, 0f); y2 = (int) (y - ((_aa.threshold.value - min) / range) * _aa.graphHeight); - g.drawLine(0, y2, (eRes - sRes) * charWidth, y2); - g2.setStroke(new BasicStroke()); + drawLine(g, s, 0, y2, (eRes - sRes) * charWidth, y2); } } @@ -1598,7 +1672,7 @@ public class AnnotationRenderer { eRes = Math.min(eRes, aa_annotations.length); g.setColor(Color.white); - g.fillRect(0, 0, width, y); + fillRect(g, 0, 0, width, y); g.setColor(new Color(0, 0, 180)); int x = 0, height; @@ -1622,7 +1696,7 @@ public class AnnotationRenderer height = y; } - g.fillRect(x, y - height, charWidth, height); + fillRect(g, x, y - height, charWidth, height); } x += charWidth; } @@ -1749,9 +1823,92 @@ public class AnnotationRenderer return new Color(0, 80, 255); default: - jalview.bin.Console.outPrintln("This is not a interaction : " + lastss); + Console.info("This is not a interaction : " + lastss); return null; } } + + private void fillPolygon(Graphics g, int[] xpoints, int[] ypoints, int n) + { + setAntialias(g); + g.fillPolygon(xpoints, ypoints, n); + } + + /* + private void fillRect(Graphics g, int a, int b, int c, int d) + { + fillRect(g, false, a, b, c, d); + }*/ + + private void fillRect(Graphics g, int a, int b, int c, int d) + { + unsetAntialias(g); + g.fillRect(a, b, c, d); + } + + private void fillRoundRect(Graphics g, int a, int b, int c, int d, int e, + int f) + { + setAntialias(g); + g.fillRoundRect(a, b, c, d, e, f); + } + + private void fillArc(Graphics g, int a, int b, int c, int d, int e, int f) + { + setAntialias(g); + g.fillArc(a, b, c, d, e, f); + } + + private void drawLine(Graphics g, Stroke s, int a, int b, int c, int d) + { + Graphics2D g2d = (Graphics2D) g; + Stroke p = g2d.getStroke(); + g2d.setStroke(s); + drawLine(g, a, b, c, d); + g2d.setStroke(p); + } + + private void drawLine(Graphics g, int a, int b, int c, int d) + { + setAntialias(g); + g.drawLine(a, b, c, d); + } + + private void setAntialias(Graphics g) + { + if (isVectorRendition()) + { + // no need to antialias vector drawings + return; + } + if (Cache.getDefault("ANTI_ALIAS", true)) + { + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } + } + + private void unsetAntialias(Graphics g) + { + if (isVectorRendition()) + { + // no need to antialias vector drawings + return; + } + Graphics2D g2d = (Graphics2D) g; + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_OFF); + } + + public void setVectorRendition(boolean b) + { + vectorRendition = b; + } + + public boolean isVectorRendition() + { + return vectorRendition; + } } diff --git a/src/jalview/util/StringUtils.java b/src/jalview/util/StringUtils.java index 11a1088..89bc36d 100644 --- a/src/jalview/util/StringUtils.java +++ b/src/jalview/util/StringUtils.java @@ -586,6 +586,15 @@ public class StringUtils return min < text.length() + 1 ? min : -1; } + public static boolean equalsIgnoreCase(String s1, String s2) + { + if (s1 == null || s2 == null) + { + return s1 == s2; + } + return s1.toLowerCase(Locale.ROOT).equals(s2.toLowerCase(Locale.ROOT)); + } + public static int indexOfFirstWhitespace(String text) { int index = -1; diff --git a/src/jalview/viewmodel/styles/ViewStyle.java b/src/jalview/viewmodel/styles/ViewStyle.java index 91f2f0c..715645a 100644 --- a/src/jalview/viewmodel/styles/ViewStyle.java +++ b/src/jalview/viewmodel/styles/ViewStyle.java @@ -20,10 +20,10 @@ */ package jalview.viewmodel.styles; -import jalview.api.ViewStyleI; - import java.awt.Color; +import jalview.api.ViewStyleI; + /** * A container for holding alignment view properties. View properties are * data-independent, which means they can be safely copied between views diff --git a/test/files/annotation_label_width/sample.a2m b/test/files/annotation_label_width/sample.a2m new file mode 100644 index 0000000..0ca6801 --- /dev/null +++ b/test/files/annotation_label_width/sample.a2m @@ -0,0 +1,80 @@ +>101 +P.I...A..Q..I.....H.....I........L.......E........G.......R.......S....D.......E.......Q.....K....E. +T..LI....RE...V.S.E...A...I......S.......R...S.......L........D....A.....P......L................... +..........T......S.......V.......R......V...I....I.......T......E.....M........A....K.........G..... +.H..........F..........G........I..........G........G......E........L....A...SK +>UPI +P.Hye.V..S..V.....T.....M........P.......T........G.......Wl......N....T.......V.......R.....K....Q. +G..MI....DA...V.T.R...A...L......L.......E...A.......I........A....T.....P......F................... +..........D......Essrfr..V.......R......C...L....I.......P......E.....I........P....D.........G..... +.N..........W..........G........S..........G........Gya....L........P....L...S- +>SRR +P.H...V..A..V.....K.....L........Y.......P........G.......R.......T....E.......Q.......Q.....K....E. +Q..LA....RA...I.A.D...D...V......M.......R...I.......L........G....S.....S......E................... +..........A......S.......V.......S......V...S....I.......E......E.....V........D....A.........A..... +.D..........W..........A........EkvyrplivegG........G......T........L....Y...KK +>SRR +P.H...V..I..V.....K.....L........W.......P........G.......R.......S....E.......P.......Q.....K....Q. +K..LV....ES...V.T.K...A...V......T.......T...S.......L........G....Y.....S......D................... +..........E......A.......V.......S......V...S....L.......Q......E.....V........P....S.........D..... +.Q..........WtekvyrpdilG........T..........A........G......R........L....Y...KK +>MGY +P.I...V..R..I.....T.....M........F.......E........G.......R.......T....K.......E.......Q.....K....Q. +E..LA....RV...I.T.E...A...V......V.......N...I.......A........K....T.....T......P................... +..........D......A.......T.......E......VkdqI....L.......Q......K.....VllvrslrlP....PppasrrqvsG..... +.A..........W..........S........A..........D........G......K........P....T...SE +>446 +P.H...V..I..V.....K.....L........W.......P........G.......K.......S....E.......R.......E.....E....T. +Q..LA....EA...I.T.K...S...V......T.......E...T.......L........N....F.....G......P................... +..........E......S.......V.......S......V...A....F.......E......E.....I........P....A.........K..... +.D..........W..........AskvyhadiI..........Gne......G......K........L....Y...KK +>SRR +P.L...V..R..I.....T.....Y........P.......R........Ga......L.......S....P.......E.......H.....K....T. +R..IA....RA...L.T.E...I...V......L.......D...Vevdaa..T........D....A.....G......R................... +..........M......V.......T.......V......V...H....F.......N......E.....A........A....P.........D..... +.D..........W..........A........V..........G........G......Eirs.....T....A...AE +>SRR +P.L...V..R..I.....T.....Y........P.......R........Ga......L.......S....P.......D.......H.....K....R. +R..IA....RE...L.T.E...I...V......L.......D...Vevdaa..T........D....A.....G......R................... +..........M......V.......T.......V......I...H....F.......N......E.....A........A....A.........D..... +.D..........W..........A........V..........G........G......Eirs.....T....A...AE +>SRR +P.R...Y..R..Vip...T.....V........P.......E........G.......Qy......S....N.......E.......S.....R....K. +A..LV....KD...V.T.E...A...V......V.......R...A.......D........G....G.....K......Y................... +..........E......Dvapr...V.......W......V...F....P.......T......E.....I........P....D.........G..... +.Q..........W..........G........S..........R........Gvi....R........P....L...PE +>SRR +P.R...Y..R..Iip...T.....V........P.......E........G.......Qy......S....N.......E.......S.....R....K. +A..LV....KD...V.T.E...A...V......V.......R...A.......D........G....G.....K......Y................... +..........E......Dvapr...V.......W......V...F....P.......T......E.....I........P....D.........G..... +.Q..........W..........G........S..........R........Gvi....R........P....L...PE +>SRR +P.V...I..E..M.....F.....V........P.......E........G.......La......D....A.......E.......A.....K....R. +A..LH....DR...V.S.R...Q...V......L.......E...V.......E........G....AtydesP......L................... +..........A......Qsi.....T.......W......M...L....I.......Q......E.....V........L....E.........C..... +.G..........W..........S........V..........G........S......K........Avw..A...SE +>SRR +P.I...I..E..M.....H.....V........Q.......E........Gv......L.......D....E.......E.......T.....K....R. +T..LH....ER...V.G.R...Q...V......L.......E...I.......E........G....Any...D......E................... +..........N......D.......Varllt..F......M...F....I.......R......E.....H........P....E.........G..... +.G..........F..........S........I..........G........G......E........M....It..SE +>SRR +P.Lyr.V..D..V.....T.....V........P.......E........Gsmihg..Q.......G....Pwal....S.......R.....R....R. +A..IV....RE...V.T.E...I...V......L.......E...A.......E........G....S.....D......P................... +..........Slgeaw.R.......V.......W......V...V....L.......R......E.....V........G....D.........A..... +.F..........W..........G........A..........A........G......E........L....-...-- +>SRR +P.Lyr.V..Q..I.....T.....V........P.......E........Gsmlhg..Q.......G....Pwai....E.......R.....R....R. +E..LV....RA...V.S.K...A...V......L.......D...A.......E........G....Teyn..P......A................... +..........Saw....R.......V.......W......V...L....M.......S......E.....I........S....E.........T..... +.H..........W..........G........A..........A........G......E........-....-...-- +>SRR +P.L...V..E..M.....S.....F........P.......V........Gv......L.......T....L.......D.......Q.....K....A. +A..MI....KS...V.T.D...V...V......R.......G...A.......M........K....L.....P......P................... +..........Dpar...K.......L.......F......V...E....I.......F......E.....T........P....G.........G..... +.G..........F..........G........Vtakvvvvp..G........Gky....R........P....A...P- +>SRR +P.L...V..E..I.....D.....L........L.......E........A.......W.......A....P.......D.......Q.....I....D. +A..IA....DA...I.H.E...A...M......V.......E...T.......L........G....V.....P......Eraagrdsatkqhfysrfaa +llaeratvqsA......D.......L.......T......A...V....L.......V......E.....N........S....R.........D..... +.D..........W..........S........F..........Gm.......G......Q........-....-...-- diff --git a/test/files/annotation_label_width/test_fab41_nostructureviewers.txt b/test/files/annotation_label_width/test_fab41_nostructureviewers.txt new file mode 100644 index 0000000..b40c4e2 --- /dev/null +++ b/test/files/annotation_label_width/test_fab41_nostructureviewers.txt @@ -0,0 +1,15 @@ +--nonews +--nosplash +--open=./test/files/annotation_label_width/sample.a2m +--colour=gecos:flower +--gui +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb +--paematrix=./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3_scores.json +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_2_model_4.pdb +--paematrix=./examples/test_fab41.result/test_fab41_unrelaxed_rank_2_model_4_scores.json +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_3_model_2.pdb +--paematrix=./examples/test_fab41.result/test_fab41_unrelaxed_rank_3_model_2_scores.json +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_4_model_5.pdb +--paematrix=./examples/test_fab41.result/test_fab41_unrelaxed_rank_4_model_5_scores.json +--structure=[structureviewer=none]./examples/test_fab41.result/test_fab41_unrelaxed_rank_5_model_1.pdb +--paematrix=./examples/test_fab41.result/test_fab41_unrelaxed_rank_5_model_1_scores.json diff --git a/test/jalview/gui/AnnotationLabelsTest2.java b/test/jalview/gui/AnnotationLabelsTest2.java new file mode 100644 index 0000000..ad97e8b --- /dev/null +++ b/test/jalview/gui/AnnotationLabelsTest2.java @@ -0,0 +1,253 @@ +/* + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors + * + * This file is part of Jalview. + * + * Jalview is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * Jalview is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. + */ +package jalview.gui; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertFalse; +import static org.testng.Assert.assertTrue; + +import java.io.File; + +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import jalview.bin.Cache; +import jalview.bin.Jalview; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.SequenceI; +import jalview.gui.StructureViewer.ViewerType; +import jalview.io.DataSourceType; +import jalview.io.FileLoader; +import jalview.structure.StructureImportSettings.TFType; + +public class AnnotationLabelsTest2 +{ + @BeforeClass(alwaysRun = true) + public static void setUpBeforeClass() throws Exception + { + if (Desktop.instance != null) + Desktop.instance.closeAll_actionPerformed(null); + + setUpJvOptionPane(); + /* + * use read-only test properties file + */ + Cache.loadProperties("test/jalview/io/testProps.jvprops"); + Jalview.main(new String[] { "--nonews", "--nosplash", }); + } + + @AfterMethod(alwaysRun = true) + public void tearDown() + { + if (Desktop.instance != null) + Desktop.instance.closeAll_actionPerformed(null); + } + + /** + * configure (read-only) properties for test to ensure Consensus is computed + * for colour Above PID testing + */ + @BeforeMethod(alwaysRun = true) + public void setUp() + { + Cache.loadProperties("test/jalview/io/testProps.jvprops"); + Cache.applicationProperties.setProperty("SHOW_IDENTITY", + Boolean.TRUE.toString()); + + } + + public static void setUpJvOptionPane() + { + JvOptionPane.setInteractiveMode(false); + JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION); + } + + @Test( + groups = + { "Functional", "testTask1" }, + dataProvider = "openFilesWithIdWidthChanges") + public void testIdWidthChanges(String alignmentFilename, boolean wrap, + int idWidth1min, int idWidth1max, int manualWidth, + String structureFilename, String paeFilename, + boolean secondaryStructureView, TFType temperatureFactorType, + ViewerType viewerType, int idWidth2min, int idWidth2max) + { + AlignFrame af = new FileLoader() + .LoadFileWaitTillLoaded(alignmentFilename, DataSourceType.FILE); + try + { + Thread.sleep(200); // to allow alignment annotations to open + } catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + AlignViewport av = af.getCurrentView(); + + int idWidth = 0; + + idWidth = av.getIdWidth(); + assertTrue(idWidth > idWidth1min, + "idWidth (" + idWidth + ") is not greater than " + idWidth1min); + assertTrue(idWidth < idWidth1max, + "idWidth (" + idWidth + ") is not narrower than" + idWidth1max); + + // set wrap + if (wrap) + { + af.setWrapFormat(true, false); + idWidth = av.getIdWidth(); + assertTrue(idWidth > idWidth1min, "After wrap idWidth (" + idWidth + + ") is not greater than " + idWidth1min); + assertTrue(idWidth < idWidth1max, "After wrap idWidth (" + idWidth + + ") is not narrower than" + idWidth1max); + } + + AlignmentI al = av.getAlignment(); + SequenceI s = al.getSequenceAt(0); + AlignmentPanel ap = af.alignPanel; + + File structureFile = new File(structureFilename); + File paeFile = new File(paeFilename); + + StructureViewer sv = StructureChooser.openStructureFileForSequence(null, + null, ap, s, false, structureFile.getAbsolutePath(), + temperatureFactorType, paeFile.getAbsolutePath(), true, + secondaryStructureView, false, viewerType); + // give time for annotations to open + try + { + Thread.sleep(200); // to allow alignment annotations to open + } catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + // idWidth = ap.getIdPanel().getWidth(); + idWidth = av.getIdWidth(); + assertTrue(idWidth > idWidth2min, + "idWidth (" + idWidth + ") is not greater than " + idWidth2min); + assertTrue(idWidth < idWidth2max, + "idWidth (" + idWidth + ") is not narrower than" + idWidth2max); + } + + @Test( + groups = + { "Functional", "testTask1" }, + dataProvider = "openFilesWithIdWidthChanges") + public void testIdWidthNoChanges(String alignmentFilename, boolean wrap, + int idWidth1min, int idWidth1max, int manualWidth, + String structureFilename, String paeFilename, + boolean secondaryStructureView, TFType temperatureFactorType, + ViewerType viewerType, int idWidth2min, int idWidth2max) + { + AlignFrame af = new FileLoader() + .LoadFileWaitTillLoaded(alignmentFilename, DataSourceType.FILE); + try + { + Thread.sleep(200); // to allow alignment annotations to open + } catch (InterruptedException e) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + AlignViewport av = af.getCurrentView(); + + int idWidth = 0; + + idWidth = av.getIdWidth(); + assertTrue(idWidth > idWidth1min, + "idWidth (" + idWidth + ") is not greater than " + idWidth1min); + assertTrue(idWidth < idWidth1max, + "idWidth (" + idWidth + ") is not narrower than" + idWidth1max); + + AlignmentI al = av.getAlignment(); + SequenceI s = al.getSequenceAt(0); + AlignmentPanel ap = af.alignPanel; + + // set width manually + av.setIdWidth(manualWidth); + ap.validateAnnotationDimensions(false); + ap.paintAlignment(true, false); + ap.getIdPanel().getIdCanvas().setManuallyAdjusted(true); + + idWidth = av.getIdWidth(); + assertEquals(idWidth, manualWidth, + "idWidth is not set to the manually set width " + manualWidth); + + File structureFile = new File(structureFilename); + File paeFile = new File(paeFilename); + + StructureViewer sv = StructureChooser.openStructureFileForSequence(null, + null, ap, s, false, structureFile.getAbsolutePath(), + temperatureFactorType, paeFile.getAbsolutePath(), false, + secondaryStructureView, false, viewerType); + + idWidth = ap.getIdPanel().getWidth();// av.getIdWidth(); + idWidth = av.getIdWidth(); + assertEquals(idWidth, manualWidth, + "idWidth is not set to the manually set width " + manualWidth + + " after adding structure annotations"); + assertFalse(idWidth > idWidth2min, + "idWidth (" + idWidth + ") is greater than " + idWidth2min); + } + + @DataProvider(name = "openFilesWithIdWidthChanges") + public Object[][] openFilesWithIdWidthChanges() + { + /* + String alignmentFilename, + boolean wrap, + int idWidth1min, + int idWidth1max, + int manualWidth, // ignored by testIdWidthChanges() + String structureFilename, + String paeFilename, + boolean secondaryStructureView, + TFType temperatureFactorType, + ViewerType viewerType, + int idWidth2min, + int idWidth2max, + */ + return new Object[][] { + // + /* + */ + { "./test/files/annotation_label_width/sample.a2m", false, 50, 70, + 100, + "./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb", + "./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3_scores.json", + true, TFType.PLDDT, null, 115, 130 }, + { "./test/files/annotation_label_width/sample.a2m", true, 50, 70, + 100, + "./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3.pdb", + "./examples/test_fab41.result/test_fab41_unrelaxed_rank_1_model_3_scores.json", + true, TFType.PLDDT, null, 115, 130 }, + /* + */ + }; + } + +}