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