Package org.geotools.swing

Source Code of org.geotools.swing.MapLayerTable

/*
*    GeoTools - The Open Source Java GIS Toolkit
*    http://geotools.org
*
*    (C) 2008-2011, 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.swing;

import org.geotools.swing.locale.LocaleUtils;
import java.awt.BorderLayout;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.lang.ref.WeakReference;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;

import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.map.StyleLayer;
import org.geotools.map.event.MapLayerListEvent;
import org.geotools.map.event.MapLayerListListener;
import org.geotools.styling.Style;
import org.geotools.swing.control.DnDList;
import org.geotools.swing.control.DnDListModel;
import org.geotools.swing.event.MapPaneAdapter;
import org.geotools.swing.event.MapPaneEvent;
import org.geotools.swing.styling.JSimpleStyleDialog;

/**
* Displays a list of the map layers in an associated {@linkplain MapPane} and
* provides controls to set the visibility, selection and style of each layer.
* <p>
* Implementation note: DefaultMapContext stores its list of MapLayer objects
* in rendering order, ie. the layer at index 0 is rendererd first, followed by
* index 1 etc. MapLayerTable stores its layers in the reverse order since it
* is more intuitive for the user to think of a layer being 'on top' of other
* layers.
*
* @author Michael Bedward
* @since 2.6
*
* @source $URL$
* @version $Id$
*/
public class MapLayerTable extends JPanel {
    // used to get localized strings from LocaleUtils class
    private static final String CLASS_NAME = "MapLayerTable";
   
    private static final String LIST_TITLE = LocaleUtils.getValue(CLASS_NAME, "ListTitle");
   
    private static final String SHOW_HIDE_LAYER = LocaleUtils.getValue(CLASS_NAME, "ShowHideLayer");
    private static final String SHOW_ALL_LAYERS = LocaleUtils.getValue(CLASS_NAME, "ShowAllLayers");
    private static final String HIDE_ALL_LAYERS = LocaleUtils.getValue(CLASS_NAME, "HideAllLayers");
   
    private static final String SELECT_LAYER = LocaleUtils.getValue(CLASS_NAME, "SelectLayer");
    private static final String SELECT_ALL_LAYERS = LocaleUtils.getValue(CLASS_NAME, "SelectAllLayers");
    private static final String DESELECT_ALL_LAYERS = LocaleUtils.getValue(CLASS_NAME, "DeselectAllLayers");
   
    private static final String RENAME_LAYER = LocaleUtils.getValue(CLASS_NAME, "RenameLayer");
    private static final String RENAME_LAYER_MESSAGE = LocaleUtils.getValue(CLASS_NAME, "RenameLayer_Message");
   
    private static final String REMOVE_LAYER = LocaleUtils.getValue(CLASS_NAME, "RemoveLayer");
    private static final String REMOVE_LAYER_MESSAGE = LocaleUtils.getValue(CLASS_NAME, "RemoveLayer_ConfirmMessage");
    private static final String REMOVE_LAYER_TITLE = LocaleUtils.getValue(CLASS_NAME, "RemoveLayer_ConfirmTitle");
   
   
    private static final String STYLE_LAYER = LocaleUtils.getValue(CLASS_NAME, "StyleLayer");
   

    private MapPane mapPane;
    private DnDListModel<Layer> listModel;
    private DnDList<Layer> list;
    private JScrollPane scrollPane;
   
    /* For detecting mouse double-clicks */
    private static final long DOUBLE_CLICK_TIME = 500;
    private long lastClickTime = 0;

    /*
     * Whether to prompt for confirmation before removing a layer.
     * @todo introduce a setter or property for this
     */
    private boolean confirmRemove = true;

    /**
     * Default constructor. A subsequent call to {@linkplain #setMapPane}
     * will be required.
     */
    public MapLayerTable() {
        this(null);
    }

    /**
     * Constructor.
     * @param mapPane the map pane this MapLayerTable will service.
     */
    public MapLayerTable(MapPane mapPane) {
        listener = new Listener(this);
        initComponents();
        doSetMapPane(mapPane);
    }

    /**
     * Set the map pane that this MapLayerTable will service.
     *
     * @param mapPane the map pane
     */
    public void setMapPane(MapPane mapPane) {
        doSetMapPane(mapPane);
    }

    /**
     * Helper for {@link #setMapPane(MapPane). This is just defined so that
     * it can be called from the constructor without a warning from the compiler
     * about calling a public overridable method.
     *
     * @param mapPane the map pane
     */
    private Listener listener;
    private void doSetMapPane(MapPane newMapPane) {
        listener.disconnectFromMapPane();
        mapPane = newMapPane;
        listener.connectToMapPane(newMapPane);
    }

    /**
     * Add a new layer to those listed in the table. This method will be called
     * by the associated map pane automatically as part of the event sequence
     * when a new MapLayer is added to the pane's MapContext.
     *
     * @param layer the map layer
     */
    public void onAddLayer(Layer layer) {
        listModel.insertItem(0, layer);
    }

    /**
     * Remove a layer from those listed in the table. This method will be called
     * by the associated map pane automatically as part of the event sequence
     * when a new MapLayer is removed from the pane's MapContext.
     *
     * @param layer the map layer
     */
    void onRemoveLayer(Layer layer) {
        listModel.removeItem(layer);
    }

    /**
     * Repaint the list item associated with the specified MapLayer object
     *
     * @param layer the map layer
     */
    public void repaint(Layer layer) {
        int index = listModel.indexOf(layer);
        list.repaint(list.getCellBounds(index, index));
    }

    /**
     * Removes all items from the table. This is called by the
     * {@code MapPane} or other clients and is not intended for
     * general use.
     */
    public void clear() {
        listModel.clear();
    }

    /**
     * Called by the constructor. This method lays out the components that
     * make up the MapLayerTable and registers a mouse listener.
     */
    private void initComponents() {
        listModel = new DnDListModel<Layer>();
        list = new DnDList<Layer>(listModel) {
            private static final long serialVersionUID = 1289744440656016412L;
            /*
             * We override setToolTipText to provide tool tips
             * for the control labels displayed for each list item
             */
            @Override
            public String getToolTipText(MouseEvent e) {
                int item = list.locationToIndex(e.getPoint());

                if (item >= 0) {
                    Rectangle r = list.getCellBounds(item, item);
                    if (r.contains(e.getPoint())) {
                        Point p = new Point(e.getPoint().x, e.getPoint().y - r.y);

                        if (MapLayerTableCellRenderer.hitSelectionLabel(p)) {
                            return SELECT_LAYER;

                        } else if (MapLayerTableCellRenderer.hitVisibilityLabel(p)) {
                            return SHOW_HIDE_LAYER;

                        } else if (MapLayerTableCellRenderer.hitStyleLabel(p)) {
                            return STYLE_LAYER;

                        } else if (MapLayerTableCellRenderer.hitRemoveLabel(p)) {
                            return REMOVE_LAYER;

                        } else if (MapLayerTableCellRenderer.hitNameLabel(p)) {
                            return RENAME_LAYER;
                        }
                    }
                }
               
                return null;
            }
        };

        // Listen for drag-reordering of the list contents which
        // will be received via the contentsChanged method
        listModel.addListDataListener(new ListDataListener() {

            @Override
            public void intervalAdded(ListDataEvent e) {}
           
            @Override
            public void intervalRemoved(ListDataEvent e) {}

            @Override
            public void contentsChanged(ListDataEvent e) {
                onReorderLayers(e);
            }
        });

        list.setCellRenderer(new MapLayerTableCellRenderer());
        list.setFixedCellHeight(MapLayerTableCellRenderer.getCellHeight());

        list.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                long clickTime = System.currentTimeMillis();
                boolean doubleClick = clickTime - lastClickTime < DOUBLE_CLICK_TIME;
                lastClickTime = clickTime;
                onLayerItemClicked(e, doubleClick);
            }

        });

        scrollPane = new JScrollPane(list,
                JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
                JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);

        scrollPane.setBorder(BorderFactory.createTitledBorder(LIST_TITLE));

        JPanel btnPanel = new JPanel();
        Icon showIcon = MapLayerTableCellRenderer.LayerControlItem.VISIBLE.getIcon();
        JButton btn = null;
       
        btn = new JButton(showIcon);
        btn.setToolTipText(SHOW_ALL_LAYERS);
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onShowAllLayers();
            }
        });
        btnPanel.add(btn);

        Icon hideIcon = MapLayerTableCellRenderer.LayerControlItem.VISIBLE.getOffIcon();
        btn = new JButton(hideIcon);
        btn.setToolTipText(HIDE_ALL_LAYERS);
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onHideAllLayers();
            }
        });
        btnPanel.add(btn);

        Icon onIcon = MapLayerTableCellRenderer.LayerControlItem.SELECTED.getIcon();
        btn = new JButton(onIcon);
        btn.setToolTipText(SELECT_ALL_LAYERS);
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onSelectAllLayers();
            }
        });
        btnPanel.add(btn);

        Icon offIcon = MapLayerTableCellRenderer.LayerControlItem.SELECTED.getOffIcon();
        btn = new JButton(offIcon);
        btn.setToolTipText(DESELECT_ALL_LAYERS);
        btn.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                onUnselectAllLayers();
            }
        });
        btnPanel.add(btn);

        setLayout(new BorderLayout());
        add(scrollPane, BorderLayout.CENTER);
        add(btnPanel, BorderLayout.SOUTH);
    }

    /**
     * Handle a mouse click on a cell in the JList that displays
     * layer names and states.
     *
     * @param ev the mouse event
     * @param doubleClick true if this is the second click of a double-click; false otherwise
     */
    private void onLayerItemClicked(MouseEvent ev, boolean doubleClick) {
        int item = list.locationToIndex(ev.getPoint());

        if (item >= 0) {
            Rectangle r = list.getCellBounds(item, item);
            if (r.contains(ev.getPoint())) {
                Layer layer = listModel.getElementAt(item);
                Point p = new Point(ev.getPoint().x, ev.getPoint().y - r.y);

                if (MapLayerTableCellRenderer.hitSelectionLabel(p)) {
                    layer.setSelected(!layer.isSelected());

                } else if (MapLayerTableCellRenderer.hitVisibilityLabel(p)) {
                    layer.setVisible(!layer.isVisible());

                } else if (MapLayerTableCellRenderer.hitStyleLabel(p)) {
                    doSetStyle(layer);
               
                } else if (MapLayerTableCellRenderer.hitRemoveLabel(p)) {
                    doRemoveLayer(layer);

                } else if (MapLayerTableCellRenderer.hitNameLabel(p)) {
                    if (doubleClick) {
                        doSetLayerName(layer);
                    }
                }
            }
        }
    }

    /**
     * Show a style dialog to create a new Style for the layer
     *
     * @param layer the layer to be styled
     */
    private void doSetStyle(Layer layer) {
        if (layer instanceof StyleLayer) {
            StyleLayer styleLayer = (StyleLayer) layer;
            Style style = JSimpleStyleDialog.showDialog(this, styleLayer);
            if (style != null) {
                styleLayer.setStyle(style);
            }
        }
    }

    /**
     * Prompt for a new title for the layer
     *
     * @param layer the layer to be renamed
     */
    private void doSetLayerName(Layer layer) {
        String name = JOptionPane.showInputDialog(RENAME_LAYER_MESSAGE);
        if (name != null && name.trim().length() > 0) {
            layer.setTitle(name.trim());
        }
    }

    /**
     * Called when the user has clicked on the remove layer item.
     *
     * @param layer the layer to remove
     */
    private void doRemoveLayer(Layer layer) {
        if (confirmRemove) {
            int confirm = JOptionPane.showConfirmDialog(null,
                    REMOVE_LAYER_MESSAGE,
                    REMOVE_LAYER_TITLE,
                    JOptionPane.YES_NO_OPTION);

            if (confirm != JOptionPane.YES_OPTION) {
                return;
            }
        }

        mapPane.getMapContent().removeLayer(layer);
    }

    /**
     * Handle a ListDataEvent signallying a drag-reordering of the map layers.
     * The event is published by the list model after the layers have been
     * reordered there.
     *
     * @param ev the event
     */
    private void onReorderLayers(ListDataEvent ev) {
        ((JComponent) mapPane).setIgnoreRepaint(true);
        for (int pos = ev.getIndex0(); pos <= ev.getIndex1(); pos++) {
            Layer layer = listModel.getElementAt(pos);

            /*
             * MapLayerTable stores layers in the reverse order to
             * the MapContent layer list
             */
            int newContextPos = listModel.getSize() - pos - 1;
            int curContextPos = mapPane.getMapContent().layers().indexOf(layer);

            if (curContextPos != newContextPos) {
                mapPane.getMapContent().moveLayer(curContextPos, newContextPos);
            }
        }
        ((JComponent) mapPane).setIgnoreRepaint(false);
        ((JComponent) mapPane).repaint();
    }

    private void onShowAllLayers() {
        if (mapPane != null && mapPane.getMapContent() != null) {
            for (Layer layer : mapPane.getMapContent().layers()) {
                if (!layer.isVisible()) {
                    layer.setVisible(true);
                }
            }
        }
    }

    private void onHideAllLayers() {
        if (mapPane != null && mapPane.getMapContent() != null) {
            for (Layer layer : mapPane.getMapContent().layers()) {
                if (layer.isVisible()) {
                    layer.setVisible(false);
                }
            }
        }
    }

    private void onSelectAllLayers() {
        if (mapPane != null && mapPane.getMapContent() != null) {
            for (Layer layer : mapPane.getMapContent().layers()) {
                if (!layer.isSelected()) {
                    layer.setSelected(true);
                }
            }
        }
    }

    private void onUnselectAllLayers() {
        if (mapPane != null && mapPane.getMapContent() != null) {
            for (Layer layer : mapPane.getMapContent().layers()) {
                if (layer.isSelected()) {
                    layer.setSelected(false);
                }
            }
        }
    }

   
    private static final class Listener extends MapPaneAdapter implements MapLayerListListener {
        private final MapLayerTable table;
        private WeakReference<MapPane> paneRef;
        private WeakReference<MapContent> contentRef;

        Listener(MapLayerTable table) {
            this.table = table;
        }
       
        void connectToMapPane(MapPane newMapPane) {
            if (newMapPane != null) {
                paneRef = new WeakReference<MapPane>(newMapPane);
                newMapPane.addMapPaneListener(this);
               
                disconnectFromMapContent();
                connectToMapContent(newMapPane.getMapContent());
            }
        }
       
        void disconnectFromMapPane() {
            if (paneRef != null) {
                MapPane prevMapPane = paneRef.get();
                paneRef = null;
               
                if (prevMapPane != null) {
                    prevMapPane.removeMapPaneListener(this);
                }
            }
        }
       
        void connectToMapContent(MapContent newMapContent) {
            if (newMapContent != null) {
                contentRef = new WeakReference<MapContent>(newMapContent);
                newMapContent.addMapLayerListListener(this);
           
                for (Layer layer : newMapContent.layers()) {
                    table.onAddLayer(layer);
                }
            }
        }
       
        private void disconnectFromMapContent() {
            if (contentRef != null) {
                MapContent prevMapContent = contentRef.get();
                contentRef = null;
                if (prevMapContent != null) {
                    prevMapContent.removeMapLayerListListener(this);
                }
               
                table.clear();
            }
        }

        @Override
        public void onNewMapContent(MapPaneEvent ev) {
            table.clear();
           
            disconnectFromMapContent();
            MapContent newMapContent = (MapContent) ev.getData();
            connectToMapContent(newMapContent);
           
            if (newMapContent != null) {
                for (Layer layer : newMapContent.layers()) {
                    table.onAddLayer(layer);
                }
            }
        }

        @Override
        public void layerAdded(MapLayerListEvent event) {
            table.onAddLayer(event.getElement());
        }

        @Override
        public void layerRemoved(MapLayerListEvent event) {
            table.onRemoveLayer(event.getElement());
        }

        @Override
        public void layerChanged(MapLayerListEvent event) {
            table.repaint(event.getElement());
        }

        @Override
        public void layerMoved(MapLayerListEvent event) {
        }

        @Override
        public void layerPreDispose(MapLayerListEvent event) {
        }

    }
}
TOP

Related Classes of org.geotools.swing.MapLayerTable

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.