Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8306086

JTable generic editor border is always black

XMLWordPrintable

      A DESCRIPTION OF THE PROBLEM :
      The generic editor provided by JTable is hard coded to have a black border (see https://github.com/openjdk/jdk17u/blob/908cab4123812b6b206b966a6ce92398cdf42c08/src/java.desktop/share/classes/javax/swing/JTable.java#L5565). This will override the current look and feel provided border for text area.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      1. Start with the TableRenderDemo from the How to Use Tables documentation (https://docs.oracle.com/javase/tutorial/uiswing/examples/components/TableRenderDemoProject/src/components/TableRenderDemo.java)
      2. Change the default border for text fields by adding the following line as the first line in `main`:
          UIManager.getDefaults().put("TextField.border", new LineBorder(Color.YELLOW, 3));
      3. Run the file
      4. Edit one of the values in the "# of Years" column

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      An editor with a 3 pixel yellow border
      ACTUAL -
      An editor with a 1 pixel black border

      ---------- BEGIN SOURCE ----------

      /*
       * 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.
       */
       
      package components;
       
      /*
       * TableRenderDemo.java requires no other files.
       */
       
      import java.awt.Color;
      import javax.swing.DefaultCellEditor;
      import javax.swing.JComboBox;
      import javax.swing.JFrame;
      import javax.swing.JPanel;
      import javax.swing.JScrollPane;
      import javax.swing.JTable;
      import javax.swing.table.AbstractTableModel;
      import javax.swing.table.DefaultTableCellRenderer;
      import javax.swing.table.TableCellRenderer;
      import javax.swing.table.TableColumn;
      import java.awt.Component;
      import java.awt.Dimension;
      import java.awt.GridLayout;
      import javax.swing.UIManager;
      import javax.swing.border.LineBorder;
       
      /**
       * TableRenderDemo is just like TableDemo, except that it
       * explicitly initializes column sizes and it uses a combo box
       * as an editor for the Sport column.
       */
      public class TableRenderDemo extends JPanel {
          private boolean DEBUG = false;
       
          public TableRenderDemo() {
              super(new GridLayout(1,0));
       
              JTable table = new JTable(new MyTableModel());
              table.setPreferredScrollableViewportSize(new Dimension(500, 70));
              table.setFillsViewportHeight(true);
       
              //Create the scroll pane and add the table to it.
              JScrollPane scrollPane = new JScrollPane(table);
       
              //Set up column sizes.
              initColumnSizes(table);
       
              //Fiddle with the Sport column's cell editors/renderers.
              setUpSportColumn(table, table.getColumnModel().getColumn(2));
       
              //Add the scroll pane to this panel.
              add(scrollPane);
          }
       
          /*
           * This method picks good column sizes.
           * If all column heads are wider than the column's cells'
           * contents, then you can just use column.sizeWidthToFit().
           */
          private void initColumnSizes(JTable table) {
              MyTableModel model = (MyTableModel)table.getModel();
              TableColumn column = null;
              Component comp = null;
              int headerWidth = 0;
              int cellWidth = 0;
              Object[] longValues = model.longValues;
              TableCellRenderer headerRenderer =
                  table.getTableHeader().getDefaultRenderer();
       
              for (int i = 0; i < 5; i++) {
                  column = table.getColumnModel().getColumn(i);
       
                  comp = headerRenderer.getTableCellRendererComponent(
                                       null, column.getHeaderValue(),
                                       false, false, 0, 0);
                  headerWidth = comp.getPreferredSize().width;
       
                  comp = table.getDefaultRenderer(model.getColumnClass(i)).
                                   getTableCellRendererComponent(
                                       table, longValues[i],
                                       false, false, 0, i);
                  cellWidth = comp.getPreferredSize().width;
       
                  if (DEBUG) {
                      System.out.println("Initializing width of column "
                                         + i + ". "
                                         + "headerWidth = " + headerWidth
                                         + "; cellWidth = " + cellWidth);
                  }
       
                  column.setPreferredWidth(Math.max(headerWidth, cellWidth));
              }
          }
       
          public void setUpSportColumn(JTable table,
                                       TableColumn sportColumn) {
              //Set up the editor for the sport cells.
              JComboBox comboBox = new JComboBox();
              comboBox.addItem("Snowboarding");
              comboBox.addItem("Rowing");
              comboBox.addItem("Knitting");
              comboBox.addItem("Speed reading");
              comboBox.addItem("Pool");
              comboBox.addItem("None of the above");
              sportColumn.setCellEditor(new DefaultCellEditor(comboBox));
       
              //Set up tool tips for the sport cells.
              DefaultTableCellRenderer renderer =
                      new DefaultTableCellRenderer();
              renderer.setToolTipText("Click for combo box");
              sportColumn.setCellRenderer(renderer);
          }
       
          class MyTableModel extends AbstractTableModel {
              private String[] columnNames = {"First Name",
                                              "Last Name",
                                              "Sport",
                                              "# of Years",
                                              "Vegetarian"};
              private Object[][] data = {
              {"Kathy", "Smith",
               "Snowboarding", new Integer(5), new Boolean(false)},
              {"John", "Doe",
               "Rowing", new Integer(3), new Boolean(true)},
              {"Sue", "Black",
               "Knitting", new Integer(2), new Boolean(false)},
              {"Jane", "White",
               "Speed reading", new Integer(20), new Boolean(true)},
              {"Joe", "Brown",
               "Pool", new Integer(10), new Boolean(false)}
              };
       
              public final Object[] longValues = {"Jane", "Kathy",
                                                  "None of the above",
                                                  new Integer(20), Boolean.TRUE};
       
              public int getColumnCount() {
                  return columnNames.length;
              }
       
              public int getRowCount() {
                  return data.length;
              }
       
              public String getColumnName(int col) {
                  return columnNames[col];
              }
       
              public Object getValueAt(int row, int col) {
                  return data[row][col];
              }
       
              /*
               * JTable uses this method to determine the default renderer/
               * editor for each cell. If we didn't implement this method,
               * then the last column would contain text ("true"/"false"),
               * rather than a check box.
               */
              public Class getColumnClass(int c) {
                  return getValueAt(0, c).getClass();
              }
       
              /*
               * Don't need to implement this method unless your table's
               * editable.
               */
              public boolean isCellEditable(int row, int col) {
                  //Note that the data/cell address is constant,
                  //no matter where the cell appears onscreen.
                  if (col < 2) {
                      return false;
                  } else {
                      return true;
                  }
              }
       
              /*
               * Don't need to implement this method unless your table's
               * data can change.
               */
              public void setValueAt(Object value, int row, int col) {
                  if (DEBUG) {
                      System.out.println("Setting value at " + row + "," + col
                                         + " to " + value
                                         + " (an instance of "
                                         + value.getClass() + ")");
                  }
       
                  data[row][col] = value;
                  fireTableCellUpdated(row, col);
       
                  if (DEBUG) {
                      System.out.println("New value of data:");
                      printDebugData();
                  }
              }
       
              private void printDebugData() {
                  int numRows = getRowCount();
                  int numCols = getColumnCount();
       
                  for (int i=0; i < numRows; i++) {
                      System.out.print(" row " + i + ":");
                      for (int j=0; j < numCols; j++) {
                          System.out.print(" " + data[i][j]);
                      }
                      System.out.println();
                  }
                  System.out.println("--------------------------");
              }
          }
       
          /**
           * Create the GUI and show it. For thread safety,
           * this method should be invoked from the
           * event-dispatching thread.
           */
          private static void createAndShowGUI() {
              //Create and set up the window.
              JFrame frame = new JFrame("TableRenderDemo");
              frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
       
              //Create and set up the content pane.
              TableRenderDemo newContentPane = new TableRenderDemo();
              newContentPane.setOpaque(true); //content panes must be opaque
              frame.setContentPane(newContentPane);
       
              //Display the window.
              frame.pack();
              frame.setVisible(true);
          }
       
          public static void main(String[] args) {
              UIManager.getDefaults().put("TextField.border", new LineBorder(Color.YELLOW, 3));
              //Schedule a job for the event-dispatching thread:
              //creating and showing this application's GUI.
              javax.swing.SwingUtilities.invokeLater(new Runnable() {
                  public void run() {
                      createAndShowGUI();
                  }
              });
          }
      }
      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Use your own editor rather than the built-in generic editor.

      FREQUENCY : always


            pnarayanaswa Praveen Narayanaswamy
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: