Print at Dec 16, 2025, 3:49:01 AM

Posted by byDMA at Jun 19, 2024, 6:33:07 PM
Re: Generate roof plugin
Hi Dorin, thank you, the grids are very well.

try this and SEND me privat the compiled plugin for test.
The width of the edge panel we create with list width, not spaces.

package generateroof;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.GridBagConstraints;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.HashSet;
import java.util.ResourceBundle;
import java.util.Set;

import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JSlider;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.SpinnerNumberModel;
import javax.swing.SwingUtilities;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import com.eteks.sweethome3d.model.HomeMaterial;
import com.eteks.sweethome3d.model.UserPreferences;
import com.eteks.sweethome3d.swing.AutoCommitSpinner;
import com.eteks.sweethome3d.swing.ModelPreviewComponent;
import com.eteks.sweethome3d.swing.NullableCheckBox;
import com.eteks.sweethome3d.swing.NullableSpinner;
import com.eteks.sweethome3d.swing.SwingTools;
import com.eteks.sweethome3d.swing.NullableSpinner.NullableSpinnerLengthModel;
import com.eteks.sweethome3d.tools.OperatingSystem;
import com.eteks.sweethome3d.viewcontroller.HomeView;

public class GenrateRoofPluginView {
private final GenerateRoofPlugin plugin;

public GenrateRoofPluginView(GenerateRoofPlugin generateRoofPlugin) {
this.plugin = generateRoofPlugin;
}

float resolutionScale = SwingTools.getResolutionScale();
ModelPreviewComponent roofPreview;
ChangeListener commonListener;
ListSelectionListener edgesListener;
NullableCheckBox invisibilityCheck, showHome, showTexture, reverseFaces;
JSlider faceAngleSlider;
JRadioButton verticalBorder, orthoBorder;
ImageIcon vertIcon, vertSelIcon, orthoIcon, orthoSelIcon;
ButtonGroup borderGroupBtn;
JButton resetButton, aboutButton;
NullableSpinnerLengthModel thickModel, roofElevationModel;
NullableSpinner thick, roofElevation;
JSpinner fineSlope;
JList<SlopedEdge> edgesList;
JScrollPane scrollEdgesPane;
JTextField creator;
JLabel edgesLabel, invisibilityLabel;
JPanel invisibilityPane, slopePane, bordersPane, finePane, thickPane, elevationPane, creatorPane;

/**
* Creates and layout the components shown in the dialog box of this plug-in.
*
* @author Daniels118
*
* @param homeView
* @param resource
*/
protected void createComponents(final HomeView homeView, final ResourceBundle resource) {
UserPreferences preferences = plugin.getUserPreferences();
String unitName = preferences.getLengthUnit().getName();

// Create a common change listener for some components.
commonListener = new ChangeListener() {
@Override
public void stateChanged(ChangeEvent ev) {
if (ev.getSource().equals(showHome)) {
if (showHome.getValue()) {
plugin.homeItems.removeAllChildren();
plugin.homeItems = plugin.createHomeNode(plugin.getHome());
}
plugin.refreshRoof(homeView, resource);

} else if (ev.getSource().equals(faceAngleSlider)) {
int slope = faceAngleSlider.getValue();
int maxAngle = faceAngleSlider.getMaximum() - 1;
// Limit the slope in range 1 to 179 degrees
slope = slope > 1 ? slope : 1;
slope = slope < maxAngle ? slope : maxAngle;
// Update the angle for the selected faces
for (SlopedEdge edge : edgesList.getSelectedValuesList())
edge.setSlope(slope);
fineSlope.removeChangeListener(commonListener);
fineSlope.setValue(0.0);
fineSlope.addChangeListener(commonListener);
if (!faceAngleSlider.getValueIsAdjusting() && faceAngleSlider.hasFocus()) {
edgesList.updateUI();
plugin.updateSelectedFacesFromList(homeView, resource);
}
} else if (ev.getSource().equals(fineSlope)) {
// Add the fine tuning to edge slope
float fineSlopeVal = ((Number) fineSlope.getValue()).floatValue();
float angle = faceAngleSlider.getValue();
float maxAngle = faceAngleSlider.getMaximum() - 1;
float slope = angle + fineSlopeVal;
slope = slope > 1f ? slope : 1.0f;
slope = slope < maxAngle ? slope : maxAngle;
// Update the angle for the selected faces
for (SlopedEdge edge : edgesList.getSelectedValuesList())
edge.setSlope(slope);
edgesList.updateUI();
plugin.updateSelectedFacesFromList(homeView, resource);
} else if (ev.getSource().equals(invisibilityCheck)) {
if (invisibilityCheck.getValue() != null) {
boolean visible = !invisibilityCheck.getValue();
// Update the face visibility
for (SlopedEdge edge : edgesList.getSelectedValuesList())
edge.setFaceVisible(visible);
edgesList.updateUI();
plugin.updateSelectedFacesFromList(homeView, resource);
}
} else {
plugin.refreshRoof(homeView, resource);
}
}
};

// Create edges list selection listener.
edgesListener = new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent ev) {
int edgeSelection = edgesList.getSelectedIndices().length;
// Limit selection to have at least one face with slope < 90 deg
int maxAngle = edgeSelection < plugin.allRoofEdges.length ? 180 : 76;
if (faceAngleSlider.getMaximum() != maxAngle)
faceAngleSlider.setMaximum(maxAngle);
SlopedEdge edge = edgesList.getSelectedValue();
if (edge != null) {
// Set angle
faceAngleSlider.removeChangeListener(commonListener);
faceAngleSlider.setValue((int) edge.getSlope());
faceAngleSlider.addChangeListener(commonListener);
// Set fine slope
fineSlope.removeChangeListener(commonListener);
fineSlope.setValue(edge.getSlope() - faceAngleSlider.getValue());
fineSlope.addChangeListener(commonListener);
}
if (!ev.getValueIsAdjusting() && edgesList.hasFocus())
plugin.updateSelectedFacesFromList(homeView, resource);
}
};
// Create preview
roofPreview = new ModelPreviewComponent(true, true, true, true);
roofPreview.setFocusable(false);
roofPreview.setPreferredSize(new Dimension((int) (800 * resolutionScale), (int) (600 * resolutionScale)));// @byDMA resizing window to 800x600
// Add a mouse listener to get selected faces
roofPreview.addMouseListener(new MouseAdapter() {
private Set<SlopedEdge> selectedEdges = new HashSet<SlopedEdge>();// Using a set avoids duplicates

@Override
public void mouseClicked(MouseEvent ev) {
HomeMaterial pickedMaterial = roofPreview.getPickedMaterial();
boolean sInterval = false;
if (pickedMaterial != null) {
String faceName = pickedMaterial.getName();
for (RoofFace face : plugin.faces) {
if (faceName.equals(face.getFaceName())) {
if ((ev.getModifiers() & Event.CTRL_MASK) != 0 || (ev.getModifiers() & Event.SHIFT_MASK) != 0) {
for (SlopedEdge sEdge : face.getGeneratingEdges()) {
if (!selectedEdges.add(sEdge))
selectedEdges.remove(sEdge);// Toggle selection
}
if ((ev.getModifiers() & Event.SHIFT_MASK) != 0)
sInterval = true;
} else {
selectedEdges.clear();
for (SlopedEdge sEdge : face.getGeneratingEdges())
selectedEdges.add(sEdge);
}
break;// There can be a single face matching the picked material
}
}
int[] edgeIndices = new int[selectedEdges.size()];
int i = 0;
for (SlopedEdge sEdge : selectedEdges)
edgeIndices[i++] = sEdge.getEdge();
edgesList.setSelectedIndices(edgeIndices);
int sMin = edgesList.getMinSelectionIndex();
int sMax = edgesList.getMaxSelectionIndex();
if (sInterval)
edgesList.addSelectionInterval(sMin, sMax);
if (i > 0)
edgesList.ensureIndexIsVisible(edgeIndices[i - 1]);
} else if ((ev.getModifiers() & Event.CTRL_MASK) == 0) {
edgesList.setSelectedIndices(new int[0]);// Deselect when click the background
}
edgesList.updateUI();
plugin.updateSelectedFacesFromList(homeView, resource);
}
});

// Create edges list
// String edgesLabel = resource.getString("edges.txt");// Edges: Slope
edgesList = new JList<SlopedEdge>();
edgesList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
edgesList.setListData(plugin.allRoofEdges);// Add edges to the list
edgesList.setCellRenderer(new EdgesCellRenderer());
edgesList.addListSelectionListener(edgesListener);
edgesList.setFixedCellWidth(140);// byDMA resize width of the List in the JScrollPane
// Put edges list into scroll panel
String edgesLab = resource.getString("edges.txt");// Edges: Slope
edgesLabel = new JLabel(edgesLab);// Edges: Slope
// scrollEdges = SwingTools.createScrollPane(edgesList);
scrollEdgesPane = new JScrollPane(edgesList);
if (OperatingSystem.isMacOSX()) {
Dimension preferredSize = scrollEdgesPane.getPreferredSize();
scrollEdgesPane.setPreferredSize(new Dimension(Math.min(200, preferredSize.width), preferredSize.height));
} else {

scrollEdgesPane.setBorder(new TitledBorder(edgesLabel.getText()));
}

// Create slope slider
String slopeSliderLabel = resource.getString("slope.txt");// Slope
faceAngleSlider = new JSlider(JSlider.VERTICAL, 0, 180, 40);
faceAngleSlider.setPaintTicks(true);
faceAngleSlider.setPaintLabels(true);
faceAngleSlider.setMajorTickSpacing(30);
faceAngleSlider.setMinorTickSpacing(5);
faceAngleSlider.setBorder(new TitledBorder(slopeSliderLabel));
faceAngleSlider.addChangeListener(commonListener);

// Create fine slope angle spinner
SpinnerNumberModel fineSlopeModel = new SpinnerNumberModel(.0, -0.9, 0.9, .1);
fineSlope = new AutoCommitSpinner(fineSlopeModel);
fineSlope.addChangeListener(commonListener);
// Fine angle panel
String fineSlopeShort = resource.getString("fineSlope.txt");// Fine slope
finePane = SwingTools.createTitledPanel(fineSlopeShort);
finePane.add(fineSlope);

// Create face invisibility check box
String invisibility = resource.getString("invisibility.txt");// Invisible
invisibilityCheck = new NullableCheckBox(invisibility);
invisibilityCheck.setNullable(false);
invisibilityCheck.setValue(false);
invisibilityCheck.addChangeListener(commonListener);
// Invisibility check panel
String faceVisible = resource.getString("face.txt");// Face
invisibilityLabel = new JLabel(faceVisible);
if (OperatingSystem.isMacOSX()) {
invisibilityPane = SwingTools.createTitledPanel(null);
} else {
invisibilityPane = SwingTools.createTitledPanel(invisibilityLabel.getText());
}
invisibilityPane.add(invisibilityCheck);

// Create roof border thickness spinner
String thickLong = String.format(resource.getString("thickness.txt"), unitName);// Roof thickness
thickModel = new NullableSpinnerLengthModel(preferences, 7.6f, 2.5f, 30.5f);
thick = new NullableSpinner(thickModel);
thick.setToolTipText(thickLong);
// Thickness panel
String thickShort = String.format(resource.getString("thick.txt"), unitName);// Thick
thickPane = SwingTools.createTitledPanel(thickShort);
thickPane.add(thick);
if (plugin.isRoofSelected()) {
String sThickness = plugin.currentRoof.getProperty(GenerateRoofPlugin.ROOF_THICKNESS);
if (sThickness != null) {
float currentThick = Float.valueOf(sThickness);
try {
thickModel.setLength(currentThick);
} catch (Exception e) {
/**
* @author Daniels118 No need to translate this message, only "hackers" have
* chances to see it
*/
plugin.getHomeController().getView().showError("Cannot retrieve thickModel: " + e.getMessage());
}
}
}
thickModel.addChangeListener(commonListener);

// Create roof elevation spinner
String elevationLong = String.format(resource.getString("roofelevation.txt"), unitName);// Elevate roof at needed position
roofElevationModel = new NullableSpinnerLengthModel(preferences, 0.0f, 0.0f, 9999f);
roofElevation = new NullableSpinner(roofElevationModel);
roofElevation.setToolTipText(elevationLong);
roofElevationModel.setLength(plugin.getRoofElevation(plugin.currentLevel));
roofElevationModel.addChangeListener(commonListener);
// Elevation panel
String elevationShort = String.format(resource.getString("elevation.txt"), unitName);// Elevation(%s)
elevationPane = SwingTools.createTitledPanel(elevationShort);
elevationPane.add(roofElevation);

// Create home visibility check box
String displayHome = resource.getString("displayHomeCheckBox.txt");// Show home
showHome = new NullableCheckBox(displayHome);
showHome.setValue(false);
showHome.addChangeListener(commonListener);

// Create texture visibility check box
String useTexture = resource.getString("displayTextureCheckBox.txt");// Use roof texture
showTexture = new NullableCheckBox(useTexture);
showTexture.setValue(false);
showTexture.setEnabled(plugin.isRoofSelected());
showTexture.addChangeListener(commonListener);

// Create reverse faces check box
String reverseFacesTxt = resource.getString("reverseFaces.txt");// Reverse faces
reverseFaces = new NullableCheckBox(reverseFacesTxt);
reverseFaces.setValue(false);
reverseFaces.addChangeListener(commonListener);

// Create creator field
String author = System.getProperty("user.name");
if (plugin.isRoofSelected())
author = plugin.currentRoof.getCreator();
creator = new JTextField(author);
creator.setEditable(true);
// @byDMA 2. store default height for TextField creator
int defaultHeight = creator.getSize().getHeight();
// @byDMA 3. set custom width and default height
creator.setSize(new Dimension(250, defaultHeight));

// Creator panel
String authorText = resource.getString("authorText.txt");// Creator
creatorPane = SwingTools.createTitledPanel(authorText);
creatorPane.add(creator);

// Create borders radio buttons
vertIcon = new ImageIcon(GenerateRoofPlugin.class.getResource("/generateroof/resources/vert.png"));
vertSelIcon = new ImageIcon(GenerateRoofPlugin.class.getResource("/generateroof/resources/vertSel.png"));
orthoIcon = new ImageIcon(GenerateRoofPlugin.class.getResource("/generateroof/resources/ortho.png"));
orthoSelIcon = new ImageIcon(GenerateRoofPlugin.class.getResource("/generateroof/resources/orthoSel.png"));
// Vertical border button
verticalBorder = new JRadioButton(vertIcon, true);// default selected
verticalBorder.setActionCommand("vertical");
verticalBorder.setSelectedIcon(vertSelIcon);
// Orthogonal border button
orthoBorder = new JRadioButton(orthoIcon);
orthoBorder.setActionCommand("orthoUp");
orthoBorder.setSelectedIcon(orthoSelIcon);
// Group border direction buttons
borderGroupBtn = new ButtonGroup();
borderGroupBtn.add(verticalBorder);
borderGroupBtn.add(orthoBorder);
// Border orientation panel
String borders = resource.getString("borderText.txt");// Border
bordersPane = SwingTools.createTitledPanel(borders);
bordersPane.add(verticalBorder, 0);
bordersPane.add(orthoBorder, 1);
// Set initial border
if (plugin.isRoofSelected()) {
String dirThick = plugin.getSelectedRoof().getProperty(GenerateRoofPlugin.ROOF_THICKNESS_DIRECTION);
if (dirThick != null) {
try {
if (orthoBorder.getActionCommand().equals(dirThick))
borderGroupBtn.setSelected(orthoBorder.getModel(), true);
} catch (Exception e) {
/**
* @author Daniels118 No need to translate this message, only "hackers" have
* chances to see it
*/
plugin.getHomeController().getView().showError("Cannot retrieve direction: " + e.getMessage());
}
}
}
// Create a listener for the border buttons.
ActionListener borderActionListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
AbstractButton aButton = (AbstractButton) actionEvent.getSource();
if (aButton.isSelected())
plugin.refreshRoof(homeView, resource);
}
};
// Register a listener for the borders buttons.
verticalBorder.addActionListener(borderActionListener);
orthoBorder.addActionListener(borderActionListener);

// Create reset button
resetButton = new JButton(new AbstractAction(resource.getString("resetButton.txt")) {
private static final long serialVersionUID = 1L;

public void actionPerformed(ActionEvent ev) {
faceAngleSlider.setValue(40);
fineSlope.setValue(0.0);
thickModel.setLength(7.6f);
edgesList.setSelectedIndices(new int[0]);
invisibilityCheck.setNullable(false);
invisibilityCheck.setValue(false);
invisibilityCheck.setValue(false);
// Reset the angle for all faces to 40 deg
int slope = faceAngleSlider.getValue();
for (int i = 0; i < plugin.allRoofEdges.length; i++) {
plugin.allRoofEdges.setSlope(slope);
plugin.allRoofEdges.setFaceVisible(true);
}
// Reset the author
String author = System.getProperty("user.name");
creator.setText(author);
// Reset the borders
borderGroupBtn.setSelected(verticalBorder.getModel(), true);
edgesList.updateUI();
plugin.updateSelectedFacesFromList(homeView, resource);
}
});
resetButton.setToolTipText(resource.getString("resetButtonInf.txt"));

// About button
aboutButton = new JButton(new AbstractAction(resource.getString("aboutButton.txt")) {
private static final long serialVersionUID = 1L;

@Override
public void actionPerformed(ActionEvent ev) {
HTMLViewer viewer = new HTMLViewer();
viewer.addErrorListener(new HTMLViewer.ErrorListener() {
@Override
public void onError(Exception e) {
e.printStackTrace();
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
try {
viewer.setPage(GenerateRoofPlugin.class.getResource("/generateroof/resources/about.html"));
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
}
});
try {
String lang = resource.getLocale().getLanguage();
String version = plugin.getVersion();
String url = "https://daniels118.altervista.org/sh3d/roofgenerator/about/" + "?lang=" + lang + "&ver="
+ URLEncoder.encode(version, "UTF8");
viewer.setPage(url);
} catch (IOException e) {
e.printStackTrace();
}
JScrollPane scrollPane = new JScrollPane(viewer);
JOptionPane helpPanel = new JOptionPane(scrollPane, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION);
JDialog aboutDialog = helpPanel.createDialog((JComponent) homeView, resource.getString("ROOF_GEN.title"));
aboutDialog.applyComponentOrientation(((JComponent) homeView).getComponentOrientation());
aboutDialog.setLocationByPlatform(true);
aboutDialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
aboutDialog.setResizable(true);
aboutDialog.setMinimumSize(new Dimension(600, 700));
aboutDialog.setVisible(true);
}
});
//aboutButton.setBackground(Color.green); //standard button, not green
aboutButton.setToolTipText(resource.getString("aboutButton_Warn.txt"));
}

/**
* Layout user interface
* @author Daniels118 & byDMA
*/
protected JPanel createUI() {
JPanel roofPanel = SwingTools.createTitledPanel(null);
roofPanel.setBorder(null);
// Column 0~3 Row 0~9
roofPanel.add(roofPreview,
new GridBagConstraints(0, 0, 4, 9, 1, 1, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
// Column 4 Row 1
if (OperatingSystem.isMacOSX()) {
roofPanel.add(edgesLabel,
new GridBagConstraints(4, 0, 2, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.NONE, new Insets(0, 10, 0, 0), 0, 0));
}
// Column 4+5 Row 0~7
roofPanel.add(scrollEdgesPane,
new GridBagConstraints(4, 0, 2, 7, 0, 0, GridBagConstraints.NORTH, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
// Column 4+5 Row 9
roofPanel.add(resetButton,
new GridBagConstraints(4, 9, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
if (OperatingSystem.isMacOSX()) {
roofPanel.add(invisibilityLabel,
new GridBagConstraints(5, 1, 1, 1, 0, 0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 10, 0, 0), 0, 0));
}
// Column 6+7 Row 0
roofPanel.add(invisibilityPane,
new GridBagConstraints(6, 0, 2, 1, 0, 0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 6+7 Row 1~5
roofPanel.add(faceAngleSlider,
new GridBagConstraints(6, 1, 2, 5, 0, 1, GridBagConstraints.LINE_START, GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
// Column 6+7 Row 6
roofPanel.add(finePane,
new GridBagConstraints(6, 6, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 4+5 Row 7
roofPanel.add(thickPane,
new GridBagConstraints(4, 7, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 6+7 Row 7
roofPanel.add(elevationPane,
new GridBagConstraints(6, 7, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 6+7 Row 8
roofPanel.add(creatorPane,
new GridBagConstraints(6, 8, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 0 Row 9
roofPanel.add(showHome,
new GridBagConstraints(0, 9, 1, 1, 1, 0, GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 2 Row 9
roofPanel.add(showTexture,
new GridBagConstraints(2, 9, 1, 1, 0, 0, GridBagConstraints.LINE_END, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 3 Row 9
roofPanel.add(reverseFaces,
new GridBagConstraints(3, 9, 1, 1, 0, 0, GridBagConstraints.LINE_END, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 4+5 Row 8
roofPanel.add(bordersPane,
new GridBagConstraints(4, 8, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
// Column 6+7 Row 9
roofPanel.add(aboutButton,
new GridBagConstraints(6, 9, 2, 1, 0, 0, GridBagConstraints.CENTER, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

// First refresh roof
edgesList.updateUI();
plugin.refreshRoof(plugin.homeView, plugin.resource);
return roofPanel;
}

private class EdgesCellRenderer extends DefaultListCellRenderer {

private static final long serialVersionUID = 1L;
final ImageIcon eyeCloseIcon = new ImageIcon(GenerateRoofPlugin.class.getResource("/generateroof/resources/closed-eye.png"));
final ImageIcon emptyIcon = new ImageIcon(GenerateRoofPlugin.class.getResource("/generateroof/resources/emptyIcon.png"));

@Override
public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
String s = value.toString();
setText(s);
boolean visible = plugin.allRoofEdges[index].isFaceVisible();
setIcon(!visible ? eyeCloseIcon : emptyIcon);

if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}

setEnabled(list.isEnabled());
setFont(list.getFont());
setOpaque(true);
return this;
}

}

}