/***************************************************
*
* cismet GmbH, Saarbruecken, Germany
*
* ... and it just works.
*
****************************************************/
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package de.cismet.cismap.commons.gui.layerwidget;
import org.apache.log4j.Logger;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.GridBagLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.event.TreeModelListener;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import de.cismet.cismap.commons.RetrievalServiceLayer;
import de.cismet.cismap.commons.ServiceLayer;
import de.cismet.cismap.commons.featureservice.AbstractFeatureService;
import de.cismet.cismap.commons.featureservice.H2FeatureService;
import de.cismet.cismap.commons.featureservice.ShapeFileFeatureService;
import de.cismet.cismap.commons.interaction.CismapBroker;
import de.cismet.cismap.commons.rasterservice.MapService;
/**
* DOCUMENT ME!
*
* @author therter
* @version $Revision$, $Date$
*/
public class ReadOnlyThemeLayerWidget extends javax.swing.JPanel { // implements
//~ Instance fields --------------------------------------------------------
private Logger log = Logger.getLogger(ThemeLayerWidget.class);
private ActiveLayerModel layerModel;
private TreeModel model;
private Map<Object, Boolean> serviceStateMap = new HashMap<Object, Boolean>();
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTree tree;
// End of variables declaration//GEN-END:variables
//~ Constructors -----------------------------------------------------------
/**
* Creates new form ThemeLayerWidget.
*/
public ReadOnlyThemeLayerWidget() {
initComponents();
tree.setCellRenderer(new CheckBoxNodeRenderer());
tree.setEditable(false);
}
//~ Methods ----------------------------------------------------------------
/**
* DOCUMENT ME!
*
* @param mappingModel DOCUMENT ME!
*/
public void setMappingModel(final ActiveLayerModel mappingModel) {
setMappingModel(mappingModel, null);
}
/**
* DOCUMENT ME!
*
* @param mappingModel DOCUMENT ME!
* @param classes DOCUMENT ME!
*/
public void setMappingModel(final ActiveLayerModel mappingModel, final Class[] classes) {
layerModel = mappingModel;
model = new ActiveLayerModelWrapperWithoutProgress(layerModel);
if (classes != null) {
model = new FilterTreeModelWrapper(model, classes);
}
tree.setModel(model);
tree.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(final MouseEvent e) {
if (!e.isPopupTrigger() && (e.getClickCount() == 1)) {
final int x = e.getX();
final int y = e.getY();
// is click over the combobox?
final TreePath tp = tree.getPathForLocation(x, y);
if (tp != null) {
final int pathCount = tp.getPathCount() - 1;
final int minX = pathCount * 20;
final int maxX = minX + 15;
if ((x >= minX) && (x <= maxX)) {
// click is over the checkbox
changeState(tp.getLastPathComponent(), !getState(tp.getLastPathComponent()));
tree.repaint();
}
}
}
}
});
}
/**
* DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
public List<Object> getSelectedServices() {
final List<Object> result = new ArrayList<Object>();
for (final Object key : serviceStateMap.keySet()) {
final Boolean selected = serviceStateMap.get(key);
if (selected.booleanValue()) {
result.add(key);
}
}
return result;
}
/**
* changes the visibility of the given object.
*
* @param objectToChange either the root layer, a LayerCollection or a ServiceLayer
* @param newState DOCUMENT ME!
*/
private void changeState(final Object objectToChange, final boolean newState) {
if (objectToChange.equals(model.getRoot())) {
for (int i = 0; i < model.getChildCount(model.getRoot()); ++i) {
changeState(model.getChild(model.getRoot(), i), newState);
}
} else if (objectToChange instanceof LayerCollection) {
final LayerCollection lc = (LayerCollection)objectToChange;
for (int i = 0; i < lc.size(); ++i) {
changeState(lc.get(i), newState);
}
} else if (objectToChange instanceof ServiceLayer) {
final Boolean currentState = serviceStateMap.get(objectToChange);
if (currentState != null) {
serviceStateMap.put(objectToChange, newState);
}
}
}
/**
* DOCUMENT ME!
*
* @param value DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private boolean getState(final Object value) {
if (value.equals(model.getRoot())) {
boolean isSelected = true;
for (int i = 0; i < model.getChildCount(model.getRoot()); ++i) {
if (!getState(model.getChild(model.getRoot(), i))) {
isSelected = false;
}
}
return isSelected;
} else if (value instanceof LayerCollection) {
boolean isSelected = true;
final LayerCollection lc = (LayerCollection)value;
for (int i = 0; i < lc.size(); ++i) {
if (!getState(lc.get(i))) {
isSelected = false;
}
}
return isSelected;
} else if (value instanceof ServiceLayer) {
Boolean state = serviceStateMap.get(value);
if (state == null) {
state = Boolean.FALSE;
serviceStateMap.put(value, state);
}
return state.booleanValue();
}
return false;
}
/**
* This method is called from within the constructor to initialize the form. WARNING: Do NOT modify this code. The
* content of this method is always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
final java.awt.GridBagConstraints gridBagConstraints;
jScrollPane1 = new javax.swing.JScrollPane();
tree = new javax.swing.JTree();
setLayout(new java.awt.GridBagLayout());
jScrollPane1.setViewportView(tree);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(jScrollPane1, gridBagConstraints);
} // </editor-fold>//GEN-END:initComponents
//~ Inner Classes ----------------------------------------------------------
/**
* DOCUMENT ME!
*
* @version $Revision$, $Date$
*/
class CheckBoxNodeRenderer extends ActiveLayerTreeCellRenderer {
//~ Instance fields ----------------------------------------------------
protected Color selectionBorderColor;
protected Color selectionForeground;
protected Color selectionBackground;
protected Color textForeground;
protected Color textBackground;
protected Boolean drawsFocusBorderAroundIcon;
protected Font fontValue;
//~ Constructors -------------------------------------------------------
/**
* Creates a new CheckBoxNodeRenderer object.
*/
public CheckBoxNodeRenderer() {
fontValue = UIManager.getFont("Tree.font");
drawsFocusBorderAroundIcon = (Boolean)UIManager.get("Tree.drawsFocusBorderAroundIcon");
selectionBorderColor = UIManager.getColor("Tree.selectionBorderColor");
selectionForeground = UIManager.getColor("Tree.selectionForeground");
selectionBackground = UIManager.getColor("Tree.selectionBackground");
textForeground = UIManager.getColor("Tree.textForeground");
textBackground = UIManager.getColor("Tree.textBackground");
}
//~ Methods ------------------------------------------------------------
@Override
public Component getTreeCellRendererComponent(final JTree tree,
final Object value,
final boolean selected,
final boolean expanded,
final boolean leaf,
final int row,
final boolean hasFocus) {
final JLabel lab;
synchronized (ReadOnlyThemeLayerWidget.this.getTreeLock()) {
final Component ret = super.getTreeCellRendererComponent(
tree,
value,
selected,
expanded,
leaf,
row,
hasFocus);
final JLabel retLab = (JLabel)ret;
lab = new JLabel(retLab.getText(), retLab.getIcon(), retLab.getHorizontalAlignment());
}
final JPanel pan = new JPanel();
final JCheckBox leafRenderer = new JCheckBox();
pan.setLayout(new GridBagLayout());
if (fontValue != null) {
leafRenderer.setFont(fontValue);
}
leafRenderer.setFocusPainted((drawsFocusBorderAroundIcon != null)
&& (drawsFocusBorderAroundIcon.booleanValue()));
leafRenderer.setEnabled(tree.isEnabled());
if (selected) {
leafRenderer.setForeground(selectionForeground);
leafRenderer.setBackground(selectionBackground);
pan.setForeground(selectionForeground);
pan.setBackground(selectionBackground);
} else {
leafRenderer.setForeground(textForeground);
leafRenderer.setBackground(textBackground);
pan.setForeground(textForeground);
pan.setBackground(textBackground);
}
if ((value instanceof ShapeFileFeatureService) && ((ShapeFileFeatureService)value).isFileNotFound()) {
lab.setForeground(Color.GRAY);
} else if ((value instanceof H2FeatureService) && ((H2FeatureService)value).isTableNotFound()) {
lab.setForeground(Color.GRAY);
}
leafRenderer.setSelected(getState(value));
pan.add(leafRenderer);
pan.add(lab);
pan.doLayout();
pan.repaint();
return pan;
}
}
/**
* Wraps an TreeModel and shows only child nodes with the type of the supported classes.
*
* @version $Revision$, $Date$
*/
private class FilterTreeModelWrapper implements TreeModel {
//~ Instance fields ----------------------------------------------------
private TreeModel model;
private Class[] supportedClasses;
//~ Constructors -------------------------------------------------------
/**
* Creates a new FilterTreeModelWrapper object.
*
* @param model DOCUMENT ME!
* @param supportedClasses DOCUMENT ME!
*/
public FilterTreeModelWrapper(final TreeModel model, final Class[] supportedClasses) {
this.model = model;
this.supportedClasses = supportedClasses;
}
//~ Methods ------------------------------------------------------------
@Override
public Object getRoot() {
return model.getRoot();
}
@Override
public Object getChild(final Object parent, final int index) {
int counter = 0;
for (int i = 0; i < model.getChildCount(parent); ++i) {
final Object child = model.getChild(parent, i);
if (isInstanceOfSupportedClass(child)) {
if (counter == index) {
return child;
} else {
++counter;
}
}
}
// If this code is reached, either this method is wrong or the getChildCount(Object) method
log.error("No child found. This should never happen.");
return null;
}
@Override
public int getChildCount(final Object parent) {
int counter = 0;
for (int i = 0; i < model.getChildCount(parent); ++i) {
if (isInstanceOfSupportedClass(model.getChild(parent, i))) {
++counter;
}
}
return counter;
}
@Override
public boolean isLeaf(final Object node) {
return model.isLeaf(node);
}
@Override
public void valueForPathChanged(final TreePath path, final Object newValue) {
model.valueForPathChanged(path, newValue);
}
@Override
public int getIndexOfChild(final Object parent, final Object child) {
int counter = 0;
if ((parent == null) || (child == null)) {
return -1;
}
for (int i = 0; i < model.getChildCount(parent); ++i) {
final Object originChild = model.getChild(parent, i);
if (isInstanceOfSupportedClass(originChild)) {
if (child == originChild) {
return counter;
} else {
++counter;
}
}
}
return -1;
}
@Override
public void addTreeModelListener(final TreeModelListener l) {
model.addTreeModelListener(l);
}
@Override
public void removeTreeModelListener(final TreeModelListener l) {
model.removeTreeModelListener(l);
}
/**
* DOCUMENT ME!
*
* @param o DOCUMENT ME!
*
* @return DOCUMENT ME!
*/
private boolean isInstanceOfSupportedClass(final Object o) {
for (final Class c : supportedClasses) {
if (c.isAssignableFrom(o.getClass())) {
return true;
}
}
return false;
}
}
}