package nodebox.client; import nodebox.node.Node; import nodebox.node.Port; import nodebox.ui.ShadowLabel; import nodebox.ui.Theme; import nodebox.util.StringUtils; import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.io.IOException; public class PortRow extends JComponent implements MouseListener, ActionListener { private static Image popupButtonImage; static { try { popupButtonImage = ImageIO.read(PortRow.class.getResourceAsStream("/options-button.png")); } catch (IOException e) { throw new RuntimeException(e); } } private NodeBoxDocument document; private String portName; private JLabel label; private JComponent control; private JPopupMenu popupMenu; private static final int TOP_PADDING = 2; private static final int BOTTOM_PADDING = 2; public PortRow(NodeBoxDocument document, String portName, JComponent control) { this.document = document; this.portName = portName; addMouseListener(this); setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); Dimension labelSize = new Dimension(PortView.LABEL_WIDTH, 16); Port port = getPort(); label = new ShadowLabel(port.getDisplayLabel()); if (! port.getDescription().isEmpty()) label.setToolTipText(port.getName() + ": " + port.getDescription()); else label.setToolTipText(port.getName()); label.setBorder(null); label.setPreferredSize(labelSize); label.setMinimumSize(labelSize); this.control = control; control.setBorder(BorderFactory.createEmptyBorder(TOP_PADDING, 0, BOTTOM_PADDING, 0)); popupMenu = new JPopupMenu(); popupMenu.add(new RevertToDefaultAction()); add(this.label); add(Box.createHorizontalStrut(10)); add(this.control); add(Box.createHorizontalGlue()); // Compensate for the popup button. add(Box.createHorizontalStrut(30)); setBorder(Theme.PARAMETER_ROW_BORDER); } public Port getPort() { Node activeNode = document.getActiveNode(); if (activeNode == null) activeNode = document.getActiveNetwork(); return activeNode.getInput(portName); } @Override public Dimension getPreferredSize() { return new Dimension(400, control.getPreferredSize().height + TOP_PADDING + BOTTOM_PADDING); } //// Mouse listeners //// public void mouseClicked(MouseEvent e) { } public void mousePressed(MouseEvent e) { if (e.getX() < this.getWidth() - 20) return; popupMenu.show(this, this.getWidth() - 20, 20); } public void mouseReleased(MouseEvent e) { } public void mouseEntered(MouseEvent e) { } public void mouseExited(MouseEvent e) { } @Override protected void paintComponent(Graphics g) { // Height aligns to 30px high control, such as float, string, color, etc. g.drawImage(popupButtonImage, getWidth() - 20, 4, null); } //// Port context menu //// @Override public void setEnabled(boolean enabled) { super.setEnabled(enabled); control.setEnabled(enabled); label.setEnabled(enabled); } /** * A user clicked the expression editor button. Show the expression window. * * @param e the event */ public void actionPerformed(ActionEvent e) { // NodeBoxDocument doc = NodeBoxDocument.getCurrentDocument(); // if (doc == null) throw new RuntimeException("No current active document."); // ExpressionWindow window = new ExpressionWindow(port); // window.setLocationRelativeTo(this); // window.setVisible(true); // doc.addPortEditor(window); } //// Action classes //// private class RevertToDefaultAction extends AbstractAction { private RevertToDefaultAction() { putValue(Action.NAME, "Revert to Default"); } public void actionPerformed(ActionEvent e) { document.revertPortToDefault(portName); // Reverting to default could cause an expression to be set/cleared. // This triggers an valueChanged event, where we check if our expression field is // still up-to-date. } } }