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()) {
110 /* get number of maximum horizontal grid cells. */
111 int gridCellCount = 1;
112 for (int n = 2; n <= sinkCount; ++n) {
116 /* paint all components recursively. */
117 addControlled(pipeline.source(), 0, 0, gridCellCount, null);
121 * Displays the given component.
123 * @param controlledComponent
124 * The component to add this panel.
126 * The level at which to show the component (the source is level {@code 0})
128 * The position at which to display the component
130 * The width of the component in grid cells
132 private void addControlled(final ControlledComponent controlledComponent, int level, int position, int width, ControlledComponent parentComponent) {
133 /* create a GUI component that displays the component. */
134 JPanel componentPanel = createComponentPanel(controlledComponent, parentComponent);
135 componentPanel.addMouseListener(new MouseAdapter() {
138 public void mouseEntered(MouseEvent mouseEvent) {
139 for (ComponentHoverListener componentHoverListener : componentHoverListeners.getListeners(ComponentHoverListener.class)) {
140 componentHoverListener.componentEntered(controlledComponent);
145 /* show component. */
146 add(componentPanel, new GridBagConstraints(position, level, width, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
148 /* if the component does not have connected sinks, exit here. */
149 if (!(controlledComponent instanceof Source)) {
150 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));
154 /* iterate over the component’s sinks. */
155 Collection<Sink> sinks = pipeline.sinks((Source) controlledComponent);
156 if (!sinks.isEmpty()) {
157 int sinkWidth = width / sinks.size();
159 for (Sink connectedSink : sinks) {
160 /* distribute all sinks evenly below this source. */
161 addControlled(connectedSink, level + 1, position + sinkIndex * sinkWidth, sinkWidth, controlledComponent);
168 * Creates a panel displaying a single component.
170 * @param controlledComponent
171 * The component to display
172 * @return The created panel
174 private static JPanel createComponentPanel(final ControlledComponent controlledComponent, final ControlledComponent parentComponent) {
175 JPanel componentPanel = new JPanel(new BorderLayout(12, 12));
176 componentPanel.setBorder(createCompoundBorder(createEtchedBorder(), createEmptyBorder(0, 4, 0, 3)));
177 componentPanel.add(new JLabel(controlledComponent.name()), BorderLayout.WEST);
178 final JLabel titleLabel = new JLabel(controlledComponent.metadata().title());
179 titleLabel.setFont(titleLabel.getFont().deriveFont(titleLabel.getFont().getSize2D() * 0.8f));
180 componentPanel.add(titleLabel, BorderLayout.EAST);
181 if (parentComponent != null) {
182 titleLabel.setVisible(!parentComponent.metadata().title().equals(controlledComponent.metadata().title()));
183 parentComponent.addMetadataListener(new MetadataListener() {
186 public void metadataUpdated(ControlledComponent component, Metadata metadata) {
187 titleLabel.setText(metadata.title());
188 titleLabel.setVisible(!controlledComponent.metadata().title().equals(metadata.title()));
192 controlledComponent.addMetadataListener(new MetadataListener() {
195 public void metadataUpdated(ControlledComponent component, Metadata metadata) {
196 titleLabel.setText(metadata.title());
197 titleLabel.setVisible((parentComponent == null) || !parentComponent.metadata().title().equals(metadata.title()));
200 return componentPanel;
204 * Interface for objects that want to be notified if the user moves the mouse
205 * cursor over a controlled component.
207 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
209 public static interface ComponentHoverListener extends EventListener {
212 * Notifies the listener that the mouse is now over the given controlled
215 * @param controlledComponent
216 * The controlled component now under the mouse
218 void componentEntered(ControlledComponent controlledComponent);