title: JLabel with wrapped HTML-text as JScrollPane client
body: <p>A JLabel containing HTML-text automatically wraps lines using the available space. If one adds that JLabel to a JSrollPane he has to set the preferredSize to a decent value otherwise it won`t wrap. All this should work fine along other Components inside a JPanel using a LayoutManager.</p>
  
  <p>Cause I want a resizeable application window I extended JScrollPane to keep track of the resize events and dynamically change the size synced to the width of the viewport. Basically it works but sometimes the calculation of the preferred height by the layout manager is wrong (value too big or too small). For instance the visibility of the red border cutting through the first line indicates that the calculation of the height is wrong.</p>
  
  <p><img src="https://i.stack.imgur.com/ufVMD.png" alt="framegrabbing"></p>
  
  <p>I cannot reproduce the failure with a single wrapping JLabel.</p>
  
  <pre><code>import java.awt.Color;
  import java.awt.Container;
  import java.awt.Dimension;
  import java.awt.GridBagConstraints;
  import java.awt.GridBagLayout;
  import java.awt.event.ComponentEvent;
  import java.awt.event.ComponentListener;
  import javax.swing.BorderFactory;
  import javax.swing.JFrame;
  import javax.swing.JLabel;
  import javax.swing.JPanel;
  import javax.swing.JScrollPane;
  import javax.swing.SwingUtilities;
  
  public class WrappedLabel implements Runnable {
  
      public static void main( String[] args ){
          SwingUtilities.invokeLater( new WrappedLabel() );
      }
  
      @Override
      public void run(){
          final JPanel panel = new JPanel( new GridBagLayout() );
          final GridBagConstraints gc = new GridBagConstraints();
          gc.fill = GridBagConstraints.BOTH;
          gc.weightx = 1.0;
          gc.weighty = 1.0;
          {
              gc.gridx = 0;
              gc.gridy = 0;
              final JLabel label = new JLabel(
                  "&lt;html&gt;" + "please add some more text here"
              );
              label.setBorder( BorderFactory.createLineBorder( Color.RED ) );
              panel.add( label, gc );
          }
          {
              gc.gridx = 0;
              gc.gridy = 1;
              final JLabel label = new JLabel(
                  "&lt;html&gt;" + "please add some more text here"
              );
              label.setBorder( BorderFactory.createLineBorder( Color.RED ) );
              panel.add( label, gc );
          }
          final JFrame frame = new JFrame();
          frame.add( new ScrollPane( panel ) );
          frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
          frame.setSize( 256, 256 );
          frame.setVisible( true );
      }
  
      private class ScrollPane extends JScrollPane implements ComponentListener {
  
          ScrollPane( Container view ){
              super( view );
              this.viewport.addComponentListener( this );
          }
  
          @Override
          public void componentHidden( ComponentEvent ce ){
          }
  
          @Override
          public void componentMoved( ComponentEvent ce ){
          }
  
          /** calculating required height is a 3 step process
            * 1. sync width of client and viewport, set height of client to high value
            * 2. let GridbagManager calculate required minimum size
            * 3. set preferredSize and revalidate
           **/
          @Override
          public void componentResized( ComponentEvent ce ){
              assert( this.viewport == ce.getSource() );
              final Container view = (Container) this.viewport.getView();
              final int width = this.viewport.getExtentSize().width;
              view.setPreferredSize( new Dimension( width, Integer.MAX_VALUE ) );
              final int height = view.getLayout().preferredLayoutSize( view ).height;
              view.setPreferredSize( new Dimension( width, height ) );
              view.revalidate();
          }
  
          @Override
          public void componentShown( ComponentEvent ce ){
          }
  
      }
  
  }
  </code></pre>