diff --git a/src/main/java/bdv/ij/BigDataBrowserPlugIn.java b/src/main/java/bdv/ij/BigDataBrowserPlugIn.java index 96e072a..36069ee 100644 --- a/src/main/java/bdv/ij/BigDataBrowserPlugIn.java +++ b/src/main/java/bdv/ij/BigDataBrowserPlugIn.java @@ -1,5 +1,6 @@ package bdv.ij; +import bdv.ij.util.SpringUtilities; import ij.IJ; import ij.ImageJ; import ij.plugin.PlugIn; @@ -7,25 +8,52 @@ import java.awt.Component; import java.awt.Dimension; import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.net.Authenticator; +import java.net.HttpURLConnection; +import java.net.PasswordAuthentication; import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import javax.imageio.ImageIO; +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.HttpsURLConnection; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSession; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; +import javax.swing.BorderFactory; +import javax.swing.ButtonGroup; import javax.swing.DefaultListCellRenderer; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.SpringLayout; import mpicbg.spim.data.SpimDataException; @@ -47,6 +75,8 @@ public class BigDataBrowserPlugIn implements PlugIn public static String serverUrl = "http://"; + public static String domain = "/public"; + @Override public void run( final String arg ) { @@ -60,33 +90,259 @@ public void run( final String arg ) e.printStackTrace(); } - final Object remoteUrl = JOptionPane.showInputDialog( null, "Enter BigDataServer Remote URL:", "BigDataServer", - JOptionPane.QUESTION_MESSAGE, new ImageIcon( image ), null, serverUrl ); + if ( null == arg || arg == "" ) + { + final JRadioButton publicButton = new JRadioButton( "Public" ); + publicButton.setActionCommand( "Public" ); + + final JRadioButton privateButton = new JRadioButton( "Private" ); + privateButton.setActionCommand( "Private" ); + + ButtonGroup group = new ButtonGroup(); + group.add( publicButton ); + group.add( privateButton ); + + final JTextField id = new JTextField(); + final JPasswordField password = new JPasswordField(); + + final JPanel userPanel = new JPanel( new SpringLayout() ); + userPanel.setBorder( BorderFactory.createTitledBorder( "" ) ); + + userPanel.add( new JLabel( "Enter ID" ) ); + userPanel.add( id ); + userPanel.add( new JLabel( "Enter Password" ) ); + userPanel.add( password ); + + SpringUtilities.makeCompactGrid( userPanel, + 2, 2, //rows, cols + 6, 6, //initX, initY + 6, 6 ); //xPad, yPad + + for ( Component c : userPanel.getComponents() ) + { + c.setEnabled( false ); + } + + Object[] inputFields = { + publicButton, + privateButton, + userPanel, + "Remote URL" }; + + publicButton.addActionListener( new ActionListener() + { + @Override public void actionPerformed( ActionEvent e ) + { + for ( Component c : userPanel.getComponents() ) + { + c.setEnabled( !publicButton.isSelected() ); + } + } + } ); + + privateButton.addActionListener( new ActionListener() + { + @Override public void actionPerformed( ActionEvent e ) + { + for ( Component c : userPanel.getComponents() ) + { + c.setEnabled( privateButton.isSelected() ); + } + } + } ); - if ( remoteUrl == null ) - return; + publicButton.setSelected( true ); - serverUrl = remoteUrl.toString(); + final Object remoteUrl = JOptionPane.showInputDialog( null, inputFields, "BigDataServer", + JOptionPane.QUESTION_MESSAGE, new ImageIcon( image ), null, serverUrl ); - final ArrayList< String > nameList = new ArrayList< String >(); + if ( remoteUrl == null ) + return; + + serverUrl = remoteUrl.toString(); + + if ( privateButton.isSelected() ) + { + Authenticator.setDefault( new Authenticator() + { + protected PasswordAuthentication getPasswordAuthentication() + { + return new PasswordAuthentication( id.getText(), password.getPassword() ); + } + } ); + domain = "/private"; + } + else + { + domain = "/public"; + } + } + else + { + String line = null; + FileReader fileReader = null; + + try + { + fileReader = new FileReader( new File( arg ) ); + } + catch ( FileNotFoundException e ) + { + e.printStackTrace(); + } + + BufferedReader br = new BufferedReader( fileReader ); + + try + { + if ( ( line = br.readLine() ) != null ) + { + serverUrl = line; + } + } + catch ( IOException e ) + { + e.printStackTrace(); + } + + if ( line == null ) + return; + } + + final ArrayList< Object > nameList = new ArrayList< Object >(); + + String tag = ""; try { - getDatasetList( serverUrl, nameList ); + // TODO: search datasets by tag name should be provided soon + getDatasetList( serverUrl, nameList, tag ); } catch ( final IOException e ) { IJ.showMessage( "Error connecting to server at " + serverUrl ); e.printStackTrace(); } - createDatasetListUI( serverUrl, nameList.toArray() ); + catch ( NoSuchAlgorithmException e ) + { + e.printStackTrace(); + } + catch ( KeyManagementException e ) + { + e.printStackTrace(); + } + createDatasetListUI( tag, serverUrl, nameList.toArray() ); + } + + class DataSet + { + private final String index; + private final String name; + private final String description; + private final String tags; + private final String sharedBy; + private final Boolean isPublic; + + DataSet( String index, String name, String description, String tags, String sharedBy, Boolean isPublic ) + { + this.index = index; + this.name = name; + this.description = description; + this.tags = tags; + this.sharedBy = sharedBy; + this.isPublic = isPublic; + } + + public String getIndex() + { + return index; + } + + public String getName() + { + return name; + } + + public String getDescription() + { + return description; + } + + public String getTags() + { + return tags; + } + + public String getSharedBy() + { + return sharedBy; + } + + public Boolean isPublic() + { + return isPublic; + } } - private boolean getDatasetList( final String remoteUrl, final ArrayList< String > nameList ) throws IOException + class Category { - // Get JSON string from the server - final URL url = new URL( remoteUrl + "/json/" ); + private final String name; + + Category( String name ) + { + this.name = name; + } + + public String getName() + { + return name; + } + } + + private boolean getDatasetList( final String remoteUrl, final ArrayList< Object > nameList, final String searchTag ) throws IOException, NoSuchAlgorithmException, KeyManagementException + { + TrustManager[] trustAllCerts = new TrustManager[] { + new X509TrustManager() + { + public java.security.cert.X509Certificate[] getAcceptedIssuers() + { + return null; + } + + public void checkClientTrusted( X509Certificate[] certs, String authType ) + { + } + + public void checkServerTrusted( X509Certificate[] certs, String authType ) + { + } - final InputStream is = url.openStream(); + } + }; + + SSLContext sc = SSLContext.getInstance( "SSL" ); + sc.init( null, trustAllCerts, new java.security.SecureRandom() ); + HttpsURLConnection.setDefaultSSLSocketFactory( sc.getSocketFactory() ); + + // Create all-trusting host name verifier + HostnameVerifier allHostsValid = new HostnameVerifier() + { + public boolean verify( String hostname, SSLSession session ) + { + return true; + } + }; + // Install the all-trusting host verifier + HttpsURLConnection.setDefaultHostnameVerifier( allHostsValid ); + + String urlString = resolveRedirectedURL( remoteUrl + domain + "/tag/?name=" + URLEncoder.encode( searchTag, "UTF-8" ) ); + + final URL url = new URL( urlString ); + + URLConnection conn = url.openConnection(); + + System.out.println( url ); + + final InputStream is = conn.getInputStream(); final JsonReader reader = new JsonReader( new InputStreamReader( is, "UTF-8" ) ); reader.beginObject(); @@ -98,25 +354,35 @@ private boolean getDatasetList( final String remoteUrl, final ArrayList< String reader.beginObject(); - String id = null, description = null, thumbnailUrl = null, datasetUrl = null; + String id = null, datasetName = null, tags = null, description = null, thumbnailUrl = null, datasetUrl = null, sharedBy = null; + Boolean isPublic = false; + while ( reader.hasNext() ) { final String name = reader.nextName(); - if ( name.equals( "id" ) ) - id = reader.nextString(); + if ( name.equals( "name" ) ) + datasetName = reader.nextString(); else if ( name.equals( "description" ) ) description = reader.nextString(); else if ( name.equals( "thumbnailUrl" ) ) thumbnailUrl = reader.nextString(); else if ( name.equals( "datasetUrl" ) ) datasetUrl = reader.nextString(); + else if ( name.equals( "tags" ) ) + tags = reader.nextString(); + else if ( name.equals( "index" ) ) + id = reader.nextString(); + else if ( name.equals( "sharedBy" ) ) + sharedBy = reader.nextString(); + else if ( name.equals( "isPublic" ) ) + isPublic = reader.nextBoolean(); else reader.skipValue(); } if ( id != null ) { - nameList.add( id ); + nameList.add( new DataSet( id, datasetName, description, tags, sharedBy, isPublic ) ); if ( thumbnailUrl != null && StringUtils.isNotEmpty( thumbnailUrl ) ) imageMap.put( id, new ImageIcon( new URL( thumbnailUrl ) ) ); if ( datasetUrl != null ) @@ -133,7 +399,57 @@ else if ( name.equals( "datasetUrl" ) ) return true; } - private void createDatasetListUI( final String remoteUrl, final Object[] values ) + private String resolveRedirectedURL( String url ) + { + try + { + URL obj = new URL( url ); + + HttpURLConnection conn = ( HttpURLConnection ) obj.openConnection(); + conn.setReadTimeout( 5000 ); + conn.addRequestProperty( "Accept-Language", "en-US,en;q=0.8" ); + conn.addRequestProperty( "User-Agent", "Mozilla" ); + + boolean redirect = false; + + // normally, 3xx is redirect + int status = conn.getResponseCode(); + if ( status != HttpURLConnection.HTTP_OK ) + { + if ( status == HttpURLConnection.HTTP_MOVED_TEMP + || status == HttpURLConnection.HTTP_MOVED_PERM + || status == HttpURLConnection.HTTP_SEE_OTHER ) + redirect = true; + } + + if ( redirect ) + { + + // get redirect url from "location" header field + String newUrl = conn.getHeaderField( "Location" ); + + // get the cookie if need, for login + String cookies = conn.getHeaderField( "Set-Cookie" ); + + // open the new connnection again + conn = ( HttpURLConnection ) new URL( newUrl ).openConnection(); + conn.setRequestProperty( "Cookie", cookies ); + conn.addRequestProperty( "Accept-Language", "en-US,en;q=0.8" ); + conn.addRequestProperty( "User-Agent", "Mozilla" ); + + return newUrl; + } + + return url; + } + catch ( Exception e ) + { + e.printStackTrace(); + } + return url; + } + + private void createDatasetListUI( final String tag, final String remoteUrl, final Object[] values ) { final JList list = new JList( values ); list.setCellRenderer( new ThumbnailListRenderer() ); @@ -146,15 +462,20 @@ public void mouseClicked( final MouseEvent evt ) if ( evt.getClickCount() == 2 ) { final int index = list.locationToIndex( evt.getPoint() ); - final String key = String.valueOf( list.getModel().getElementAt( index ) ); - System.out.println( key ); - try - { - BigDataViewer.view( datasetUrlMap.get( key ), new ProgressWriterIJ() ); - } - catch ( final SpimDataException e ) + final Object cell = list.getModel().getElementAt( index ); + + if ( cell instanceof DataSet ) { - e.printStackTrace(); + DataSet ds = ( DataSet ) cell; + + try + { + BigDataViewer.view( ds.getName(), datasetUrlMap.get( ds.getIndex() ), new ProgressWriterIJ() ); + } + catch ( final SpimDataException e ) + { + e.printStackTrace(); + } } } } @@ -164,7 +485,10 @@ public void mouseClicked( final MouseEvent evt ) scroll.setPreferredSize( new Dimension( 600, 800 ) ); final JFrame frame = new JFrame(); - frame.setTitle( "BigDataServer Browser - " + remoteUrl ); + if ( tag.equals( "" ) ) + frame.setTitle( "BigDataServer Browser - " + remoteUrl ); + else + frame.setTitle( "BigDataServer Browser - " + remoteUrl + " searched by " + tag ); frame.add( scroll ); frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); frame.pack(); @@ -176,7 +500,7 @@ public class ThumbnailListRenderer extends DefaultListCellRenderer { private static final long serialVersionUID = 1L; - Font font = new Font( "helvetica", Font.BOLD, 12 ); + Font font = new Font( "helvetica", Font.PLAIN, 12 ); @Override public Component getListCellRendererComponent( @@ -186,9 +510,36 @@ public Component getListCellRendererComponent( final JLabel label = ( JLabel ) super.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus ); - label.setIcon( imageMap.get( value ) ); - label.setHorizontalTextPosition( JLabel.RIGHT ); - label.setFont( font ); + + if ( value instanceof Category ) + { + Category category = ( Category ) value; + label.setText( category.getName() ); + label.setFont( label.getFont().deriveFont( Font.BOLD, 26 ) ); + label.setBorder( BorderFactory.createEmptyBorder( 0, 5, 0, 0 ) ); + } + else + { + DataSet ds = ( DataSet ) value; + label.setIcon( imageMap.get( ds.getIndex() ) ); + + StringBuilder sb = new StringBuilder( "" + ds.getName() + "
" ); + + if ( domain.equals( "/private" ) ) + { + sb.append( "Public: " + ds.isPublic() + "
" ); + if ( !ds.sharedBy.isEmpty() ) + sb.append( "Shared by " + ds.getSharedBy() + "
" ); + } + + sb.append( "Tags: " + ds.getTags() + "
" ); + sb.append( ds.getDescription() + "
" ); + + label.setText( sb.toString() ); + label.setHorizontalTextPosition( JLabel.RIGHT ); + label.setFont( font ); + } + return label; } } @@ -196,6 +547,7 @@ public Component getListCellRendererComponent( public static void main( final String[] args ) { ImageJ.main( args ); + //new BigDataBrowserPlugIn().run( "/Users/moon/Desktop/local.bdv" ); new BigDataBrowserPlugIn().run( null ); } } diff --git a/src/main/java/bdv/ij/BigDataViewerPlugIn.java b/src/main/java/bdv/ij/BigDataViewerPlugIn.java index 0e26d3b..6fdfd0b 100644 --- a/src/main/java/bdv/ij/BigDataViewerPlugIn.java +++ b/src/main/java/bdv/ij/BigDataViewerPlugIn.java @@ -23,80 +23,83 @@ public class BigDataViewerPlugIn implements PlugIn @Override public void run( final String arg ) { - File file = null; + File file = getFile( arg ); - if ( Prefs.useJFileChooser ) + if( file == null ) { - final JFileChooser fileChooser = new JFileChooser(); - fileChooser.setSelectedFile( new File( lastDatasetPath ) ); - fileChooser.setFileFilter( new FileFilter() + if ( Prefs.useJFileChooser ) { - @Override - public String getDescription() + final JFileChooser fileChooser = new JFileChooser(); + fileChooser.setSelectedFile( new File( lastDatasetPath ) ); + fileChooser.setFileFilter( new FileFilter() { - return "xml files"; - } + @Override + public String getDescription() + { + return "xml files"; + } - @Override - public boolean accept( final File f ) - { - if ( f.isDirectory() ) - return true; - if ( f.isFile() ) + @Override + public boolean accept( final File f ) { - final String s = f.getName(); - final int i = s.lastIndexOf('.'); - if (i > 0 && i < s.length() - 1) { - final String ext = s.substring(i+1).toLowerCase(); - return ext.equals( "xml" ); - } + if ( f.isDirectory() ) + return true; + if ( f.isFile() ) + { + final String s = f.getName(); + final int i = s.lastIndexOf('.'); + if (i > 0 && i < s.length() - 1) { + final String ext = s.substring(i+1).toLowerCase(); + return ext.equals( "xml" ); + } + } + return false; } - return false; - } - } ); + } ); - final int returnVal = fileChooser.showOpenDialog( null ); - if ( returnVal == JFileChooser.APPROVE_OPTION ) - file = fileChooser.getSelectedFile(); - } - else // use FileDialog - { - final FileDialog fd = new FileDialog( ( Frame ) null, "Open", FileDialog.LOAD ); - fd.setDirectory( new File( lastDatasetPath ).getParent() ); - fd.setFile( new File( lastDatasetPath ).getName() ); - final AtomicBoolean workedWithFilenameFilter = new AtomicBoolean( false ); - fd.setFilenameFilter( new FilenameFilter() + final int returnVal = fileChooser.showOpenDialog( null ); + if ( returnVal == JFileChooser.APPROVE_OPTION ) + file = fileChooser.getSelectedFile(); + } + else // use FileDialog { - private boolean firstTime = true; - - @Override - public boolean accept( final File dir, final String name ) + final FileDialog fd = new FileDialog( ( Frame ) null, "Open", FileDialog.LOAD ); + fd.setDirectory( new File( lastDatasetPath ).getParent() ); + fd.setFile( new File( lastDatasetPath ).getName() ); + final AtomicBoolean workedWithFilenameFilter = new AtomicBoolean( false ); + fd.setFilenameFilter( new FilenameFilter() { - if ( firstTime ) - { - workedWithFilenameFilter.set( true ); - firstTime = false; - } + private boolean firstTime = true; - final int i = name.lastIndexOf( '.' ); - if ( i > 0 && i < name.length() - 1 ) + @Override + public boolean accept( final File dir, final String name ) { - final String ext = name.substring( i + 1 ).toLowerCase(); - return ext.equals( "xml" ); + if ( firstTime ) + { + workedWithFilenameFilter.set( true ); + firstTime = false; + } + + final int i = name.lastIndexOf( '.' ); + if ( i > 0 && i < name.length() - 1 ) + { + final String ext = name.substring( i + 1 ).toLowerCase(); + return ext.equals( "xml" ); + } + return false; } - return false; - } - } ); - fd.setVisible( true ); - if ( isMac() && !workedWithFilenameFilter.get() ) - { - fd.setFilenameFilter( null ); + } ); fd.setVisible( true ); - } - final String filename = fd.getFile(); - if ( filename != null ) - { - file = new File( fd.getDirectory() + filename ); + if ( isMac() && !workedWithFilenameFilter.get() ) + { + fd.setFilenameFilter( null ); + fd.setVisible( true ); + } + final String filename = fd.getFile(); + if ( filename != null ) + { + file = new File( fd.getDirectory() + filename ); + } } } @@ -114,6 +117,14 @@ public boolean accept( final File dir, final String name ) } } + private File getFile( String arg ) + { + final File file = new File( arg ); + if( file.exists() ) + return file; + else return null; + } + private boolean isMac() { final String OS = System.getProperty( "os.name", "generic" ).toLowerCase( Locale.ENGLISH ); diff --git a/src/main/java/bdv/ij/util/SpringUtilities.java b/src/main/java/bdv/ij/util/SpringUtilities.java new file mode 100644 index 0000000..dc3ed9e --- /dev/null +++ b/src/main/java/bdv/ij/util/SpringUtilities.java @@ -0,0 +1,250 @@ +package bdv.ij.util; + +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle or the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import javax.swing.*; +import javax.swing.SpringLayout; + +import java.awt.*; + +/** + * A 1.4 file that provides utility methods for + * creating form- or grid-style layouts with SpringLayout. + * These utilities are used by several programs, such as + * SpringBox and SpringCompactGrid. + */ +public class SpringUtilities +{ + /** + * A debugging utility that prints to stdout the component's + * minimum, preferred, and maximum sizes. + */ + public static void printSizes( Component c ) + { + System.out.println( "minimumSize = " + c.getMinimumSize() ); + System.out.println( "preferredSize = " + c.getPreferredSize() ); + System.out.println( "maximumSize = " + c.getMaximumSize() ); + } + + /** + * Aligns the first rows * cols + * components of parent in + * a grid. Each component is as big as the maximum + * preferred width and height of the components. + * The parent is made just big enough to fit them all. + * @param rows number of rows + * @param cols number of columns + * @param initialX x location to start the grid at + * @param initialY y location to start the grid at + * @param xPad x padding between cells + * @param yPad y padding between cells + */ + public static void makeGrid( Container parent, + int rows, int cols, + int initialX, int initialY, + int xPad, int yPad ) + { + SpringLayout layout; + try + { + layout = ( SpringLayout ) parent.getLayout(); + } + catch ( ClassCastException exc ) + { + System.err.println( "The first argument to makeGrid must use SpringLayout." ); + return; + } + + Spring xPadSpring = Spring.constant( xPad ); + Spring yPadSpring = Spring.constant( yPad ); + Spring initialXSpring = Spring.constant( initialX ); + Spring initialYSpring = Spring.constant( initialY ); + int max = rows * cols; + + //Calculate Springs that are the max of the width/height so that all + //cells have the same size. + Spring maxWidthSpring = layout.getConstraints( parent.getComponent( 0 ) ). + getWidth(); + Spring maxHeightSpring = layout.getConstraints( parent.getComponent( 0 ) ). + getHeight(); + for ( int i = 1; i < max; i++ ) + { + SpringLayout.Constraints cons = layout.getConstraints( + parent.getComponent( i ) ); + + maxWidthSpring = Spring.max( maxWidthSpring, cons.getWidth() ); + maxHeightSpring = Spring.max( maxHeightSpring, cons.getHeight() ); + } + + //Apply the new width/height Spring. This forces all the + //components to have the same size. + for ( int i = 0; i < max; i++ ) + { + SpringLayout.Constraints cons = layout.getConstraints( + parent.getComponent( i ) ); + + cons.setWidth( maxWidthSpring ); + cons.setHeight( maxHeightSpring ); + } + + //Then adjust the x/y constraints of all the cells so that they + //are aligned in a grid. + SpringLayout.Constraints lastCons = null; + SpringLayout.Constraints lastRowCons = null; + for ( int i = 0; i < max; i++ ) + { + SpringLayout.Constraints cons = layout.getConstraints( + parent.getComponent( i ) ); + if ( i % cols == 0 ) + { //start of new row + lastRowCons = lastCons; + cons.setX( initialXSpring ); + } + else + { //x position depends on previous component + cons.setX( Spring.sum( lastCons.getConstraint( SpringLayout.EAST ), + xPadSpring ) ); + } + + if ( i / cols == 0 ) + { //first row + cons.setY( initialYSpring ); + } + else + { //y position depends on previous row + cons.setY( Spring.sum( lastRowCons.getConstraint( SpringLayout.SOUTH ), + yPadSpring ) ); + } + lastCons = cons; + } + + //Set the parent's size. + SpringLayout.Constraints pCons = layout.getConstraints( parent ); + pCons.setConstraint( SpringLayout.SOUTH, + Spring.sum( + Spring.constant( yPad ), + lastCons.getConstraint( SpringLayout.SOUTH ) ) ); + pCons.setConstraint( SpringLayout.EAST, + Spring.sum( + Spring.constant( xPad ), + lastCons.getConstraint( SpringLayout.EAST ) ) ); + } + + /* Used by makeCompactGrid. */ + private static SpringLayout.Constraints getConstraintsForCell( + int row, int col, + Container parent, + int cols ) + { + SpringLayout layout = ( SpringLayout ) parent.getLayout(); + Component c = parent.getComponent( row * cols + col ); + return layout.getConstraints( c ); + } + + /** + * Aligns the first rows * cols + * components of parent in + * a grid. Each component in a column is as wide as the maximum + * preferred width of the components in that column; + * height is similarly determined for each row. + * The parent is made just big enough to fit them all. + * @param rows number of rows + * @param cols number of columns + * @param initialX x location to start the grid at + * @param initialY y location to start the grid at + * @param xPad x padding between cells + * @param yPad y padding between cells + */ + public static void makeCompactGrid( Container parent, + int rows, int cols, + int initialX, int initialY, + int xPad, int yPad ) + { + SpringLayout layout; + try + { + layout = ( SpringLayout ) parent.getLayout(); + } + catch ( ClassCastException exc ) + { + System.err.println( "The first argument to makeCompactGrid must use SpringLayout." ); + return; + } + + //Align all cells in each column and make them the same width. + Spring x = Spring.constant( initialX ); + for ( int c = 0; c < cols; c++ ) + { + Spring width = Spring.constant( 0 ); + for ( int r = 0; r < rows; r++ ) + { + width = Spring.max( width, + getConstraintsForCell( r, c, parent, cols ). + getWidth() ); + } + for ( int r = 0; r < rows; r++ ) + { + SpringLayout.Constraints constraints = + getConstraintsForCell( r, c, parent, cols ); + constraints.setX( x ); + constraints.setWidth( width ); + } + x = Spring.sum( x, Spring.sum( width, Spring.constant( xPad ) ) ); + } + + //Align all cells in each row and make them the same height. + Spring y = Spring.constant( initialY ); + for ( int r = 0; r < rows; r++ ) + { + Spring height = Spring.constant( 0 ); + for ( int c = 0; c < cols; c++ ) + { + height = Spring.max( height, + getConstraintsForCell( r, c, parent, cols ). + getHeight() ); + } + for ( int c = 0; c < cols; c++ ) + { + SpringLayout.Constraints constraints = + getConstraintsForCell( r, c, parent, cols ); + constraints.setY( y ); + constraints.setHeight( height ); + } + y = Spring.sum( y, Spring.sum( height, Spring.constant( yPad ) ) ); + } + + //Set the parent's size. + SpringLayout.Constraints pCons = layout.getConstraints( parent ); + pCons.setConstraint( SpringLayout.SOUTH, y ); + pCons.setConstraint( SpringLayout.EAST, x ); + } +}