title: JTextArea (line wrapping) as TableCellRender to multiple colums
body: <p>Title says it all. I have a table where in each cell there is the possibility of having long text. What <code>TableCellRenderer</code> should i use in order to display the data - text of each cell with proper line wrap? I have tried a lot of variations messing with renderer's size and <code>setRowHeight</code> but nothing seem to be optimal. Every thing I tried has some kind of instability.</p>
  
  <p>First thing I tried is to use a <code>JTextArea</code> as my <code>TableCellRenderer</code> in order to take the advantage of <code>setLineWrap</code>. This <a href="https://stackoverflow.com/a/37768834/6579265">answer</a> describes exactly what i did. It works exactly as i want but there is a problem with it. You can have only one column with this renderer. If you add the renderer to a second column, the column with the bigger "id" (column index in table) will "dominate" (and give the height to table's row) ignoring the case where the text in the column with the smaller "id" needs more line to be shown a.k.a bigger height.</p>
  
  <p>Check this gif. It is exactly the behavior i want to achieve, with the column with "highest" text to dominate in row's height. It works because the column has the biggest index. (It renders it last)</p>
  
  <p><a href="https://i.stack.imgur.com/x2KhO.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/x2KhO.gif" alt="good"></a></p>
  
  <p>You see? The column's width is decreased, so the text need more lines to be represented completely, and the text in column 1 is ok, since all its text is visible.</p>
  
  <p>Now lets see the case where the text in column 1 needs more lines than the last column.</p>
  
  <p><a href="https://i.stack.imgur.com/ivy9V.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/ivy9V.gif" alt="bad"></a></p>
  
  <p>It is obvious that we are loosing the text in the first column. The last column (which is rendered last) has the proper height, so column 1 does not "dominate" and fill with space the last column.</p>
  
  <p>The SSCCE that produces this behavior:</p>
  
  <pre><code>public class TableTest extends JTable {
      private static final long serialVersionUID = 7180027425789244942L;
      private static final String[] COLUMNS = { "SomeColumn", "OtherColumn", "OtherOtherColumn" };
  
      public TableTest() {
          super();
          Object[][] data = new Object[5][3];
          for (int i = 0; i &lt; data.length; i++) {
              data[i][0] = "Row: " + i + " - " + loremIpsum();
              data[i][1] = "Row: " + i + " Maybe something small?";
              data[i][2] = "Row: " + i + "___" + new StringBuilder(loremIpsum()).reverse().toString();
          }
          setModel(new DefaultTableModel(data, COLUMNS) {
              @Override
              public Class&lt;?&gt; getColumnClass(int columnIndex) {
                  return String.class;
              }
          });
          setDefaultRenderer(String.class, new WordWrapCellRenderer());
          getTableHeader().setReorderingAllowed(false);
      }
  
      public static class WordWrapCellRenderer extends JTextArea implements TableCellRenderer {
          private WordWrapCellRenderer() {
              setLineWrap(true);
              setWrapStyleWord(true);
          }
  
          @Override
          public WordWrapCellRenderer getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
              setText(value.toString());
              setSize(table.getColumnModel().getColumn(column).getWidth(), getPreferredSize().height);
              if (table.getRowHeight(row) != getPreferredSize().height) {
                  table.setRowHeight(row, getPreferredSize().height);
              }
              return this;
          }
      }
  
      private String loremIpsum() {
          return "Lorem Ipsum is simply dummy text of the printing and typesetting industry."
                  + " Lorem Ipsum has been the industry's standard dummy text ever since the 1500s,"
                  + " when an unknown printer took a galley of type and scrambled it to make a type specimen book."
                  + " It has survived not only five centuries, but also the leap into electronic typesetting, "
                  + "remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset"
                  + " sheets containing Lorem Ipsum passages, and more recently with desktop publishing software"
                  + " like Aldus PageMaker including versions of Lorem Ipsum";
      }
  
      public static void main(String[] args) {
          SwingUtilities.invokeLater(() -&gt; {
              JFrame frame = new JFrame("Test");
              frame.setLayout(new BorderLayout());
  
              TableTest table = new TableTest();
  
              JScrollPane sp = new JScrollPane(table);
              frame.add(sp);
              frame.setSize(500, 500);
              frame.setLocationRelativeTo(null);
              frame.setVisible(true);
          });
      }
  }
  </code></pre>
  
  <p>First attempt to solve this issue is to change <code>table.getRowHeight(row) != getPreferredSize().height</code> to <code>table.getRowHeight(row) &lt;= getPreferredSize().height</code> in order to make the height of the row change only when a "taller" component must be rendered. This will not work either because  after a "tall" render row height will not be restored (wrapped). Related gif image:</p>
  
  <p><a href="https://i.stack.imgur.com/a4Ut3.gif" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/a4Ut3.gif" alt="enter image description here"></a></p>
  
  <p>After seeing this, i tried to create some kind of listener (<code>ComponentListener#componentResized</code> - <code>MouseListener#mouseReleased</code> to header?) that restores each row's height like the following:</p>
  
  <pre><code>private void restoreRowHeight() {
      if (getModel() == null) //causing NPE
          return;
  
      for (int row = 0; row &lt; getRowCount(); row++) {
          int heightOfTheTallestComponent = -1;
          for (int column = 0; column &lt; getColumnCount(); column++) {
              Component c = prepareRenderer(getDefaultRenderer(String.class), row, column);
              if (c.getPreferredSize().height &gt; heightOfTheTallestComponent)
                  heightOfTheTallestComponent = c.getPreferredSize().height;
          }
          setRowHeight(row, heightOfTheTallestComponent);
      }
  }
  </code></pre>
  
  <p>None of the listeners i could think of seem to fit. However, even if i find the proper listener that will call this method, a small but very annoying glitch takes place. (Any alternatives that prevent it you are welcome).</p>
  
  <hr>
  
  <p>Finally i took some hopes (i regret it after 10 minutes) that maybe JTable renders properly <code>JLabel</code>s with <code>&lt;html&gt;</code> text (a <code>JLabel</code> wraps lines when it has an HTML text) and used the following (extending <code>DefaultTableCellRenderer</code>)</p>
  
  <pre><code>int width = table.getColumnModel().getColumn(column).getWidth();
  setText("&lt;html&gt;&lt;p style='width: " + width + "px'&gt;" + String.valueOf(value));
  </code></pre>
  
  <p>But of course, no chance.</p>
  
  <p>Another approach i tried is again with a JLabel which is described in this <a href="https://stackoverflow.com/a/54132214/6579265">answer</a> but again, there is no height restoration in case it needs to be restored.</p>
  
  <p>Is there any solution out there that will wrap and show the text of all columns properly and does not cause a glitch?</p>