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.List;
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.Filter;
39 import net.pterodactylus.sonitus.data.Metadata;
40 import net.pterodactylus.sonitus.data.MetadataListener;
41 import net.pterodactylus.sonitus.data.Pipeline;
42 import net.pterodactylus.sonitus.data.Sink;
43 import net.pterodactylus.sonitus.data.Source;
45 import com.google.common.collect.Lists;
48 * {@link JPanel} that displays all components of a {@link Pipeline}.
50 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
52 public class PipelinePanel extends JPanel {
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 List<Source> sources = Lists.newArrayList(pipeline.source());
98 while (!sources.isEmpty()) {
99 Collection<Sink> sinks = pipeline.sinks(sources.remove(0));
100 for (Sink sink : sinks) {
101 /* only count real sinks, everything else is filter. */
102 if (sink instanceof Filter) {
103 sources.add((Filter) sink);
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, level + 1, 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 int sinkWidth = width / sinks.size();
158 for (Sink connectedSink : sinks) {
159 /* distribute all sinks evenly below this source. */
160 addControlled(connectedSink, level + 1, position + sinkIndex * sinkWidth, sinkWidth, controlledComponent);
166 * Creates a panel displaying a single component.
168 * @param controlledComponent
169 * The component to display
170 * @return The created panel
172 private static JPanel createComponentPanel(final ControlledComponent controlledComponent, final ControlledComponent parentComponent) {
173 JPanel componentPanel = new JPanel(new BorderLayout(12, 12));
174 componentPanel.setBorder(createCompoundBorder(createEtchedBorder(), createEmptyBorder(0, 4, 0, 3)));
175 componentPanel.add(new JLabel(controlledComponent.name()), BorderLayout.WEST);
176 final JLabel titleLabel = new JLabel(controlledComponent.metadata().title());
177 titleLabel.setFont(titleLabel.getFont().deriveFont(titleLabel.getFont().getSize2D() * 0.8f));
178 componentPanel.add(titleLabel, BorderLayout.EAST);
179 if (parentComponent != null) {
180 titleLabel.setVisible(!parentComponent.metadata().title().equals(controlledComponent.metadata().title()));
181 parentComponent.addMetadataListener(new MetadataListener() {
184 public void metadataUpdated(ControlledComponent component, Metadata metadata) {
185 titleLabel.setText(metadata.title());
186 titleLabel.setVisible(!controlledComponent.metadata().title().equals(metadata.title()));
190 controlledComponent.addMetadataListener(new MetadataListener() {
193 public void metadataUpdated(ControlledComponent component, Metadata metadata) {
194 titleLabel.setText(metadata.title());
195 titleLabel.setVisible((parentComponent == null) || !parentComponent.metadata().title().equals(metadata.title()));
198 return componentPanel;
202 * Interface for objects that want to be notified if the user moves the mouse
203 * cursor over a controlled component.
205 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
207 public static interface ComponentHoverListener extends EventListener {
210 * Notifies the listener that the mouse is now over the given controlled
213 * @param controlledComponent
214 * The controlled component now under the mouse
216 void componentEntered(ControlledComponent controlledComponent);