View Javadoc
1   package org.opentrafficsim.editor.render;
2   
3   import java.awt.Color;
4   import java.awt.Component;
5   import java.util.Optional;
6   
7   import javax.swing.Icon;
8   import javax.swing.JCheckBox;
9   import javax.swing.JLabel;
10  import javax.swing.JTable;
11  import javax.swing.SwingConstants;
12  import javax.swing.UIManager;
13  import javax.swing.border.Border;
14  import javax.swing.border.EmptyBorder;
15  import javax.swing.border.LineBorder;
16  import javax.swing.table.TableCellRenderer;
17  
18  import org.opentrafficsim.editor.AttributesTableModel;
19  import org.opentrafficsim.editor.OtsEditor;
20  import org.opentrafficsim.editor.XsdTreeNode;
21  
22  /**
23   * Renderer for cells in the attributes table. Provides a {@code JCheckBox} for boolean-type attributes (those that cannot be
24   * specified with an expression).
25   * <p>
26   * Copyright (c) 2023-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
27   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
28   * </p>
29   * @author Wouter Schakel
30   */
31  public class AttributeCellRenderer extends JLabel implements TableCellRenderer
32  {
33  
34      /** Serialization version UID. */
35      private static final long serialVersionUID = 20230226L;
36  
37      /** Empty border for re-use. */
38      private static final Border EMPTY_BORDER = new EmptyBorder(0, 0, 0, 0);
39  
40      /** Info icon. */
41      private final Icon infoIcon;
42  
43      /** Checkbox to use for boolean types. */
44      private final JCheckBox checkBox = new JCheckBox();
45  
46      /** Selection color. */
47      private final Color selectionColor = UIManager.getColor("Table.selectionBackground");
48  
49      /** Selection background color. */
50      private final Color tableSelectionBackgroundColor = UIManager.getColor("Table.selectionBackground");
51  
52      /** Foreground color. */
53      private final Color tableForegroundColor = UIManager.getColor("Table.foreground");
54  
55      /** Background color. */
56      private final Color tableBackgroundColor = UIManager.getColor("Table.background");
57  
58      /** Panel color. */
59      private final Color panelBackgroundColor = UIManager.getColor("Panel.background");
60  
61      /** Line border for editable column. */
62      private final Border lineBorder = new LineBorder(UIManager.getColor("Table.gridColor"));
63  
64      /**
65       * Constructor.
66       * @param infoIcon info icon.
67       */
68      public AttributeCellRenderer(final Icon infoIcon)
69      {
70          setOpaque(true);
71          this.infoIcon = infoIcon;
72          this.checkBox.setBorder(EMPTY_BORDER);
73      }
74  
75      @Override
76      public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected,
77              final boolean hasFocus, final int row, final int column)
78      {
79          XsdTreeNode node = null;
80          if (table.convertColumnIndexToModel(column) == AttributesTableModel.VALUE_COLUMN)
81          {
82              node = ((AttributesTableModel) table.getModel()).getNode();
83              String baseType = node.getAttributeBaseType(row);
84              if ("xsd:boolean".equals(baseType))
85              {
86                  String message = node.isSelfValid() ? null : node.reportInvalidAttributeValue(row).orElse(null);
87                  if (message != null)
88                  {
89                      this.checkBox.setToolTipText(OtsEditor.limitTooltip(message));
90                      this.checkBox.setBackground(OtsEditor.getInvalidColor());
91                  }
92                  else
93                  {
94                      this.checkBox.setToolTipText(null);
95                      if (isSelected)
96                      {
97                          this.checkBox.setBackground(this.selectionColor);
98                      }
99                      else if (node.attributeIsExpression(row))
100                     {
101                         this.checkBox.setBackground(OtsEditor.getExpressionColor());
102                     }
103                     else
104                     {
105                         this.checkBox.setBackground(table.getBackground());
106                     }
107                 }
108                 if (value == null || value.toString().isEmpty())
109                 {
110                     Optional<String> defaultValue = node.getDefaultAttributeValue(row);
111                     this.checkBox.setSelected(defaultValue.isPresent() && defaultValue.toString().equalsIgnoreCase("true"));
112                     this.checkBox.setText(" (default)");
113                     this.checkBox.setFont(table.getFont());
114                 }
115                 else
116                 {
117                     this.checkBox.setSelected(value.toString().equalsIgnoreCase("true"));
118                     this.checkBox.setText("");
119                 }
120                 // All xsd:boolean attributes under Ots.Definitions are 'Default' that should be disabled and false by default.
121                 if (node.getPathString().startsWith("Ots.Definitions"))
122                 {
123                     this.checkBox.setEnabled(false);
124                 }
125                 else
126                 {
127                     this.checkBox.setEnabled(true);
128                 }
129                 return this.checkBox;
130             }
131         }
132 
133         boolean showingDefault = false;
134         if (table.convertColumnIndexToModel(column) == AttributesTableModel.VALUE_COLUMN)
135         {
136             if (value == null || value.toString().isEmpty())
137             {
138                 node = ((AttributesTableModel) table.getModel()).getNode();
139                 Optional<String> defaultValue = node.getDefaultAttributeValue(row);
140                 showingDefault = defaultValue.isPresent();
141                 setText(showingDefault ? defaultValue.get() : "");
142             }
143             else
144             {
145                 setText(value.toString());
146             }
147         }
148         else if (table.convertColumnIndexToModel(column) == AttributesTableModel.DESCRIPTION_COLUMN)
149         {
150             setText("");
151         }
152         else
153         {
154             setText(value == null ? "" : value.toString());
155 
156         }
157         setFont(table.getFont());
158         setIcon(null);
159         setForeground(showingDefault ? OtsEditor.getInactiveColor() : this.tableForegroundColor);
160         if (table.convertColumnIndexToModel(column) == AttributesTableModel.VALUE_COLUMN)
161         {
162             String message = node.isSelfValid() ? null : node.reportInvalidAttributeValue(row).orElse(null);
163             if (message != null)
164             {
165                 setToolTipText(OtsEditor.limitTooltip(message));
166                 setBackground(OtsEditor.getInvalidColor());
167             }
168             else
169             {
170                 setToolTipText(value == null || value.toString().isEmpty() ? null : value.toString());
171                 if (node.isIncluded())
172                 {
173                     setBackground(this.panelBackgroundColor);
174                 }
175                 else if (node.attributeIsExpression(row))
176                 {
177                     setBackground(OtsEditor.getExpressionColor());
178                 }
179                 else
180                 {
181                     setBackground(this.tableBackgroundColor);
182                 }
183             }
184             setBorder(this.lineBorder);
185         }
186         else
187         {
188             setToolTipText(null);
189             setBorder(EMPTY_BORDER);
190             if (table.convertColumnIndexToModel(column) == AttributesTableModel.DESCRIPTION_COLUMN && value != null)
191             {
192                 setIcon(this.infoIcon);
193             }
194             if (isSelected)
195             {
196                 setBackground(this.tableSelectionBackgroundColor);
197             }
198             else
199             {
200                 setBackground(table.getBackground());
201             }
202         }
203         if (table.convertColumnIndexToModel(column) == AttributesTableModel.USE_COLUMN
204                 || table.convertColumnIndexToModel(column) == AttributesTableModel.DESCRIPTION_COLUMN)
205         {
206             setHorizontalAlignment(SwingConstants.CENTER);
207         }
208         else
209         {
210             setHorizontalAlignment(SwingConstants.LEFT);
211         }
212         return this;
213     }
214 
215 }