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.Color;
26 import java.awt.Component;
27 import java.awt.GridBagConstraints;
28 import java.awt.GridBagLayout;
29 import java.awt.Insets;
30 import java.awt.event.MouseAdapter;
31 import java.awt.event.MouseEvent;
32 import java.util.Collection;
33 import java.util.EventListener;
34 import java.util.logging.Logger;
35 import javax.swing.JComponent;
36 import javax.swing.JLabel;
37 import javax.swing.JPanel;
38 import javax.swing.UIManager;
39 import javax.swing.event.EventListenerList;
41 import net.pterodactylus.sonitus.data.Filter;
42 import net.pterodactylus.sonitus.data.Metadata;
43 import net.pterodactylus.sonitus.data.MetadataListener;
44 import net.pterodactylus.sonitus.data.Pipeline;
47 * {@link JPanel} that displays all filters of a {@link Pipeline}.
49 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
51 public class PipelinePanel extends JPanel {
54 private static final Logger logger = Logger.getLogger(PipelinePanel.class.getName());
56 /** The pipeline being displayed. */
57 private final Pipeline pipeline;
59 /** The filter selection listeners. */
60 private final EventListenerList filterSelectionListeners = new EventListenerList();
62 /** The currently selected filter. */
63 private JComponent selectedFilter;
66 * Creates a new pipeline panel displaying the given pipeline.
69 * The pipeline to display
71 public PipelinePanel(Pipeline pipeline) {
72 super(new GridBagLayout());
73 this.pipeline = pipeline;
78 // LISTENER MANAGEMENT
82 * Adds the given filter selection listener to this panel.
84 * @param filterSelectionListener
85 * The filter selection listener to add
87 public void addFilterSelectionListener(FilterSelectionListener filterSelectionListener) {
88 filterSelectionListeners.add(FilterSelectionListener.class, filterSelectionListener);
95 /** Update the panel. Needs to be called when the pipeline is changed. */
96 private void updatePanel() {
97 /* clear everything. */
100 /* count all filters. */
102 for (Filter filter : pipeline.filters()) {
103 Collection<Filter> sinks = pipeline.filters(filter);
104 logger.finest(String.format("%s has %d filters: %s", filter.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 filters recursively. */
117 addFilter(pipeline.source(), 0, 0, gridCellCount, null);
121 * Displays the given filter.
124 * The filter to add this panel.
126 * The level at which to show the filter (the source is level {@code 0})
128 * The position at which to display the filter
130 * The width of the filter in grid cells
132 private void addFilter(final Filter filter, int level, int position, int width, Filter parentFilter) {
133 /* create a GUI component that displays the filter. */
134 final JPanel filterPanel = createFilterPanel(filter, parentFilter);
135 filterPanel.addMouseListener(new MouseAdapter() {
138 public void mouseClicked(MouseEvent e) {
139 for (Component component : getComponents()) {
140 component.setBackground(UIManager.getColor("Panel.background"));
142 for (FilterSelectionListener filterSelectionListener : filterSelectionListeners.getListeners(FilterSelectionListener.class)) {
143 filterPanel.setBackground(Color.LIGHT_GRAY);
144 filterSelectionListener.filterSelected(filter);
146 selectedFilter = filterPanel;
150 public void mouseEntered(MouseEvent mouseEvent) {
151 if (filterPanel != selectedFilter) {
152 filterPanel.setBackground(Color.white);
157 public void mouseExited(MouseEvent mouseEvent) {
158 if (filterPanel != selectedFilter) {
159 filterPanel.setBackground(UIManager.getColor("Panel.background"));
165 add(filterPanel, new GridBagConstraints(position, level, width, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
167 /* if the filter does not have connected filters, exit here. */
168 Collection<Filter> sinks = pipeline.filters(filter);
169 if (sinks.isEmpty()) {
170 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));
174 /* iterate over the filter’s connected filters. */
175 if (!sinks.isEmpty()) {
176 int sinkWidth = width / sinks.size();
178 for (Filter connectedSink : sinks) {
179 /* distribute all filters evenly below this source. */
180 addFilter(connectedSink, level + 1, position + sinkIndex * sinkWidth, sinkWidth, filter);
187 * Creates a panel displaying a single filter.
190 * The filter to display
191 * @return The created panel
193 private static JPanel createFilterPanel(final Filter filter, final Filter parentFilter) {
194 JPanel filterPanel = new JPanel(new BorderLayout(12, 12));
195 filterPanel.setBorder(createCompoundBorder(createEtchedBorder(), createEmptyBorder(0, 4, 0, 3)));
196 filterPanel.add(new JLabel(filter.name()), BorderLayout.WEST);
197 final JLabel titleLabel = new JLabel(filter.metadata().fullTitle());
198 titleLabel.setFont(titleLabel.getFont().deriveFont(titleLabel.getFont().getSize2D() * 0.8f));
199 filterPanel.add(titleLabel, BorderLayout.EAST);
200 if (parentFilter != null) {
201 titleLabel.setVisible(!parentFilter.metadata().fullTitle().equals(filter.metadata().fullTitle()));
203 filter.addMetadataListener(new MetadataListener() {
206 public void metadataUpdated(Filter filter, Metadata metadata) {
207 titleLabel.setText(metadata.fullTitle());
208 titleLabel.setVisible((parentFilter == null) || !parentFilter.metadata().fullTitle().equals(metadata.fullTitle()));
215 * Interface for objects that want to be notified if the user moves the mouse
216 * cursor over a filter.
218 * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
220 public static interface FilterSelectionListener extends EventListener {
223 * Notifies the listener that the mouse is now over the given filter.
226 * The filter now under the mouse
228 void filterSelected(Filter filter);