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 JTable; table of the attributes.
55       * @param editor OtsEditor; editor.
56       */
57      public AttributesCellEditor(final JTable table, final OtsEditor editor)
58      {
59          super(new AppearanceControlTextField());
60          getComponent().addKeyListener(new KeyAdapter()
61          {
62              /** {@inheritDoc} */
63              @Override
64              public void keyReleased(final KeyEvent e)
65              {
66                  int col = table.getSelectedColumn();
67                  int editorCol = table.convertColumnIndexToView(col);
68                  if (editorCol == 1)
69                  {
70                      int row = table.getSelectedRow();
71                      String value = ((JTextField) e.getComponent()).getText();
72                      table.getModel().setValueAt(value, row, col);
73                  }
74              }
75          });
76          setClickCountToStart(1);
77          this.checkBox.setBorder(new EmptyBorder(0, 0, 0, 0));
78          this.editor = editor;
79      }
80  
81      /** {@inheritDoc} */
82      @Override
83      public Object getCellEditorValue()
84      {
85          if (this.checkMode)
86          {
87              this.checkMode = false;
88              return Boolean.toString(this.checkBox.isSelected());
89          }
90          return super.getCellEditorValue();
91      }
92  
93      /** {@inheritDoc} */
94      @Override
95      public Component getTableCellEditorComponent(final JTable table, final Object value, final boolean isSelected,
96              final int row, final int column)
97      {
98          XsdTreeNode node = ((AttributesTableModel) table.getModel()).getNode();
99          String attribute = DocumentReader.getAttribute(node.getAttributeNode(row), "name");
100         this.editor.getUndo().startAction(ActionType.ATTRIBUTE_CHANGE, node, attribute);
101         if (table.convertColumnIndexToModel(column) == 1)
102         {
103             this.checkBox.setVisible(false);
104             if ("xsd:boolean".equals(node.getAttributeBaseType(row)))
105             {
106                 String message = node.isSelfValid() ? null : node.reportInvalidAttributeValue(row);
107                 if (message != null)
108                 {
109                     this.checkBox.setToolTipText(OtsEditor.limitTooltip(message));
110                     this.checkBox.setBackground(OtsEditor.INVALID_COLOR);
111                 }
112                 else
113                 {
114                     this.checkBox.setToolTipText(null);
115                     if (isSelected)
116                     {
117                         this.checkBox.setBackground(UIManager.getColor("Table.selectionBackground"));
118                     }
119                     else
120                     {
121                         this.checkBox.setBackground(UIManager.getColor("Panel.background"));
122                     }
123                 }
124                 this.checkBox.setSelected(value != null && value.toString().equalsIgnoreCase("true"));
125                 this.checkBox.setVisible(true);
126                 this.checkBox.removeActionListener(this.lastActionListener);
127                 this.lastActionListener = new ActionListener()
128                 {
129                     /** {@inheritDoc} */
130                     @Override
131                     public void actionPerformed(final ActionEvent e)
132                     {
133                         int dataColumn = table.convertColumnIndexToModel(column);
134                         table.getModel().setValueAt(Boolean.toString(AttributesCellEditor.this.checkBox.isSelected()), row,
135                                 dataColumn);
136                         AttributesCellEditor.this.checkBox.setToolTipText(null);
137                         AttributesCellEditor.this.checkBox.setBackground(UIManager.getColor("Table.selectionBackground"));
138                     }
139                 };
140                 this.checkBox.addActionListener(this.lastActionListener);
141                 this.checkMode = true;
142                 return this.checkBox;
143             }
144         }
145         this.checkMode = false;
146         // JTable.GenericEditor cannot be extended, setting a black border on the wrapped JTextField is the main thing it does
147         ((JComponent) getComponent()).setBorder(new LineBorder(Color.black));
148         return super.getTableCellEditorComponent(table, value, isSelected, row, column);
149     }
150 }