Rename “Controlled” to “ControlledComponent.”
[sonitus.git] / src / main / java / net / pterodactylus / sonitus / gui / PipelinePanel.java
1 /*
2  * Sonitus - PipelinePanel.java - Copyright © 2013 David Roden
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 package net.pterodactylus.sonitus.gui;
19
20 import static javax.swing.BorderFactory.createEtchedBorder;
21
22 import java.awt.GridBagConstraints;
23 import java.awt.GridBagLayout;
24 import java.awt.Insets;
25 import java.awt.event.MouseAdapter;
26 import java.awt.event.MouseEvent;
27 import java.util.Collection;
28 import java.util.EventListener;
29 import java.util.List;
30 import javax.swing.JLabel;
31 import javax.swing.JPanel;
32 import javax.swing.event.EventListenerList;
33
34 import net.pterodactylus.sonitus.data.ControlledComponent;
35 import net.pterodactylus.sonitus.data.Filter;
36 import net.pterodactylus.sonitus.data.Pipeline;
37 import net.pterodactylus.sonitus.data.Sink;
38 import net.pterodactylus.sonitus.data.Source;
39
40 import com.google.common.collect.Lists;
41
42 /**
43  * {@link JPanel} that displays all components of a {@link Pipeline}.
44  *
45  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
46  */
47 public class PipelinePanel extends JPanel {
48
49         /** The pipeline being displayed. */
50         private final Pipeline pipeline;
51
52         /** The component hover listeners. */
53         private final EventListenerList componentHoverListeners = new EventListenerList();
54
55         /**
56          * Creates a new pipeline panel displaying the given pipeline.
57          *
58          * @param pipeline
59          *              The pipeline to display
60          */
61         public PipelinePanel(Pipeline pipeline) {
62                 super(new GridBagLayout());
63                 this.pipeline = pipeline;
64                 updatePanel();
65         }
66
67         //
68         // LISTENER MANAGEMENT
69         //
70
71         /**
72          * Adds the given component hover listener to this panel.
73          *
74          * @param componentHoverListener
75          *              The component hover listener to add
76          */
77         public void addComponentHoverListener(ComponentHoverListener componentHoverListener) {
78                 componentHoverListeners.add(ComponentHoverListener.class, componentHoverListener);
79         }
80
81         //
82         // PRIVATE METHODS
83         //
84
85         /** Update the panel. Needs to be called when the pipeline is changed. */
86         private void updatePanel() {
87                 /* clear everything. */
88                 removeAll();
89
90                 /* count all sinks. */
91                 int sinkCount = 0;
92                 List<Source> sources = Lists.newArrayList(pipeline.source());
93                 while (!sources.isEmpty()) {
94                         Collection<Sink> sinks = pipeline.sinks(sources.remove(0));
95                         for (Sink sink : sinks) {
96                                 /* only count real sinks, everything else is filter. */
97                                 if (sink instanceof Filter) {
98                                         sources.add((Filter) sink);
99                                 } else {
100                                         sinkCount++;
101                                 }
102                         }
103                 }
104
105                 /* get number of maximum horizontal grid cells. */
106                 int gridCellCount = 1;
107                 for (int n = 2; n <= sinkCount; ++n) {
108                         gridCellCount *= n;
109                 }
110
111                 /* paint all components recursively. */
112                 addControlled(pipeline.source(), 0, 0, gridCellCount);
113         }
114
115         /**
116          * Displays the given component.
117          *
118          * @param controlledComponent
119          *              The component to add this panel.
120          * @param level
121          *              The level at which to show the component (the source is level {@code 0})
122          * @param position
123          *              The position at which to display the component
124          * @param width
125          *              The width of the component in grid cells
126          */
127         private void addControlled(final ControlledComponent controlledComponent, int level, int position, int width) {
128                 /* create a GUI component that displays the component. */
129                 JLabel sourceLabel = new JLabel(controlledComponent.name());
130                 sourceLabel.setBorder(createEtchedBorder());
131                 sourceLabel.addMouseListener(new MouseAdapter() {
132
133                         @Override
134                         public void mouseEntered(MouseEvent mouseEvent) {
135                                 for (ComponentHoverListener componentHoverListener : componentHoverListeners.getListeners(ComponentHoverListener.class)) {
136                                         componentHoverListener.componentEntered(controlledComponent);
137                                 }
138                         }
139                 });
140
141                 /* show component. */
142                 add(sourceLabel, new GridBagConstraints(position, level, width, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
143
144                 /* if the component does not have connected sinks, exit here. */
145                 if (!(controlledComponent instanceof Source)) {
146                         return;
147                 }
148
149                 /* iterate over the component’s sinks. */
150                 Collection<Sink> sinks = pipeline.sinks((Source) controlledComponent);
151                 int sinkWidth = width / sinks.size();
152                 int sinkIndex = 0;
153                 for (Sink connectedSink : sinks) {
154                         /* distribute all sinks evenly below this source. */
155                         addControlled(connectedSink, level + 1, position + sinkIndex * sinkWidth, sinkWidth);
156                         sinkIndex++;
157                 }
158         }
159
160         /**
161          * Interface for objects that want to be notified if the user moves the mouse
162          * cursor over a controlled component.
163          *
164          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
165          */
166         public static interface ComponentHoverListener extends EventListener {
167
168                 /**
169                  * Notifies the listener that the mouse is now over the given controlled
170                  * component.
171                  *
172                  * @param controlledComponent
173                  *              The controlled component now under the mouse
174                  */
175                 void componentEntered(ControlledComponent controlledComponent);
176
177         }
178
179 }