View Javadoc
1   package org.opentrafficsim.editor.render;
2   
3   import java.awt.Color;
4   import java.awt.Component;
5   import java.awt.event.ActionEvent;
6   import java.awt.event.ActionListener;
7   import java.awt.event.KeyAdapter;
8   import java.awt.event.KeyEvent;
9   
10  import javax.swing.DefaultCellEditor;
11  import javax.swing.JCheckBox;
12  import javax.swing.JComponent;
13  import javax.swing.JTable;
14  import javax.swing.JTextField;
15  import javax.swing.UIManager;
16  import javax.swing.border.EmptyBorder;
17  import javax.swing.border.LineBorder;
18  
19  import org.opentrafficsim.editor.AttributesTableModel;
20  import org.opentrafficsim.editor.DocumentReader;
21  import org.opentrafficsim.editor.OtsEditor;
22  import org.opentrafficsim.editor.Undo.ActionType;
23  import org.opentrafficsim.editor.XsdTreeNode;
24  import org.opentrafficsim.swing.gui.AppearanceControlTextField;
25  
26  /**
27   * Editor for attribute table cells. Extends the default cell editor with checkboxes for boolean attributes (those that cannot
28   * be specified with an expression).
29   * <p>
30   * Copyright (c) 2023-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
31   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
32   * </p>
33   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
34   */
35  public class AttributesCellEditor extends DefaultCellEditor
36  {
37      /** */
38      private static final long serialVersionUID = 20230203L;
39  
40      /** Checkbox to use as editor for boolean attributes. */
41      private JCheckBox checkBox = new JCheckBox();
42  
43      /** Listener that will set the boolean on the right attribute, must be unregistered for a next edited boolean attribute. */
44      private ActionListener lastActionListener;
45  
46      /** Whether the editor is currently using a checkbox. */
47      private boolean checkMode;
48  
49      /** Editor. */
50      private final OtsEditor editor;
51  
52      /**
53       * Constructor.
54       * @param table table of the attributes.
55       * @param editor editor.
56       */
57      public AttributesCellEditor(final JTable table, final OtsEditor editor)
58      {
59          super(new AppearanceControlTextField());
60          getComponent().addKeyListener(new KeyAdapter()
61          {
62              @Override
63              public void keyReleased(final KeyEvent e)
64              {
65                  int col = table.getSelectedColumn();
66                  int editorCol = table.convertColumnIndexToView(col);
67                  if (editorCol == 1)
68                  {
69                      int row = table.getSelectedRow();
70                      String value = ((JTextField) e.getComponent()).getText();
71                      table.getModel().setValueAt(value, row, col);
72                  }
73              }
74          });
75          setClickCountToStart(1);
76          this.checkBox.setBorder(new EmptyBorder(0, 0, 0, 0));
77          this.editor = editor;
78      }
79  
80      @Override
81      public Object getCellEditorValue()
82      {
83          if (this.checkMode)
84          {
85              this.checkMode = false;
86              return Boolean.toString(this.checkBox.isSelected());
87          }
88          return super.getCellEditorValue();
89      }
90  
91      @Override
92      public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected,
93              final int row, final int column)
94      {
95          XsdTreeNode node = ((AttributesTableModel) table.getModel()).getNode();
96          String attribute = DocumentReader.getAttribute(node.getAttributeNode(row), "name");
97          this.editor.getUndo().startAction(ActionType.ATTRIBUTE_CHANGE, node, attribute);
98          if (table.convertColumnIndexToModel(column) == 1)
99          {
100             this.checkBox.setVisible(false);
101             if ("xsd:boolean".equals(node.getAttributeBaseType(row)))
102             {
103                 String message = node.isSelfValid() ? null : node.reportInvalidAttributeValue(row);
104                 if (message != null)
105                 {
106                     this.checkBox.setToolTipText(OtsEditor.limitTooltip(message));
107                     this.checkBox.setBackground(OtsEditor.INVALID_COLOR);
108                 }
109                 else
110                 {
111                     this.checkBox.setToolTipText(null);
112                     if (isSelected)
113                     {
114                         this.checkBox.setBackground(UIManager.getColor("Table.selectionBackground"));
115                     }
116                     else
117                     {
118                         this.checkBox.setBackground(UIManager.getColor("Panel.background"));
119                     }
120                 }
121                 this.checkBox.setSelected(value != null && value.toString().equalsIgnoreCase("true"));
122                 this.checkBox.setVisible(true);
123                 this.checkBox.removeActionListener(this.lastActionListener);
124                 this.lastActionListener = new ActionListener()
125                 {
126                     @Override
127                     public void actionPerformed(final ActionEvent e)
128                     {
129                         int dataColumn = table.convertColumnIndexToModel(column);
130                         table.getModel().setValueAt(Boolean.toString(AttributesCellEditor.this.checkBox.isSelected()), row,
131                                 dataColumn);
132                         AttributesCellEditor.this.checkBox.setToolTipText(null);
133                         AttributesCellEditor.this.checkBox.setBackground(UIManager.getColor("Table.selectionBackground"));
134                     }
135                 };
136                 this.checkBox.addActionListener(this.lastActionListener);
137                 this.checkMode = true;
138                 return this.checkBox;
139             }
140         }
141         this.checkMode = false;
142         // JTable.GenericEditor cannot be extended, setting a black border on the wrapped JTextField is the main thing it does
143         ((JComponent) getComponent()).setBorder(new LineBorder(Color.black));
144         return super.getTableCellEditorComponent(table, value, isSelected, row, column);
145     }
146 }