X Tutup
Skip to content

Commit fa9ecab

Browse files
committed
Fix MultipleAlignmentJmol resizing bug
Add WrapLayout (fixes default FlowLayout wrapping behaviour) WrapLayout is public domain.
1 parent af526f1 commit fa9ecab

File tree

2 files changed

+221
-32
lines changed

2 files changed

+221
-32
lines changed

biojava-structure-gui/src/main/java/org/biojava/nbio/structure/align/gui/jmol/MultipleAlignmentJmol.java

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121
package org.biojava.nbio.structure.align.gui.jmol;
2222

23+
import java.awt.BorderLayout;
2324
import java.awt.Color;
2425
import java.awt.Container;
2526
import java.awt.Dimension;
@@ -40,6 +41,7 @@
4041
import javax.swing.JFrame;
4142
import javax.swing.JLabel;
4243
import javax.swing.JMenuBar;
44+
import javax.swing.JPanel;
4345
import javax.swing.JTextField;
4446

4547
import org.biojava.nbio.structure.Atom;
@@ -56,6 +58,7 @@
5658
import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentTools;
5759
import org.biojava.nbio.structure.align.multiple.util.MultipleAlignmentWriter;
5860
import org.biojava.nbio.structure.align.webstart.AligUIManager;
61+
import org.biojava.nbio.structure.gui.WrapLayout;
5962
import org.biojava.nbio.structure.jama.Matrix;
6063
import org.forester.archaeopteryx.Archaeopteryx;
6164
import org.forester.phylogeny.Phylogeny;
@@ -137,14 +140,13 @@ public void windowClosing(WindowEvent e) {
137140
});
138141

139142
Container contentPane = frame.getContentPane();
140-
Box vBox = Box.createVerticalBox();
141143

142144
jmolPanel.addMouseMotionListener(this);
143145
jmolPanel.addMouseListener(this);
144-
jmolPanel
145-
.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
146+
jmolPanel.setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
147+
contentPane.add(jmolPanel,BorderLayout.CENTER);
146148

147-
vBox.add(jmolPanel);
149+
Box vBox = Box.createVerticalBox();
148150

149151
// / USER SCRIPTING COMMAND
150152
JTextField field = new JTextField();
@@ -162,8 +164,10 @@ public void windowClosing(WindowEvent e) {
162164
// / STRUCTURE SELECTION
163165
if (multAln != null) {
164166

165-
Box hBox00 = Box.createHorizontalBox();
166-
hBox00.setMaximumSize(new Dimension(Short.MAX_VALUE, 30));
167+
JPanel modelSelection = new JPanel();
168+
modelSelection.setLayout(new WrapLayout(WrapLayout.LEFT));
169+
modelSelection.setSize(new Dimension(DEFAULT_WIDTH,30));
170+
vBox.add(modelSelection);
167171

168172
JButton show = new JButton("Show Only: ");
169173
show.addActionListener(new ActionListener() {
@@ -184,27 +188,16 @@ public void actionPerformed(ActionEvent e) {
184188
jmolPanel.executeCmd(cmd + " restore selection;");
185189
}
186190
});
187-
hBox00.add(show);
188-
hBox00.add(Box.createGlue());
189-
vBox.add(hBox00);
191+
modelSelection.add(show);
190192

191-
// A line of structures of maximum 5
192-
for (int line = 0; line < 1 + (multAln.size() / 5); line++) {
193-
Box hBox0 = Box.createHorizontalBox();
194-
hBox0.setMaximumSize(new Dimension(Short.MAX_VALUE, 30));
195-
196-
for (int str = line * 5; str < Math.min((line + 1) * 5,
197-
multAln.size()); str++) {
198-
JCheckBox structureSelection = new JCheckBox(multAln
199-
.getEnsemble().getStructureIdentifiers().get(str)
200-
.getIdentifier());
201-
hBox0.add(structureSelection);
202-
hBox0.add(Box.createGlue());
203-
structureSelection.setSelected(true);
204-
selectedStructures.add(structureSelection);
205-
}
206-
207-
vBox.add(hBox0);
193+
// Check boxes for all models
194+
for(int str = 0; str < multAln.size();str++) {
195+
JCheckBox structureSelection = new JCheckBox(multAln
196+
.getEnsemble().getStructureIdentifiers().get(str)
197+
.getIdentifier());
198+
modelSelection.add(structureSelection);
199+
structureSelection.setSelected(true);
200+
selectedStructures.add(structureSelection);
208201
}
209202
}
210203

@@ -214,35 +207,35 @@ public void actionPerformed(ActionEvent e) {
214207

215208
String[] styles = new String[] { "Cartoon", "Backbone", "CPK",
216209
"Ball and Stick", "Ligands", "Ligands and Pocket" };
217-
JComboBox style = new JComboBox(styles);
210+
JComboBox<String> style = new JComboBox<>(styles);
218211

219212
hBox1.setMaximumSize(new Dimension(Short.MAX_VALUE, 30));
220213

221214
hBox1.add(new JLabel("Style"));
222215
hBox1.add(style);
223216
vBox.add(hBox1);
224-
contentPane.add(vBox);
225217

226218
style.addActionListener(jmolPanel);
227219

228220
String[] colorModes = new String[] { "Secondary Structure", "By Chain",
229221
"Rainbow", "By Element", "By Amino Acid", "Hydrophobicity",
230222
"Suggest Domains", "Show SCOP Domains" };
231-
JComboBox jcolors = new JComboBox(colorModes);
223+
JComboBox<String> jcolors = new JComboBox<>(colorModes);
232224
jcolors.addActionListener(jmolPanel);
233225

234226
hBox1.add(Box.createGlue());
235227
hBox1.add(new JLabel("Color"));
236228
hBox1.add(jcolors);
237229

238230
String[] cPalette = { "Spectral", "Set1", "Set2", "Pastel" };
239-
JComboBox palette = new JComboBox(cPalette);
231+
JComboBox<String> palette = new JComboBox<>(cPalette);
240232

241233
palette.addActionListener(new ActionListener() {
242234
@Override
243235
public void actionPerformed(ActionEvent e) {
244236

245-
JComboBox source = (JComboBox) e.getSource();
237+
@SuppressWarnings("unchecked")
238+
JComboBox<String> source = (JComboBox<String>) e.getSource();
246239
String value = source.getSelectedItem().toString();
247240
evalString("save selection; select *; color grey; "
248241
+ "select ligand; color CPK;");
@@ -268,6 +261,7 @@ public void actionPerformed(ActionEvent e) {
268261

269262
// / CHECK BOXES
270263
Box hBox2 = Box.createHorizontalBox();
264+
hBox2.setMaximumSize(new Dimension(Short.MAX_VALUE, 30));
271265

272266
JButton resetDisplay = new JButton("Reset Display");
273267
resetDisplay.addActionListener(new ActionListener() {
@@ -337,7 +331,7 @@ public void itemStateChanged(ItemEvent e) {
337331

338332
vBox.add(hBox);
339333

340-
contentPane.add(vBox);
334+
contentPane.add(vBox,BorderLayout.SOUTH);
341335
MyJmolStatusListener li = (MyJmolStatusListener) jmolPanel
342336
.getStatusListener();
343337

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
package org.biojava.nbio.structure.gui;
2+
3+
import java.awt.*;
4+
import javax.swing.JScrollPane;
5+
import javax.swing.SwingUtilities;
6+
7+
/**
8+
* FlowLayout subclass that fully supports wrapping of components.
9+
*
10+
* Originally written by Rob Camick
11+
* https://tips4java.wordpress.com/2008/11/06/wrap-layout/
12+
*/
13+
public class WrapLayout extends FlowLayout
14+
{
15+
private Dimension preferredLayoutSize;
16+
17+
/**
18+
* Constructs a new <code>WrapLayout</code> with a left
19+
* alignment and a default 5-unit horizontal and vertical gap.
20+
*/
21+
public WrapLayout()
22+
{
23+
super();
24+
}
25+
26+
/**
27+
* Constructs a new <code>FlowLayout</code> with the specified
28+
* alignment and a default 5-unit horizontal and vertical gap.
29+
* The value of the alignment argument must be one of
30+
* <code>WrapLayout</code>, <code>WrapLayout</code>,
31+
* or <code>WrapLayout</code>.
32+
* @param align the alignment value
33+
*/
34+
public WrapLayout(int align)
35+
{
36+
super(align);
37+
}
38+
39+
/**
40+
* Creates a new flow layout manager with the indicated alignment
41+
* and the indicated horizontal and vertical gaps.
42+
* <p>
43+
* The value of the alignment argument must be one of
44+
* <code>WrapLayout</code>, <code>WrapLayout</code>,
45+
* or <code>WrapLayout</code>.
46+
* @param align the alignment value
47+
* @param hgap the horizontal gap between components
48+
* @param vgap the vertical gap between components
49+
*/
50+
public WrapLayout(int align, int hgap, int vgap)
51+
{
52+
super(align, hgap, vgap);
53+
}
54+
55+
/**
56+
* Returns the preferred dimensions for this layout given the
57+
* <i>visible</i> components in the specified target container.
58+
* @param target the component which needs to be laid out
59+
* @return the preferred dimensions to lay out the
60+
* subcomponents of the specified container
61+
*/
62+
@Override
63+
public Dimension preferredLayoutSize(Container target)
64+
{
65+
return layoutSize(target, true);
66+
}
67+
68+
/**
69+
* Returns the minimum dimensions needed to layout the <i>visible</i>
70+
* components contained in the specified target container.
71+
* @param target the component which needs to be laid out
72+
* @return the minimum dimensions to lay out the
73+
* subcomponents of the specified container
74+
*/
75+
@Override
76+
public Dimension minimumLayoutSize(Container target)
77+
{
78+
Dimension minimum = layoutSize(target, false);
79+
minimum.width -= (getHgap() + 1);
80+
return minimum;
81+
}
82+
83+
/**
84+
* Returns the minimum or preferred dimension needed to layout the target
85+
* container.
86+
*
87+
* @param target target to get layout size for
88+
* @param preferred should preferred size be calculated
89+
* @return the dimension to layout the target container
90+
*/
91+
private Dimension layoutSize(Container target, boolean preferred)
92+
{
93+
synchronized (target.getTreeLock())
94+
{
95+
// Each row must fit with the width allocated to the containter.
96+
// When the container width = 0, the preferred width of the container
97+
// has not yet been calculated so lets ask for the maximum.
98+
99+
int targetWidth = target.getSize().width;
100+
Container container = target;
101+
102+
while (container.getSize().width == 0 && container.getParent() != null)
103+
{
104+
container = container.getParent();
105+
}
106+
107+
targetWidth = container.getSize().width;
108+
109+
if (targetWidth == 0)
110+
targetWidth = Integer.MAX_VALUE;
111+
112+
int hgap = getHgap();
113+
int vgap = getVgap();
114+
Insets insets = target.getInsets();
115+
int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
116+
int maxWidth = targetWidth - horizontalInsetsAndGap;
117+
118+
// Fit components into the allowed width
119+
120+
Dimension dim = new Dimension(0, 0);
121+
int rowWidth = 0;
122+
int rowHeight = 0;
123+
124+
int nmembers = target.getComponentCount();
125+
126+
for (int i = 0; i < nmembers; i++)
127+
{
128+
Component m = target.getComponent(i);
129+
130+
if (m.isVisible())
131+
{
132+
Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
133+
134+
// Can't add the component to current row. Start a new row.
135+
136+
if (rowWidth + d.width > maxWidth)
137+
{
138+
addRow(dim, rowWidth, rowHeight);
139+
rowWidth = 0;
140+
rowHeight = 0;
141+
}
142+
143+
// Add a horizontal gap for all components after the first
144+
145+
if (rowWidth != 0)
146+
{
147+
rowWidth += hgap;
148+
}
149+
150+
rowWidth += d.width;
151+
rowHeight = Math.max(rowHeight, d.height);
152+
}
153+
}
154+
155+
addRow(dim, rowWidth, rowHeight);
156+
157+
dim.width += horizontalInsetsAndGap;
158+
dim.height += insets.top + insets.bottom + vgap * 2;
159+
160+
// When using a scroll pane or the DecoratedLookAndFeel we need to
161+
// make sure the preferred size is less than the size of the
162+
// target containter so shrinking the container size works
163+
// correctly. Removing the horizontal gap is an easy way to do this.
164+
165+
Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
166+
167+
if (scrollPane != null && target.isValid())
168+
{
169+
dim.width -= (hgap + 1);
170+
}
171+
172+
return dim;
173+
}
174+
}
175+
176+
/*
177+
* A new row has been completed. Use the dimensions of this row
178+
* to update the preferred size for the container.
179+
*
180+
* @param dim update the width and height when appropriate
181+
* @param rowWidth the width of the row to add
182+
* @param rowHeight the height of the row to add
183+
*/
184+
private void addRow(Dimension dim, int rowWidth, int rowHeight)
185+
{
186+
dim.width = Math.max(dim.width, rowWidth);
187+
188+
if (dim.height > 0)
189+
{
190+
dim.height += getVgap();
191+
}
192+
193+
dim.height += rowHeight;
194+
}
195+
}

0 commit comments

Comments
 (0)
X Tutup