/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, Open Source Geospatial Foundation (OSGeo)
*
* 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;
* version 2.1 of the License.
*
* 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.
*/
package org.geotools.gui.swing.contexttree;
import java.util.ArrayList;
import java.util.ResourceBundle;
import java.util.Vector;
import javax.swing.ImageIcon;
import javax.swing.event.EventListenerList;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.geotools.gui.swing.contexttree.column.TreeTableColumn;
import org.geotools.gui.swing.contexttree.node.SubNodeGroup;
import org.geotools.gui.swing.icon.IconBundle;
import org.geotools.map.MapContext;
import org.geotools.map.MapLayer;
import org.geotools.map.event.MapLayerListEvent;
import org.geotools.map.event.MapLayerListListener;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;
import org.jdesktop.swingx.treetable.MutableTreeTableNode;
import org.jdesktop.swingx.treetable.TreeTableModel;
import org.jdesktop.swingx.treetable.TreeTableNode;
/**
* ContextTreeModel for JContextTree
*
* @author Johann Sorel
*
* @source $URL$
*/
public final class ContextTreeModel extends DefaultTreeTableModel implements MapLayerListListener {
private static ResourceBundle BUNDLE = ResourceBundle.getBundle("org/geotools/gui/swing/contexttree/Bundle");
/**
* number of the tree column
*/
public static final int TREE = 0;
private static final SubNodeGroup[] EMPTY_SUBNODEGROUP_ARRAY = new SubNodeGroup[] {};
private final EventListenerList listeners = new EventListenerList();
private final JContextTree frame;
private final ArrayList<TreeTableColumn> columns = new ArrayList<TreeTableColumn>();
private final ArrayList<SubNodeGroup> subgroups = new ArrayList<SubNodeGroup>();
private final Vector columnNames = new Vector();
private final LightContextTreeModel lightModel;
private MapContext activeContext;
private boolean treeedit = true;
/**
* Creates a new instance of ContextTreeModel
* prevent build model by other use
*
*/
ContextTreeModel(JContextTree frame) {
super();
lightModel = new LightContextTreeModel(this);
this.frame = frame;
ContextTreeNode node = new ContextTreeNode(lightModel) {
@Override
public ImageIcon getIcon() {
return IconBundle.EMPTY_ICON;
}
@Override
public boolean isEditable() {
return false;
}
@Override
public Object getValue() {
return "Root";
}
@Override
public void setValue(Object obj) {
}
};
setRoot(node);
columnNames.add(BUNDLE.getString("col_tree"));
setColumnIdentifiers(columnNames);
}
/**
* set if the treecolumn (maplayer and mapcontext titles) can be edited
* @param b new value
*/
void setTreeColumEditable(boolean b) {
treeedit = b;
}
/**
* move a node
* @param newChild the moving node
* @param father his new parent node
* @param index position in the father node
*/
void moveNode(MutableTreeTableNode newChild, MutableTreeTableNode father, int index) {
super.removeNodeFromParent(newChild);
super.insertNodeInto(newChild, father, index);
}
/**
* get the class of a specific column
* @param column column number
* @return Class of the column
*/
@Override
public Class getColumnClass(int column) {
Class c ;
if (column == TREE) {
c = TreeTableModel.class;
} else {
if (column <= columns.size()) {
c = columns.get(column - 1).getColumnClass();
}else{
c = Object.class;
}
}
return c;
}
/**
* get number of column
* @return int
*/
@Override
public int getColumnCount() {
return 1 + columns.size();
}
/**
* know is the cell is editable
* @param node specific node
* @param column column number
* @return editable state
*/
@Override
public boolean isCellEditable(Object node, int column) {
if (column == TREE) {
ContextTreeNode n = (ContextTreeNode) node;
return (treeedit && n.isEditable(column));
} else {
if (column <= columns.size()) {
return columns.get(column - 1).isCellEditable(((ContextTreeNode) node).getUserObject());
} else {
return false;
}
}
}
/**
* insert a node at a specific node
* @param newChild the new node
* @param father the node who will contain the new node
* @param index position of the new node
*/
@Override
public void insertNodeInto(MutableTreeTableNode newChild, MutableTreeTableNode father, int index) {
super.insertNodeInto(newChild, father, index);
}
/**
* remove a node from his parent
* @param node the node to remove
*/
@Override
public void removeNodeFromParent(MutableTreeTableNode node) {
super.removeNodeFromParent(node);
}
////////////////////////////////////////////////////////////////////////////////
// COLUMNS MANAGEMENT //////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* add a new column in the model
* @param model the new column model
*/
void addColumnModel(TreeTableColumn model) {
columns.add(model);
columnNames.add(model.getTitle());
setColumnIdentifiers(columnNames);
model.setModelIndex(columns.indexOf(model) + 1);
}
void removeColumnModel(TreeTableColumn model) {
int index = columns.indexOf(model);
this.removeColumnModel(index);
}
void removeColumnModel(int index){
columns.remove(index);
columnNames.remove(index+1);
setColumnIdentifiers(columnNames);
for(TreeTableColumn col : columns){
col.setModelIndex(columns.indexOf(col) + 1);
}
}
public int getColumnModelCount() {
return columns.size();
}
public TreeTableColumn getColumnModel(int index){
return columns.get(index);
}
public int getColumnModelIndex(TreeTableColumn model) {
return columns.indexOf(model);
}
/**
* get the list of column
* @return list of column models
*/
ArrayList<TreeTableColumn> getColumnModels() {
return columns;
}
////////////////////////////////////////////////////////////////////////////////
// SUBNODES MANAGEMENT /////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void addSubNodeGroup(SubNodeGroup group) {
if(group!=null && !subgroups.contains(group)){
subgroups.add(group);
visitNode(getRoot(),group);
}
}
void removeSubNodeGroup(SubNodeGroup group) {
if(group!=null && subgroups.contains(group)){
subgroups.remove(group);
cleanNode(getRoot(), group);
}
}
void removeSubNodeGroup(int index){
SubNodeGroup grp = subgroups.get(index);
removeSubNodeGroup(grp);
}
int getSubNodeGroupCount(){
return subgroups.size();
}
int getSubNodeGroupIndex(SubNodeGroup group){
return subgroups.indexOf(group);
}
SubNodeGroup[] getSubNodeGroups() {
return subgroups.toArray(EMPTY_SUBNODEGROUP_ARRAY);
}
private void visitNode(TreeTableNode node, SubNodeGroup group){
for(int i=0, max=node.getChildCount(); i<max;i++){
visitNode(node.getChildAt(i),group);
}
if(node instanceof ContextTreeNode ){
ContextTreeNode tn = (ContextTreeNode) node;
Object obj = tn.getUserObject();
if(group.isValid(obj)){
group.installInNode(lightModel,tn);
}
}
}
private void cleanNode(TreeTableNode node){
for(SubNodeGroup sub : subgroups){
cleanNode(node, sub);
}
}
private void cleanNode(TreeTableNode node, SubNodeGroup group){
for(int max=node.getChildCount(), i=max-1; i>=0;i--){
cleanNode(node.getChildAt(i),group);
}
if(node instanceof ContextTreeNode ){
ContextTreeNode tn = (ContextTreeNode) node;
Object obj = tn.getUserObject();
if(group.isValid(obj)){
group.removeForNode(lightModel,tn);
}
}
}
////////////////////////////////////////////////////////////////////////////////
// MAPCONTEXT MANAGEMENT ///////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* get the active context
* @return return the active MapContext, if none return null
*/
public MapContext getActiveContext() {
return activeContext;
}
/**
* active the context if in the tree
* @param context the mapcontext to active
*/
void setActiveContext(MapContext context) {
if (getMapContextIndex(context) >= 0) {
ContextTreeNode node;
if (activeContext != null) {
node = (ContextTreeNode) getMapContextNode(activeContext);
modelSupport.fireChildChanged(new TreePath(getRoot()), getMapContextIndex(activeContext), node);
}
activeContext = context;
node = (ContextTreeNode) getMapContextNode(activeContext);
modelSupport.fireChildChanged(new TreePath(getRoot()), getMapContextIndex(activeContext), node);
} else if (activeContext != null) {
ContextTreeNode node = (ContextTreeNode) getMapContextNode(activeContext);
modelSupport.fireChildChanged(new TreePath(getRoot()), getMapContextIndex(activeContext), node);
activeContext = null;
}
fireContextActivated(context, getMapContextIndex(context));
}
/**
* add context to the Tree if not allready in it
* @param context the context to add
*/
void addMapContext(MapContext context) {
if (getMapContextIndex(context) < 0) {
context.addMapLayerListListener(this);
ContextTreeNode node = new MapContextTreeNode(lightModel,context);
insertNodeInto(node, (ContextTreeNode) getRoot(), getRoot().getChildCount());
for (int i = context.getLayerCount() - 1; i >= 0; i--) {
ContextTreeNode layer = new LayerContextTreeNode(lightModel,context.getLayer(i));
insertNodeInto(layer, node, node.getChildCount());
}
fireContextAdded(context, getMapContextIndex(context));
setActiveContext(context);
//subnodes
for(SubNodeGroup grp : subgroups){
visitNode(node, grp);
}
}
}
/**
* remove context from the tree
* @param context target mapcontext to remove
*/
void removeMapContext(MapContext context) {
for (int i = 0; i < getRoot().getChildCount(); i++) {
ContextTreeNode jm = (ContextTreeNode) getRoot().getChildAt(i);
if (jm.getUserObject().equals(context)) {
cleanNode(jm);
removeNodeFromParent(jm);
if (jm.getUserObject().equals(activeContext)) {
activeContext = null;
fireContextActivated(null, -1);
}
fireContextRemoved(context, i);
}
}
}
/**
* count MapContext in the tree
* @return number of mapcontext in the tree
*/
int getMapContextCount() {
return getRoot().getChildCount();
}
/**
* return context at index i
* @param i position of the mapcontext
* @return the mapcontext a position i
*/
MapContext getMapContext(int i) {
return (MapContext) ((ContextTreeNode)getRoot().getChildAt(i)).getUserObject();
}
/**
* get the index of a mapcontext in the tree
* @param context the mapcontext to find
* @return index of context, -1 if context isn't in the tree
*/
int getMapContextIndex(MapContext context) {
int ret = -1;
if (context != null) {
for (int i = 0; i < getRoot().getChildCount(); i++) {
ContextTreeNode jm = (ContextTreeNode) getRoot().getChildAt(i);
if (jm.getUserObject().equals(context)) {
ret = i;
}
}
}
return ret;
}
/**
* return the node of context
* <b>use with care!<b/>
* @param context the context to find
* @return the node contining the mapcontext
*/
TreeNode getMapContextNode(MapContext context) {
TreeNode node = null;
if (context != null) {
for (int i = 0; i < getRoot().getChildCount(); i++) {
ContextTreeNode jm = (ContextTreeNode) getRoot().getChildAt(i);
if (jm.getUserObject().equals(context)) {
node = jm;
}
}
}
return node;
}
/**
* moveContext depending on nodes
* <b>use with care!<b/>
* @param moveNode the node to move
* @param father the new parent node
* @param place new position of the child node
*/
void moveMapContext(ContextTreeNode moveNode, ContextTreeNode father, int place) {
int depart = ((ContextTreeNode) getRoot()).getIndex(moveNode);
removeNodeFromParent(moveNode);
insertNodeInto(moveNode, father, place);
fireContextMoved((MapContext) moveNode.getUserObject(), depart, place);
}
MapContext[] getMapContexts(){
ContextTreeNode rootnode = ((ContextTreeNode)root);
int childs = rootnode.getChildCount();
MapContext[] contexts = new MapContext[childs];
for(int i=0;i<childs;i++){
contexts[i] = (MapContext) ((ContextTreeNode)rootnode.getChildAt(i)).getUserObject();
}
return contexts;
}
////////////////////////////////////////////////////////////////////////////////
// LAYER MANAGEMENT - USE BY DRAG&DROP CLASSES /////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* add a maplayer into a node, used for drag and drop
* @param newChild the new node
* @param parent the father node
* @param index new position of the child node
*/
void insertLayerInto(ContextTreeNode newChild, ContextTreeNode parent, int index) {
if (newChild.getUserObject() instanceof MapLayer && parent.getUserObject() instanceof MapContext) {
MapContext context = (MapContext) parent.getUserObject();
MapLayer layer = (MapLayer) newChild.getUserObject();
index = context.getLayerCount() - index;
if (index < 0) {
index = 0;
}
if (index > context.getLayerCount()) {
index = context.getLayerCount();
}
if (index > parent.getChildCount()) {
index = 0;
}
context.addLayer(index, layer);
}
}
/**
* remove a node maplayer from it's parent
* @param node the node to remove
*/
void removeLayerFromParent(ContextTreeNode node) {
if (node.getUserObject() instanceof MapLayer && ((ContextTreeNode) node.getParent()).getUserObject() instanceof MapContext) {
MapContext context = (MapContext) ((ContextTreeNode)node.getParent()).getUserObject();
MapLayer layer = (MapLayer) node.getUserObject();
context.removeLayer(layer);
}
}
/**
* mode a specific node
* @param Child the node to move
* @param parent the new father node
* @param index the position of the child node
*/
void moveLayer(ContextTreeNode Child, ContextTreeNode parent, int index) {
if (Child.getUserObject() instanceof MapLayer && parent.getUserObject() instanceof MapContext) {
MapContext context = (MapContext) parent.getUserObject();
MapLayer layer = (MapLayer) Child.getUserObject();
index = context.getLayerCount() - 1 - index;
if (index < 0) {
index = 0;
}
if (index > context.getLayerCount()) {
index = context.getLayerCount();
}
if (index > parent.getChildCount()) {
index = 0;
}
int begin = context.indexOf(layer);
context.moveLayer(begin, index);
}
}
////////////////////////////////////////////////////////////////////////////////
// FIREEVENT AND LISTENERS /////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* generate a treeevent for an added node
* @param mapcontext the added mapcontext
* @param position the position of the mapcontext in the tree
*/
private void fireContextAdded(MapContext mapcontext, int position) {
TreeContextEvent kevent = new TreeContextEvent(frame, mapcontext, position);
TreeContextListener[] list = getTreeContextListeners();
for (int i = 0; i < list.length; i++) {
list[i].contextAdded(kevent);
}
}
/**
* generate a treeevent for a mapcontext removed
* @param mapcontext the removed mapcontext
* @param position the last position of the mapcontext
*/
private void fireContextRemoved(MapContext mapcontext, int position) {
TreeContextEvent event = new TreeContextEvent(frame, mapcontext, position);
TreeContextListener[] list = getTreeContextListeners();
for (int i = 0; i < list.length; i++) {
list[i].contextRemoved(event);
}
}
/**
* generate a treeevent for an activated mapcontext
* @param mapcontext the activated mapcontext (null if none activated)
* @param index the position of the activated context
*/
private void fireContextActivated(MapContext mapcontext, int index) {
TreeContextEvent event = new TreeContextEvent(frame, mapcontext, index);
TreeContextListener[] list = getTreeContextListeners();
for (int i = 0; i < list.length; i++) {
list[i].contextActivated(event);
}
}
/**
* generate a treeevent for a moving context
* @param mapcontext the moving mapcontext
* @param begin the start position of the mapcontext
* @param end the end position of the mapcontext
*/
private void fireContextMoved(MapContext mapcontext, int begin, int end) {
TreeContextEvent event = new TreeContextEvent(frame, mapcontext, begin, end);
TreeContextListener[] list = getTreeContextListeners();
for (int i = 0; i < list.length; i++) {
list[i].contextMoved(event);
}
}
/**
* add treeListener to Model
* @param ker the new listener
*/
void addTreeContextListener(TreeContextListener ker) {
listeners.add(TreeContextListener.class, ker);
}
/**
* remove treeListener from Model
* @param ker the listner to remove
*/
void removeTreeContextListener(TreeContextListener ker) {
listeners.remove(TreeContextListener.class, ker);
}
/**
* get treeListeners list
* @return the listener's table
*/
TreeContextListener[] getTreeContextListeners() {
return listeners.getListeners(TreeContextListener.class);
}
////////////////////////////////////////////////////////////////////////////////
// MAPCONTEXT LISTENER /////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/**
* when a layer is added
* @param mle the event
*/
public void layerAdded(MapLayerListEvent mle) {
MapContext context = (MapContext) mle.getSource();
int i = 0;
boolean find = false;
while (i < getRoot().getChildCount() && !find) {
if (((ContextTreeNode) getRoot().getChildAt(i)).getUserObject().equals(context)) {
ContextTreeNode layer = new LayerContextTreeNode(lightModel,mle.getLayer());
ContextTreeNode father = (ContextTreeNode) getRoot().getChildAt(i);
int index = mle.getToIndex();
index = context.getLayerCount() - 1 - mle.getToIndex();
if (index > father.getChildCount()) {
index = father.getChildCount();
}
if (index < 0) {
index = 0;
}
insertNodeInto(layer, father, index);
//subnodes
for(SubNodeGroup grp : subgroups){
visitNode(layer, grp);
}
}
i++;
}
}
/**
* when a layer is removed
* @param mle the event
*/
public void layerRemoved(MapLayerListEvent mle) {
MapContext context = (MapContext) mle.getSource();
int i = 0;
boolean find = false;
while (i < getRoot().getChildCount() && !find) {
if (((ContextTreeNode) getRoot().getChildAt(i)).getUserObject().equals(context)) {
ContextTreeNode father = (ContextTreeNode) getRoot().getChildAt(i);
for (int t = 0; t < father.getChildCount(); t++) {
ContextTreeNode node = (ContextTreeNode) father.getChildAt(t);
if (mle.getLayer().equals(node.getUserObject())) {
cleanNode(node);
removeNodeFromParent(node);
}
}
}
i++;
}
}
/**
* when a layer changed
* @param mle the event
*/
public void layerChanged(MapLayerListEvent mle) {
MapContext context = (MapContext) mle.getSource();
int i = 0;
boolean find = false;
while (i < getRoot().getChildCount() && !find) {
if (((ContextTreeNode) getRoot().getChildAt(i)).getUserObject().equals(context)) {
ContextTreeNode father = (ContextTreeNode) getRoot().getChildAt(i);
for (int t = 0; t < father.getChildCount(); t++) {
ContextTreeNode node = (ContextTreeNode) father.getChildAt(t);
if (mle.getLayer().equals(node.getUserObject())) {
modelSupport.fireChildChanged(new TreePath(getPathToRoot(father)), father.getIndex(node), node);
}
}
}
i++;
}
}
/**
* when a layer moved
* @param mle the event
*/
public void layerMoved(MapLayerListEvent mle) {
MapContext context = (MapContext) mle.getSource();
int i = 0;
boolean find = false;
while (i < getRoot().getChildCount() && !find) {
if (((ContextTreeNode) getRoot().getChildAt(i)).getUserObject().equals(context)) {
ContextTreeNode father = (ContextTreeNode) getRoot().getChildAt(i);
MapLayer layerA = mle.getLayer();
ContextTreeNode nodeA = null;
int indiceA = 0;
for (int t = 0; t < father.getChildCount(); t++) {
ContextTreeNode node = (ContextTreeNode) father.getChildAt(t);
if (layerA.equals(node.getUserObject())) {
nodeA = node;
indiceA = t;
}
}
indiceA = context.getLayerCount() - 1 - indiceA;
if (indiceA < 0) {
indiceA = 0;
}
if (indiceA > context.getLayerCount()) {
indiceA = context.getLayerCount();
}
if (indiceA > father.getChildCount()) {
indiceA = 0;
}
int depart = mle.getFromIndex();
int fin = mle.getToIndex();
if (indiceA == fin) {
int echange = fin;
fin = depart;
depart = echange;
}
removeNodeFromParent(nodeA);
int index = fin;
index = father.getChildCount() - index;
if (index > father.getChildCount()) {
index = father.getChildCount();
}
if (index < 0) {
index = 0;
}
insertNodeInto(nodeA, father, index);
}
i++;
}
}
}