2 * Sonitus - PipelinePanel.java - Copyright © 2013 David Roden
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 package net.pterodactylus.sonitus.gui;
20 import static javax.swing.BorderFactory.createCompoundBorder;
21 import static javax.swing.BorderFactory.createEmptyBorder;
22 import static javax.swing.BorderFactory.createEtchedBorder;
24 import java.awt.BorderLayout;
25 import java.awt.GridBagConstraints;
26 import java.awt.GridBagLayout;
27 import java.awt.Insets;
28 import java.awt.event.MouseAdapter;
29 import java.awt.event.MouseEvent;
30 import java.util.Collection;
31 import java.util.EventListener;
32 import java.util.logging.Logger;
33 import javax.swing.JLabel;
34 import javax.swing.JPanel;
35 import javax.swing.event.EventListenerList;
37 import net.pterodactylus.sonitus.data.ControlledComponent;
38 import net.pterodactylus.sonitus.data.Metadata;
39 import net.pterodactylus.sonitus.data.MetadataListener;
40 import net.pterodactylus.sonitus.data.Pipeline;
41 import net.pterodactylus.sonitus.data.Sink;
42 import net.pterodactylus.sonitus.data.Source;
45 * {@link JPanel} that displays all components of a {@link Pipeline}.
47 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
49 public class PipelinePanel extends JPanel {
52 private static final Logger logger = Logger.getLogger(PipelinePanel.class.getName());
54 /** The pipeline being displayed. */
55 private final Pipeline pipeline;
57 /** The component hover listeners. */
58 private final EventListenerList componentHoverListeners = new EventListenerList();
61 * Creates a new pipeline panel displaying the given pipeline.
64 * The pipeline to display
66 public PipelinePanel(Pipeline pipeline) {
67 super(new GridBagLayout());
68 this.pipeline = pipeline;
73 // LISTENER MANAGEMENT
77 * Adds the given component hover listener to this panel.
79 * @param componentHoverListener
80 * The component hover listener to add
82 public void addComponentHoverListener(ComponentHoverListener componentHoverListener) {
83 componentHoverListeners.add(ComponentHoverListener.class, componentHoverListener);
90 /** Update the panel. Needs to be called when the pipeline is changed. */
91 private void updatePanel() {
92 /* clear everything. */
95 /* count all sinks. */
97 for (ControlledComponent component : pipeline.components()) {
98 if (!(component instanceof Source)) {
99 logger.finest(String.format("%s is not a Source, skipping.", component.name()));
103 Collection<Sink> sinks = pipeline.sinks((Source) component);
104 logger.finest(String.format("%s has %d sinks: %s", component.name(), sinks.size(), sinks));
105 if (sinks.isEmpty()) {
109 System.out.println(sinkCount);
111 /* get number of maximum horizontal grid cells. */
112 int gridCellCount = 1;
113 for (int n = 2; n <= sinkCount; ++n) {
117 /* paint all components recursively. */
118 addControlled(pipeline.source(), 0, 0, gridCellCount, null);
122 * Displays the given component.
124 * @param controlledComponent
125 * The component to add this panel.
127 * The level at which to show the component (the source is level {@code 0})
129 * The position at which to display the component
131 * The width of the component in grid cells
133 private void addControlled(final ControlledComponent controlledComponent, int level, int position, int width, ControlledComponent parentComponent) {
134 /* create a GUI component that displays the component. */
135 JPanel componentPanel = createComponentPanel(controlledComponent, parentComponent);
136 componentPanel.addMouseListener(new MouseAdapter() {
139 public void mouseEntered(MouseEvent mouseEvent) {
140 for (ComponentHoverListener componentHoverListener : componentHoverListeners.getListeners(ComponentHoverListener.class)) {
141 componentHoverListener.componentEntered(controlledComponent);
146 /* show component. */
147 add(componentPanel, new GridBagConstraints(position, level, width, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
149 /* if the component does not have connected sinks, exit here. */
150 if (!(controlledComponent instanceof Source)) {
151 add(new JPanel(), new GridBagConstraints(position, 999, width, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
155 /* iterate over the component’s sinks. */
156 Collection<Sink> sinks = pipeline.sinks((Source) controlledComponent);
157 if (!sinks.isEmpty()) {
158 int sinkWidth = width / sinks.size();
160 for (Sink connectedSink : sinks) {
161 /* distribute all sinks evenly below this source. */
162 addControlled(connectedSink, level + 1, position + sinkIndex * sinkWidth, sinkWidth, controlledComponent);
169 * Creates a panel displaying a single component.
171 * @param controlledComponent
172 * The component to display
173 * @return The created panel
175 private static JPanel createComponentPanel(final ControlledComponent controlledComponent, final ControlledComponent parentComponent) {
176 JPanel componentPanel = new JPanel(new BorderLayout(12, 12));
177 componentPanel.setBorder(createCompoundBorder(createEtchedBorder(), createEmptyBorder(0, 4, 0, 3)));
178 componentPanel.add(new JLabel(controlledComponent.name()), BorderLayout.WEST);
179 final JLabel titleLabel = new JLabel(controlledComponent.metadata().title());
180 titleLabel.setFont(titleLabel.getFont().deriveFont(titleLabel.getFont().getSize2D() * 0.8f));
181 componentPanel.add(titleLabel, BorderLayout.EAST);
182 if (parentComponent != null) {
183 titleLabel.setVisible(!parentComponent.metadata().title().equals(controlledComponent.metadata().title()));
184 parentComponent.addMetadataListener(new MetadataListener() {
187 public void metadataUpdated(ControlledComponent component, Metadata metadata) {
188 titleLabel.setText(metadata.title());
189 titleLabel.setVisible(!controlledComponent.metadata().title().equals(metadata.title()));
193 controlledComponent.addMetadataListener(new MetadataListener() {
196 public void metadataUpdated(ControlledComponent component, Metadata metadata) {
197 titleLabel.setText(metadata.title());
198 titleLabel.setVisible((parentComponent == null) || !parentComponent.metadata().title().equals(metadata.title()));
201 return componentPanel;
205 * Interface for objects that want to be notified if the user moves the mouse
206 * cursor over a controlled component.
208 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
210 public static interface ComponentHoverListener extends EventListener {
213 * Notifies the listener that the mouse is now over the given controlled
216 * @param controlledComponent
217 * The controlled component now under the mouse
219 void componentEntered(ControlledComponent controlledComponent);