View Javadoc
1   package org.opentrafficsim.editor.render;
2   
3   import java.awt.Color;
4   import java.awt.Component;
5   import java.awt.Graphics;
6   import java.awt.Image;
7   import java.awt.image.BufferedImage;
8   import java.io.IOException;
9   import java.util.Optional;
10  
11  import javax.swing.Icon;
12  import javax.swing.JTree;
13  import javax.swing.UIManager;
14  import javax.swing.border.EmptyBorder;
15  import javax.swing.border.LineBorder;
16  import javax.swing.tree.DefaultTreeCellRenderer;
17  
18  import org.opentrafficsim.animation.data.util.IconUtil;
19  import org.opentrafficsim.editor.OtsEditor;
20  import org.opentrafficsim.editor.XsdTreeNode;
21  
22  /**
23   * Renderer for the nodes in the tree.
24   * <p>
25   * Copyright (c) 2023-2026 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
26   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
27   * </p>
28   * @author Wouter Schakel
29   */
30  public class XsdTreeCellRenderer extends DefaultTreeCellRenderer
31  {
32  
33      /** Serialization version UID. */
34      private static final long serialVersionUID = 20230221L;
35  
36      /** Number of pixels between icons when they are combined. */
37      private static final int ICON_MARGIN = 3;
38  
39      /** Editor. */
40      private final OtsEditor editor;
41  
42      /** Image for nodes with a consumer, typically an editor. */
43      private final Image consumer;
44  
45      /** Image for nodes with a description. */
46      private final Image description;
47  
48      /** Icon for option nodes, indicating a drop-down can be show. */
49      private final Image dropdown;
50  
51      /**
52       * Constructor. Loads icons.
53       * @param editor editor.
54       * @throws IOException if an icon could not be loaded from resources.
55       */
56      public XsdTreeCellRenderer(final OtsEditor editor) throws IOException
57      {
58          this.editor = editor;
59          this.consumer = IconUtil.asImage("Application24.png").getScaledInstance(12, 12, Image.SCALE_SMOOTH);
60          this.description = IconUtil.asImage("Information24.png").getScaledInstance(12, 12, Image.SCALE_SMOOTH);
61          this.dropdown = IconUtil.asImage("Dropdown24.png").getScaledInstance(16, 16, Image.SCALE_SMOOTH);
62  
63          this.leafIcon = IconUtil.of("File24.png").imageSize(16, 16).get();
64          this.openIcon = IconUtil.of("FolderOpen24.png").imageSize(16, 16).get();
65          this.closedIcon = IconUtil.of("Folder24.png").imageSize(16, 16).get();
66      }
67  
68      @Override
69      public Component getTreeCellRendererComponent(final JTree tree, final Object value, final boolean selected,
70              final boolean expanded, final boolean leaf, final int row, final boolean hasFocus)
71      {
72          super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
73          XsdTreeNode node = (XsdTreeNode) value;
74          Optional<Icon> customIcon = this.editor.getCustomIcon(node.getPathString());
75          if (customIcon.isPresent())
76          {
77              setIcon(customIcon.get());
78          }
79          if (node.hasConsumer())
80          {
81              preAppend(this.consumer, false);
82          }
83          if (node.getDescription().isPresent())
84          {
85              preAppend(this.description, false);
86          }
87          if (node.isChoice() && !node.isIncluded())
88          {
89              preAppend(this.dropdown, false);
90          }
91          if (node.isActive())
92          {
93              if (node.isIncluded())
94              {
95                  setForeground(OtsEditor.getInactiveColor());
96              }
97              else
98              {
99                  setForeground(UIManager.getColor("Table.foreground"));
100             }
101             if (node.equals(this.editor.getChoiceNode()))
102             {
103                 // draws cell to mimic header bar above choice menu
104                 setOpaque(true);
105                 setBackground(new Color(UIManager.getColor("Panel.background").getRGB())); // ColorUIResource is ignored
106                 setBorder(new LineBorder(UIManager.getColor("Menu.acceleratorForeground"), 1, false));
107             }
108             else
109             {
110                 if (node.isValid())
111                 {
112                     if (node.hasExpression())
113                     {
114                         setOpaque(true);
115                         setBackground(OtsEditor.getExpressionColor());
116                     }
117                     else
118                     {
119                         setOpaque(false);
120                     }
121                 }
122                 else
123                 {
124                     setOpaque(true);
125                     setBackground(OtsEditor.getInvalidColor());
126                 }
127                 setBorder(new EmptyBorder(0, 0, 0, 0));
128             }
129         }
130         else
131         {
132             setOpaque(false);
133             setForeground(OtsEditor.getInactiveColor());
134             setBorder(new EmptyBorder(0, 0, 0, 0));
135         }
136         return this;
137     }
138 
139     /**
140      * Pre-/appends the provided image to the left-hand or right-hand side of the current icon.
141      * @param iconImage image to pre-/append.
142      * @param prepend when true, the image is prepended rather than appended.
143      */
144     private void preAppend(final Image iconImage, final boolean prepend)
145     {
146         Icon base = getIcon();
147         int w = base.getIconWidth() + ICON_MARGIN + iconImage.getWidth(null);
148         int h = Math.max(base.getIconHeight(), iconImage.getHeight(null));
149         BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
150         Graphics g = image.getGraphics();
151         int dy = (h - base.getIconHeight()) / 2;
152         int x = prepend ? iconImage.getWidth(null) + ICON_MARGIN : 0;
153         base.paintIcon(this, g, x, dy);
154         dy = (h - iconImage.getHeight(null)) / 2;
155         x = prepend ? 0 : base.getIconWidth() + ICON_MARGIN;
156         g.drawImage(iconImage, x, dy, null);
157         setIcon(new IconUtil.InterpolatedImageIcon(image));
158     }
159 
160 }