//$HeadURL$
/*---------------- FILE HEADER ------------------------------------------
This file is part of deegree.
Copyright (C) 2001-2008 by:
Department of Geography, University of Bonn
http://www.giub.uni-bonn.de/deegree/
lat/lon GmbH
http://www.lat-lon.de
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Contact:
Andreas Poth
lat/lon GmbH
Aennchenstr. 19
53177 Bonn
Germany
E-Mail: poth@lat-lon.de
Prof. Dr. Klaus Greve
Department of Geography
University of Bonn
Meckenheimer Allee 166
53115 Bonn
Germany
E-Mail: greve@giub.uni-bonn.de
---------------------------------------------------------------------------*/
package org.deegree.igeo.views.swing.layerlist;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.List;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextPane;
import javax.swing.JTree;
import javax.swing.text.JTextComponent;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import org.deegree.igeo.ApplicationContainer;
import org.deegree.igeo.ChangeListener;
import org.deegree.igeo.ValueChangedEvent;
import org.deegree.igeo.commands.ChangeMapModelEntryStateCommand;
import org.deegree.igeo.commands.ChangeMapModelEntryStateCommand.MAPMODELENTRYSTATE;
import org.deegree.igeo.config.ViewFormType;
import org.deegree.igeo.i18n.Messages;
import org.deegree.igeo.mapmodel.Layer;
import org.deegree.igeo.mapmodel.LayerGroup;
import org.deegree.igeo.mapmodel.MapModel;
import org.deegree.igeo.mapmodel.MapModelChangedEvent;
import org.deegree.igeo.mapmodel.MapModelChangedEvent.CHANGE_TYPE;
import org.deegree.igeo.mapmodel.MapModelEntry;
import org.deegree.igeo.mapmodel.SystemLayer;
import org.deegree.igeo.views.swing.DefaultPanel;
import org.deegree.igeo.views.swing.layerlist.CheckNode.NODE_TYPE;
import org.deegree.kernel.Command;
/**
*
* The <code></code> class TODO add class documentation here.
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
*
* @author last edited by: $Author$
*
* @version $Revision$, $Date$
*
*/
public class LayerTreePanel extends DefaultPanel {
private static final long serialVersionUID = -4578390323009236537L;
private JSplitPane splPane;
private JTextComponent taDescription;
private JLabel lbLayer;
private JPanel pnInfo;
private JScrollPane scTree;
private DnDJTree tree;
private MapModel mapModel;
private ApplicationContainer<Container> appContainer;
private MapModelChangeListener mmCL = new MapModelChangeListener();
/*
* (non-Javadoc)
*
* @see org.deegree.igeo.views.IView#init(org.deegree.client.presenter.state.ComponentStateAdapter,
* org.deegree.client.configuration.ViewForm)
*/
public void init( ViewFormType viewForm )
throws Exception {
this.appContainer = this.owner.getApplicationContainer();
this.mapModel = appContainer.getMapModel( null );
this.mapModel.addChangeListener( mmCL );
initGUI();
createTreeView();
}
@Override
public void update() {
super.update();
MapModel tmp = appContainer.getMapModel( null );
if ( this.mapModel == null || !tmp.equals( this.mapModel ) ) {
this.mapModel = tmp;
this.mapModel.removeChangeListener( mmCL );
this.mapModel.addChangeListener( mmCL );
initGUI();
createTreeView();
}
}
private void createTreeView() {
DefaultMutableTreeNode root = (DefaultMutableTreeNode) tree.getModel().getRoot();
while ( root.getChildCount() > 0 ) {
root.remove( 0 );
}
List<LayerGroup> list = mapModel.getLayerGroups();
for ( LayerGroup node : list ) {
addLayerGroupNodes( node, (DefaultMutableTreeNode) tree.getModel().getRoot() );
}
tree.expandRow( 0 );
tree.expandRow( 1 );
}
private static void addLayerNodes( Layer layer, DefaultMutableTreeNode parent ) {
if ( layer instanceof SystemLayer && !( (SystemLayer) layer ).isVisibleInLayerTree() ) {
return;
}
LayerNode cn = new LayerNode( layer, NODE_TYPE.unknown );
cn.setSelected( layer.isVisible() );
parent.add( cn );
// addComplexContent( layer, cn, layer.isVisible() );
}
private void addLayerGroupNodes( LayerGroup layerGroup, DefaultMutableTreeNode parent ) {
LayerGroupNode cn = new LayerGroupNode( layerGroup, null );
cn.setSelected( layerGroup.isVisible() );
parent.add( cn );
List<MapModelEntry> entries = layerGroup.getMapModelEntries();
for ( MapModelEntry entry : entries ) {
if ( entry instanceof Layer ) {
addLayerNodes( (Layer) entry, cn );
} else {
addLayerGroupNodes( (LayerGroup) entry, cn );
}
}
}
private void initGUI() {
try {
BorderLayout thisLayout = new BorderLayout();
this.setLayout( thisLayout );
this.setPreferredSize( new java.awt.Dimension( 335, 593 ) );
{
if ( splPane != null ) {
this.remove( splPane );
}
splPane = new JSplitPane();
this.add( splPane, BorderLayout.CENTER );
splPane.setOrientation( JSplitPane.VERTICAL_SPLIT );
splPane.setPreferredSize( new java.awt.Dimension( 167, 300 ) );
splPane.setRequestFocusEnabled( false );
{
scTree = new JScrollPane();
splPane.add( scTree, JSplitPane.TOP );
scTree.setPreferredSize( new java.awt.Dimension( 333, 466 ) );
scTree.setSize( 200, 400 );
scTree.setMinimumSize( new java.awt.Dimension( 22, 250 ) );
{
DefaultMutableTreeNode root = new DefaultMutableTreeNode( mapModel.getName() );
tree = new DnDJTree( root, appContainer, this.mapModel );
tree.getSelectionModel().setSelectionMode( TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION );
tree.putClientProperty( "JTree.lineStyle", "Angled" );
tree.addMouseListener( new NodeMouseListener( tree ) );
scTree.setViewportView( tree );
}
}
{
pnInfo = new JPanel();
GridBagLayout pnInfoLayout = new GridBagLayout();
splPane.add( pnInfo, JSplitPane.BOTTOM );
pnInfo.setPreferredSize( new java.awt.Dimension( 333, 113 ) );
pnInfo.setSize( 333, 134 );
pnInfoLayout.rowWeights = new double[] { 0.0, 0.1 };
pnInfoLayout.rowHeights = new int[] { 35, 7 };
pnInfoLayout.columnWeights = new double[] { 0.1 };
pnInfoLayout.columnWidths = new int[] { 7 };
pnInfo.setLayout( pnInfoLayout );
{
lbLayer = new JLabel();
pnInfo.add( lbLayer, new GridBagConstraints( 0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
GridBagConstraints.HORIZONTAL, new Insets( 0, 10,
0, 0 ),
0, 0 ) );
lbLayer.setText( Messages.getMessage( getLocale(), "$MD10007" ) );
}
{
taDescription = new JTextPane();
taDescription.setEditable( false );
JScrollPane sc = new JScrollPane();
sc.setViewportView( taDescription );
pnInfo.add( sc, new GridBagConstraints( 0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
GridBagConstraints.BOTH, new Insets( 5, 10, 5, 10 ), 0,
0 ) );
}
}
}
} catch ( Exception e ) {
e.printStackTrace();
}
}
/**
* sets the adapter for a layer which info shall be displayed
*
* @param mapModelEntry
*/
private void setMapModelEntry( MapModelEntry mapModelEntry ) {
LayerInfoFrame.getInstance( appContainer ).setLayer( mapModelEntry );
lbLayer.setText( mapModelEntry.getTitle() );
String tmp = mapModelEntry.getAbstract();
if ( tmp != null && tmp.trim().length() > 0 ) {
taDescription.setText( tmp );
} else {
taDescription.setText( "no abstract" );
}
}
// /////////////////////////////////////////////////////////////////////////////////////
// inner classes //
// /////////////////////////////////////////////////////////////////////////////////////
/**
*
*
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
* @author last edited by: $Author$
*
* @version. $Revision$, $Date$
*/
class MapModelChangeListener implements ChangeListener {
/*
* (non-Javadoc)
*
* @see org.deegree.client.presenter.ChangeListener#valueChanged(org.deegree.client.presenter.ValueChangedEvent)
*/
public void valueChanged( ValueChangedEvent event ) {
synchronized ( tree ) {
CHANGE_TYPE ct = ( (MapModelChangedEvent) event ).getChangeType();
if ( ct == CHANGE_TYPE.layerInserted ) {
MapModelEntry entry = (MapModelEntry) ( (MapModelChangedEvent) event ).getValue();
insertLayerNode( (Layer) entry );
} else if ( ct == CHANGE_TYPE.layerGroupInserted ) {
MapModelEntry entry = (MapModelEntry) ( (MapModelChangedEvent) event ).getValue();
insertLayerGroupNode( (LayerGroup) entry );
} else if ( ct == CHANGE_TYPE.layerRemoved || ct == CHANGE_TYPE.layerGroupRemoved ) {
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
MapModelEntry entry = (MapModelEntry) ( (MapModelChangedEvent) event ).getValue();
removeChild( model, root, entry );
} else if ( ct == CHANGE_TYPE.layerStateChanged ) {
tree.revalidate();
tree.repaint();
}
owner.update();
}
}
private void insertLayerGroupNode( LayerGroup layerGroup ) {
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
DefaultMutableTreeNode ln = findParent( root, layerGroup );
if ( ln != null ) {
root = ln;
}
CheckNode cn = new LayerGroupNode( layerGroup, NODE_TYPE.layerGroup );
cn.setSelected( true );
int index = getAntecessorIndex( layerGroup );
if ( index < 0 ) {
index = root.getChildCount();
} else if ( index > root.getChildCount() ) {
index = root.getChildCount();
}
model.insertNodeInto( cn, root, index );
List<MapModelEntry> entries = layerGroup.getMapModelEntries();
for ( MapModelEntry entry : entries ) {
if ( entry instanceof Layer ) {
insertLayerNode( (Layer) entry );
} else if ( entry instanceof LayerGroup ) {
insertLayerGroupNode( (LayerGroup) entry );
}
}
}
private void insertLayerNode( Layer layer ) {
if ( layer instanceof SystemLayer && !( (SystemLayer) layer ).isVisibleInLayerTree() ) {
return;
}
DefaultTreeModel model = (DefaultTreeModel) tree.getModel();
DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
DefaultMutableTreeNode ln = findParent( root, layer );
if ( ln != null ) {
root = ln;
}
CheckNode cn = new LayerNode( layer, NODE_TYPE.unknown );
cn.setSelected( true );
int index = getAntecessorIndex( layer );
if ( index < 0 ) {
index = root.getChildCount();
} else if ( index > root.getChildCount() ) {
index = root.getChildCount();
}
model.insertNodeInto( cn, root, index );
// addComplexContent( la, cn, la.isVisible() );
}
/**
*
* @param root
* @param entry
* @return assigned treenode of a layers parent
*/
private DefaultMutableTreeNode findParent( DefaultMutableTreeNode root, MapModelEntry entry ) {
if ( root.getUserObject() instanceof LayerGroup ) {
LayerGroup la = (LayerGroup) root.getUserObject();
if ( la.equals( entry.getParent() ) ) {
return root;
}
}
int cnt = root.getChildCount();
for ( int i = 0; i < cnt; i++ ) {
TreeNode child = root.getChildAt( i );
if ( child instanceof CheckNode ) {
DefaultMutableTreeNode ln = findParent( (DefaultMutableTreeNode) child, entry );
if ( ln != null ) {
return ln;
}
}
}
return null;
}
/**
*
* @param entry
* @return index of the antecessore layer underneath the parent of the passed layer
*/
private int getAntecessorIndex( MapModelEntry entry ) {
if ( entry.getParent() != null ) {
List<MapModelEntry> entries = entry.getParent().getMapModelEntries();
for ( int i = 0; i < entries.size(); i++ ) {
if ( entries.get( i ).equals( entry ) ) {
return i;
}
}
}
return -1;
}
/**
*
* @param model
* @param parent
* @param mapModelEntry
*/
private void removeChild( DefaultTreeModel model, DefaultMutableTreeNode parent, MapModelEntry mapModelEntry ) {
int cc = parent.getChildCount();
for ( int i = 0; i < cc; i++ ) {
DefaultMutableTreeNode dmt = (DefaultMutableTreeNode) parent.getChildAt( i );
if ( mapModelEntry.equals( dmt.getUserObject() ) ) {
model.removeNodeFromParent( dmt );
return;
} else {
removeChild( model, dmt, mapModelEntry );
}
}
}
}
/**
*
*
*
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
* @author last edited by: $Author$
*
* @version. $Revision$, $Date$
*/
class NodeMouseListener extends MouseAdapter {
private JTree tree;
/**
*
* @param tree
*/
NodeMouseListener( JTree tree ) {
this.tree = tree;
}
private void handleSelectFor( CheckNode node, String s, int modifier ) {
node.setSelectedFor( s, !node.isSelectedFor( s ), KeyEvent.VK_CONTROL != modifier );
if ( node.isSelectedFor( s ) ) {
if ( KeyEvent.VK_CONTROL != modifier ) {
// remove selection for XXXX from all nodes
removeSelectedForFromAllLAyers( s, (DefaultMutableTreeNode) node.getRoot() );
}
// mark layer as being selected for XXXX
if ( node.getUserObject() != null && node.getUserObject() instanceof MapModelEntry ) {
( (MapModelEntry) node.getUserObject() ).addSelectedFor( s );
}
}
}
private void removeSelectedForFromAllLAyers( String selectedFor, DefaultMutableTreeNode node ) {
if ( node.getUserObject() != null && node.getUserObject() instanceof MapModelEntry ) {
( (MapModelEntry) node.getUserObject() ).removeSelectedFor( selectedFor );
}
int count = node.getChildCount();
for ( int i = 0; i < count; i++ ) {
if ( node.getChildAt( i ) instanceof DefaultMutableTreeNode ) {
removeSelectedForFromAllLAyers( selectedFor, (DefaultMutableTreeNode) node.getChildAt( i ) );
}
}
}
/**
* expands or collapses a tree path depending on selection status of the node receiving a mouse click event
*/
@Override
public void mouseReleased( MouseEvent e ) {
if ( e.getButton() == MouseEvent.BUTTON1 ) {
int x = e.getX();
int y = e.getY();
int row = tree.getRowForLocation( x, y );
TreePath path = tree.getPathForRow( row );
if ( tree.getRowBounds( row ) != null ) {
int min = tree.getRowBounds( row ).x;
if ( path != null ) {
Object o = path.getLastPathComponent();
if ( o instanceof CheckNode ) {
CheckNode node = (CheckNode) path.getLastPathComponent();
if ( x < min + 15 ) {
boolean isSelected = !( node.isSelected() );
( (DefaultTreeModel) tree.getModel() ).nodeChanged( node );
switchVisibility( isSelected, (MapModelEntry) node.getUserObject() );
traverseDown( node, isSelected );
traverseUp( (MapModelEntry) node.getUserObject() );
}
// call back to owner Panel
setMapModelEntry( (MapModelEntry) node.getUserObject() );
handleSelectFor( node, MapModel.SELECTION_ACTION, e.getModifiers() );
if ( row == 0 ) {
tree.revalidate();
tree.repaint();
}
}
}
}
} else {
maybeShowPopup( e );
}
}
/**
* ensures that all nodes on path to root are switched to invisible/not selected if all of child nodes are
* invisible/not selected and vice versa
*
* @param mme
* @param selected
*/
private void traverseUp( MapModelEntry mme ) {
LayerGroup lg = mme.getParent();
if ( lg != null ) {
List<MapModelEntry> list = lg.getMapModelEntries();
boolean vis = traverse( list, false );
if ( vis != lg.isVisible() ) {
switchVisibility( vis, lg );
traverseUp( lg );
}
}
}
private boolean traverse( List<MapModelEntry> list, boolean vis ) {
for ( MapModelEntry mapModelEntry : list ) {
if ( mapModelEntry.isVisible() ) {
return true;
}
if ( mapModelEntry instanceof LayerGroup ) {
vis = traverse( ( (LayerGroup) mapModelEntry ).getMapModelEntries(), vis );
if ( vis ) {
return true;
}
}
}
return vis;
}
/**
* ensures that all nodes underneath the passed {@link MapModelEntry} are switch to the same visibility if the
* passed {@link MapModelEntry} is a LayerGroup
*
* @param mme
* @param selected
*/
private void traverseDown( CheckNode node, boolean selected ) {
MapModelEntry mme = (MapModelEntry) node.getUserObject();
int cnt = node.getChildCount();
if ( mme instanceof LayerGroup ) {
for ( int i = 0; i < cnt; i++ ) {
CheckNode child = (CheckNode) node.getChildAt( i );
MapModelEntry mapModelEntry = (MapModelEntry) child.getUserObject();
if ( mapModelEntry.isVisible() != selected ) {
// child.setSelected( selected );
switchVisibility( selected, mapModelEntry );
}
if ( mapModelEntry instanceof LayerGroup ) {
traverseDown( child, selected );
}
}
}
}
private void switchVisibility( boolean selected, MapModelEntry mapModelEntry ) {
Command command = new ChangeMapModelEntryStateCommand( mapModelEntry, MAPMODELENTRYSTATE.visibility,
selected );
try {
appContainer.getCommandProcessor().executeSychronously( command, true );
} catch ( Exception e1 ) {
e1.printStackTrace();
}
}
@Override
public void mousePressed( MouseEvent e ) {
maybeShowPopup( e );
}
private void maybeShowPopup( MouseEvent e ) {
if ( e.isPopupTrigger() ) {
JMenuItem item = getMenuItemByActionName( "setVisibility" );
if ( item != null ) {
List<MapModelEntry> mapModelEntries = mapModel.getMapModelEntriesSelectedForAction( MapModel.SELECTION_ACTION );
for ( MapModelEntry mapModelEntry : mapModelEntries ) {
item.setSelected( mapModelEntry.isVisible() );
}
}
popup.show( e.getComponent(), e.getX(), e.getY() );
}
}
}
}