001: /*
002: * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI for
003: * visualizing and manipulating spatial features with geometry and attributes.
004: *
005: * Copyright (C) 2003 Vivid Solutions
006: *
007: * This program is free software; you can redistribute it and/or modify it under
008: * the terms of the GNU General Public License as published by the Free Software
009: * Foundation; either version 2 of the License, or (at your option) any later
010: * version.
011: *
012: * This program is distributed in the hope that it will be useful, but WITHOUT
013: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
014: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
015: * details.
016: *
017: * You should have received a copy of the GNU General Public License along with
018: * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
019: * Place - Suite 330, Boston, MA 02111-1307, USA.
020: *
021: * For more information, contact:
022: *
023: * Vivid Solutions
024: * Suite #1A
025: * 2328 Government Street
026: * Victoria BC V8T 5G5
027: * Canada
028: *
029: * (250)385-6040
030: * www.vividsolutions.com
031: */
032: package com.vividsolutions.jump.workbench.ui;
033:
034: import java.awt.BorderLayout;
035: import java.awt.Color;
036: import java.awt.Component;
037: import java.awt.Graphics2D;
038: import java.awt.GridBagConstraints;
039: import java.awt.GridBagLayout;
040: import java.awt.Insets;
041: import java.awt.Point;
042: import java.awt.Rectangle;
043: import java.awt.event.MouseAdapter;
044: import java.awt.event.MouseEvent;
045: import java.awt.event.MouseMotionAdapter;
046: import java.util.ArrayList;
047: import java.util.Arrays;
048: import java.util.Collection;
049: import java.util.HashMap;
050: import java.util.Iterator;
051: import java.util.Map;
052: import java.util.Stack;
053:
054: import javax.swing.BorderFactory;
055: import javax.swing.JLabel;
056: import javax.swing.JPanel;
057: import javax.swing.JPopupMenu;
058: import javax.swing.JScrollPane;
059: import javax.swing.JTree;
060: import javax.swing.SwingUtilities;
061: import javax.swing.ToolTipManager;
062: import javax.swing.event.TreeModelEvent;
063: import javax.swing.event.TreeModelListener;
064: import javax.swing.event.TreeSelectionEvent;
065: import javax.swing.event.TreeSelectionListener;
066: import javax.swing.tree.DefaultTreeCellRenderer;
067: import javax.swing.tree.TreeCellEditor;
068: import javax.swing.tree.TreeCellRenderer;
069: import javax.swing.tree.TreeModel;
070: import javax.swing.tree.TreePath;
071: import javax.swing.tree.TreeSelectionModel;
072:
073: import com.vividsolutions.jts.geom.Envelope;
074: import com.vividsolutions.jts.util.Assert;
075: import com.vividsolutions.jump.util.Block;
076: import com.vividsolutions.jump.util.CollectionUtil;
077: import com.vividsolutions.jump.util.LangUtil;
078: import com.vividsolutions.jump.workbench.model.Category;
079: import com.vividsolutions.jump.workbench.model.CategoryEvent;
080: import com.vividsolutions.jump.workbench.model.CategoryEventType;
081: import com.vividsolutions.jump.workbench.model.FeatureEvent;
082: import com.vividsolutions.jump.workbench.model.Layer;
083: import com.vividsolutions.jump.workbench.model.LayerEvent;
084: import com.vividsolutions.jump.workbench.model.LayerEventType;
085: import com.vividsolutions.jump.workbench.model.LayerListener;
086: import com.vividsolutions.jump.workbench.model.LayerManager;
087: import com.vividsolutions.jump.workbench.model.LayerManagerProxy;
088: import com.vividsolutions.jump.workbench.model.LayerTreeModel;
089: import com.vividsolutions.jump.workbench.model.Layerable;
090: import com.vividsolutions.jump.workbench.model.WMSLayer;
091: import com.vividsolutions.jump.workbench.ui.renderer.RenderingManager;
092: import com.vividsolutions.jump.workbench.ui.renderer.style.BasicStyle;
093:
094: public class TreeLayerNamePanel extends JPanel implements
095: LayerListener, LayerNamePanel, LayerNamePanelProxy,
096: PopupNodeProxy {
097: private Map nodeClassToPopupMenuMap = new HashMap();
098:
099: BorderLayout borderLayout1 = new BorderLayout();
100:
101: JTree tree = new JTree() {
102: public boolean isPathEditable(TreePath path) {
103: if (!isEditable()) {
104: return false;
105: }
106:
107: return path.getLastPathComponent() instanceof Layerable
108: || path.getLastPathComponent() instanceof Category;
109: }
110:
111: // Workaround for Java Bug 4199956 "JTree shows container can be
112: // expanded - even when empty", posted by bertrand.allo in the Java Bug
113: // Database. [Jon Aquino]
114: public boolean hasBeenExpanded(TreePath path) {
115: return super .hasBeenExpanded(path)
116: || !this .getModel().isLeaf(
117: path.getLastPathComponent());
118: }
119: };
120:
121: private LayerTreeCellRenderer layerTreeCellRenderer;
122:
123: private TreeCellEditor cellEditor = new LayerTreeCellEditor(tree);
124:
125: private Object popupNode;
126:
127: private ArrayList listeners = new ArrayList();
128:
129: private LayerManagerProxy layerManagerProxy;
130:
131: JScrollPane scrollPane = new JScrollPane();
132:
133: private FirableTreeModelWrapper firableTreeModelWrapper;
134:
135: // used to drag Layerables among Categories
136: private TreePath movingTreePath = null;
137:
138: private boolean firstTimeDragging = true;
139:
140: /**
141: * @param layerNamePopupMenu
142: * null for no popup menu on layer nodes
143: * @param categoryPopupMenu
144: * null for no popup menu on category nodes
145: */
146: public TreeLayerNamePanel(LayerManagerProxy layerManagerProxy,
147: TreeModel treeModel, RenderingManager renderingManager,
148: Map additionalNodeClassToTreeCellRendererMap) {
149: layerManagerProxy.getLayerManager().addLayerListener(this );
150: this .layerManagerProxy = layerManagerProxy;
151:
152: try {
153: jbInit();
154: } catch (Exception ex) {
155: ex.printStackTrace();
156: }
157:
158: firableTreeModelWrapper = new FirableTreeModelWrapper(treeModel);
159: tree.setModel(firableTreeModelWrapper);
160: layerTreeCellRenderer = new LayerTreeCellRenderer(
161: renderingManager);
162: renderingManager.getPanel().getViewport().addListener(
163: new ViewportListener() {
164: public void zoomChanged(Envelope modelEnvelope) {
165: // After a zoom, the scale may be outside the visible
166: // scale range for one or more layers, in which case we
167: // want to update the layer names to be grey. So
168: // repaint. [Jon Aquino 2005-03-10]
169: TreeLayerNamePanel.this .repaint();
170: }
171: });
172: setCellRenderer(additionalNodeClassToTreeCellRendererMap);
173: tree.getSelectionModel().setSelectionMode(
174: TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
175: tree.addMouseListener(new MouseAdapter() {
176:
177: public void mouseClicked(MouseEvent e) {
178: handleCheckBoxClick(e);
179: }
180:
181: public void mousePressed(MouseEvent e) {
182: if (e.getButton() == MouseEvent.BUTTON1) {
183: movingTreePath = tree.getPathForLocation(e.getX(),
184: e.getY());
185: // move only Layerables, not Categories
186: if (movingTreePath != null
187: && !(movingTreePath.getLastPathComponent() instanceof Layerable))
188: movingTreePath = null;
189: } else
190: movingTreePath = null;
191: }
192:
193: public void mouseReleased(MouseEvent e) {
194: if (e.getButton() != MouseEvent.BUTTON1
195: || movingTreePath == null)
196: return;
197:
198: Object node = movingTreePath.getLastPathComponent();
199: TreePath tpDestination = tree
200: .getClosestPathForLocation(e.getX(), e.getY());
201:
202: // Fix: When dragging a Layerable onto a Category, and then
203: // selecting a different layer, the last XOR placement of the
204: // dragbar would appear over the Category. Need to reset
205: // firstTimeDragging to true before returning.
206: movingTreePath = null;
207: firstTimeDragging = true;
208:
209: if (tpDestination == null) {
210: return;
211: }
212:
213: // remove remnants of horizontal drag bar by refreshing display
214: tree.repaint();
215: // Changed #update to #repaint -- less flickery for some reason
216: // [Jon Aquino 2004-03-17]
217:
218: // dragging a layerable
219: if (node instanceof Layerable) {
220: Layerable layerable = (Layerable) node;
221: int index = 0;
222: Category cat = null;
223:
224: if (tpDestination.getLastPathComponent() instanceof Layerable) {
225:
226: // Fix: When shift-clicking to select a range of nodes,
227: // last node would unselect because the layer would get
228: // removed then re-added. [Jon Aquino 2004-03-11]
229: if (layerable == tpDestination
230: .getLastPathComponent()) {
231: return;
232: }
233:
234: cat = getLayerManager().getCategory(
235: (Layerable) tpDestination
236: .getLastPathComponent());
237: index = tree.getModel().getIndexOfChild(
238: tpDestination.getParentPath()
239: .getLastPathComponent(),
240: tpDestination.getLastPathComponent());
241: } else if (tpDestination.getLastPathComponent() instanceof Category) {
242: cat = (Category) tpDestination
243: .getLastPathComponent();
244:
245: // Prevent unnecessary removals and re-additions
246: // [Jon Aquino 2004-03-11]
247: if (cat.contains(layerable)) {
248: return;
249: }
250:
251: } else {
252: // Can get here if the node is, for example, a LayerTreeModel.ColorThemingValue [Jon Aquino 2005-07-25]
253: return;
254: }
255:
256: getLayerManager().remove(layerable);
257: cat.add(index, layerable);
258: getLayerManager().fireLayerChanged(layerable,
259: LayerEventType.METADATA_CHANGED);
260: }
261: }
262: });
263: tree.addMouseMotionListener(new MouseMotionAdapter() {
264: int rowNew;
265:
266: int rowOld = -1;
267:
268: Rectangle dragBar;
269:
270: public void mouseDragged(MouseEvent e) {
271: // return if mouse is dragged while not originating on a tree
272: // node
273: if (movingTreePath == null) {
274: firstTimeDragging = true;
275: return;
276: }
277:
278: rowNew = tree.getClosestRowForLocation(e.getX(), e
279: .getY());
280: rowOld = tree.getRowForPath(movingTreePath);
281: // if the dragging of a row hasn't moved outside of the bounds
282: // of the currently selected row, don't show the horizontal drag
283: // bar.
284: if (rowNew == rowOld)
285: return;
286: if (!(tree.getPathForRow(rowNew).getLastPathComponent() instanceof Layer)) {
287: tree.expandRow(rowNew);
288: }
289:
290: Graphics2D g2 = (Graphics2D) tree.getGraphics();
291: g2.setColor(Color.RED);
292: g2.setXORMode(Color.WHITE);
293: // if this is the first time moving the dragbar, draw the
294: // dragbar so XOR drawing works properly
295: if (firstTimeDragging) {
296: rowOld = rowNew;
297: dragBar = new Rectangle(0, 0, tree.getWidth(), 3);
298: g2.fill(dragBar);
299: firstTimeDragging = false;
300: }
301:
302: // XOR drawing mode of horizontal drag bar
303: g2.fill(dragBar);
304: dragBar.setLocation(0, tree.getRowBounds(rowNew).y);
305: g2.fill(dragBar);
306:
307: rowOld = rowNew;
308: }
309: });
310: tree.setCellEditor(cellEditor);
311: tree.setInvokesStopCellEditing(true);
312: tree.setBackground(getBackground());
313: tree.addTreeSelectionListener(new TreeSelectionListener() {
314: public void valueChanged(TreeSelectionEvent e) {
315: fireLayerSelectionChanged();
316: }
317: });
318: tree.getModel().addTreeModelListener(new TreeModelListener() {
319: public void treeNodesChanged(TreeModelEvent e) {
320: }
321:
322: public void treeNodesInserted(TreeModelEvent e) {
323: for (int i = 0; i < e.getChildren().length; i++) {
324: TreeUtil.visit(tree.getModel(), e.getTreePath()
325: .pathByAddingChild(e.getChildren()[i]),
326: new TreeUtil.Visitor() {
327: public void visit(Stack path) {
328: // When opening a task file, don't expand the ColorThemingValues.
329: // [Jon Aquino 2005-08-01]
330: if (path.peek() instanceof LayerTreeModel.ColorThemingValue) {
331: return;
332: }
333: tree.makeVisible(new TreePath(path
334: .toArray()));
335: }
336: });
337: }
338: }
339:
340: public void treeNodesRemoved(TreeModelEvent e) {
341: }
342:
343: public void treeStructureChanged(TreeModelEvent e) {
344: }
345: });
346: TreeUtil.expandAll(tree,
347: new TreePath(tree.getModel().getRoot()));
348: }
349:
350: public void addPopupMenu(Class nodeClass, JPopupMenu popupMenu) {
351: nodeClassToPopupMenuMap.put(nodeClass, popupMenu);
352: }
353:
354: private void setCellRenderer(
355: Map additionalNodeClassToTreeCellRendererMap) {
356: final Map map = createNodeClassToTreeCellRendererMap();
357: map.putAll(additionalNodeClassToTreeCellRendererMap);
358: tree.setCellRenderer(new TreeCellRenderer() {
359: private DefaultTreeCellRenderer defaultRenderer = new DefaultTreeCellRenderer() {
360:
361: {
362: // Transparent. [Jon Aquino]
363: setBackgroundNonSelectionColor(new Color(0, 0, 0, 0));
364: }
365: };
366:
367: public Component getTreeCellRendererComponent(JTree tree,
368: Object value, boolean selected, boolean expanded,
369: boolean leaf, int row, boolean hasFocus) {
370: return ((TreeCellRenderer) LangUtil.ifNull(
371: CollectionUtil.get(value.getClass(), map),
372: defaultRenderer)).getTreeCellRendererComponent(
373: tree, value, selected, expanded, leaf, row,
374: hasFocus);
375: }
376: });
377: }
378:
379: private Map createNodeClassToTreeCellRendererMap() {
380: HashMap map = new HashMap();
381: map.put(Layer.class, layerTreeCellRenderer);
382: map.put(WMSLayer.class, layerTreeCellRenderer);
383: map.put(Category.class, layerTreeCellRenderer);
384: map.put(LayerTreeModel.ColorThemingValue.class,
385: createColorThemingValueRenderer());
386: return map;
387: }
388:
389: private TreeCellRenderer createColorThemingValueRenderer() {
390: return new TreeCellRenderer() {
391: private JPanel panel = new JPanel(new GridBagLayout());
392: private ColorPanel colorPanel = new ColorPanel();
393: private JLabel label = new JLabel();
394: {
395: panel.add(colorPanel, new GridBagConstraints(0, 0, 1,
396: 1, 0, 0, GridBagConstraints.WEST,
397: GridBagConstraints.NONE,
398: new Insets(0, 0, 0, 0), 0, 0));
399: panel.add(label, new GridBagConstraints(1, 0, 1, 1, 0,
400: 0, GridBagConstraints.WEST,
401: GridBagConstraints.NONE,
402: new Insets(0, 5, 0, 0), 0, 0));
403: }
404:
405: public Component getTreeCellRendererComponent(JTree tree,
406: Object value, boolean selected, boolean expanded,
407: boolean leaf, int row, boolean hasFocus) {
408: label
409: .setText(((LayerTreeModel.ColorThemingValue) value)
410: .toString());
411: BasicStyle style = ((LayerTreeModel.ColorThemingValue) value)
412: .getStyle();
413: colorPanel
414: .setLineColor(style.isRenderingLine() ? GUIUtil
415: .alphaColor(style.getLineColor(), style
416: .getAlpha()) : GUIUtil
417: .alphaColor(Color.BLACK, 0));
418: colorPanel
419: .setFillColor(style.isRenderingFill() ? GUIUtil
420: .alphaColor(style.getFillColor(), style
421: .getAlpha()) : GUIUtil
422: .alphaColor(Color.BLACK, 0));
423: return panel;
424: }
425: };
426: }
427:
428: void jbInit() throws Exception {
429: this .setLayout(borderLayout1);
430: tree.addMouseListener(new java.awt.event.MouseAdapter() {
431: public void mouseReleased(MouseEvent e) {
432: tree_mouseReleased(e);
433: }
434: });
435: ToolTipManager.sharedInstance().registerComponent(tree);
436: tree.setEditable(true);
437: tree.setRootVisible(false);
438:
439: // Row height is set to -1 because otherwise, in Java 1.4, tree nodes
440: // will be "chopped off" at the bottom [Jon Aquino]
441: tree.setRowHeight(-1);
442: scrollPane.getVerticalScrollBar().setUnitIncrement(20);
443: tree.setShowsRootHandles(true);
444: scrollPane
445: .setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
446: scrollPane.setBorder(BorderFactory.createEtchedBorder());
447: scrollPane.getViewport().add(tree);
448: this .add(scrollPane, BorderLayout.CENTER);
449: }
450:
451: void tree_mouseReleased(MouseEvent e) {
452: if (!SwingUtilities.isRightMouseButton(e)) {
453: return;
454: }
455:
456: TreePath popupPath = tree
457: .getPathForLocation(e.getX(), e.getY());
458:
459: if (popupPath == null) {
460: return;
461: }
462:
463: popupNode = popupPath.getLastPathComponent();
464:
465: // #isAltDown returns true on a middle-click; #isMetaDown returns true
466: // on a right-click[Jon Aquino]
467: // Third check can't simply user JTree#isPathSelected because the node
468: // wrappers are value objects and thus can't reliably be compared by
469: // reference (which is what #isPathSelected seems to do). [Jon Aquino]
470: if (!(e.isControlDown() || e.isShiftDown() || selectedNodes(
471: Object.class).contains(popupNode))) {
472: tree.getSelectionModel().clearSelection();
473: }
474:
475: tree.getSelectionModel().addSelectionPath(popupPath);
476:
477: if (getPopupMenu(popupNode.getClass()) != null) {
478: getPopupMenu(popupNode.getClass()).show(e.getComponent(),
479: e.getX(), e.getY());
480: }
481: }
482:
483: private JPopupMenu getPopupMenu(Class nodeClass) {
484: return (JPopupMenu) CollectionUtil.get(nodeClass,
485: nodeClassToPopupMenuMap);
486: }
487:
488: private void handleCheckBoxClick(MouseEvent e) {
489: if (!SwingUtilities.isLeftMouseButton(e)) {
490: return;
491: }
492:
493: TreePath path = tree.getPathForLocation(e.getX(), e.getY());
494:
495: if (path == null) {
496: return;
497: }
498:
499: Object node = path.getLastPathComponent();
500:
501: if (!(node instanceof Layerable)) {
502: return;
503: }
504:
505: Layerable layerable = (Layerable) node;
506: Point layerNodeLocation = tree.getUI()
507: .getPathBounds(tree, path).getLocation();
508:
509: // Initialize the LayerNameRenderer with the current node.
510: // checkBoxBounds will be different for Layers and WMSLayers. [Jon
511: // Aquino]
512: layerTreeCellRenderer.getLayerNameRenderer()
513: .getTreeCellRendererComponent(tree,
514: path.getLastPathComponent(), false, false,
515: false, 0, false);
516:
517: Rectangle checkBoxBounds = layerTreeCellRenderer
518: .getLayerNameRenderer().getCheckBoxBounds();
519: checkBoxBounds.translate((int) layerNodeLocation.getX(),
520: (int) layerNodeLocation.getY());
521:
522: if (checkBoxBounds.contains(e.getPoint())) {
523: layerable.setVisible(!layerable.isVisible());
524: }
525: }
526:
527: public Layer[] getSelectedLayers() {
528: return selectedLayers(this );
529: }
530:
531: public static Layer[] selectedLayers(LayerNamePanel layerNamePanel) {
532: return (Layer[]) layerNamePanel.selectedNodes(Layer.class)
533: .toArray(new Layer[] {});
534: }
535:
536: public Collection getSelectedCategories() {
537: return selectedNodes(Category.class);
538: }
539:
540: public Collection selectedNodes(Class c) {
541: return selectedNodes(c, tree);
542: }
543:
544: public static Collection selectedNodes(Class c, JTree tree) {
545: ArrayList selectedNodes = new ArrayList();
546: TreePath[] selectionPaths = tree.getSelectionPaths();
547:
548: if (selectionPaths == null) {
549: return new ArrayList();
550: }
551:
552: for (int i = 0; i < selectionPaths.length; i++) {
553: Object node = selectionPaths[i].getLastPathComponent();
554:
555: if (c.isInstance(node)) {
556: selectedNodes.add(node);
557: }
558: }
559:
560: return selectedNodes;
561: }
562:
563: private void setSelectedLayers(Layer[] layers) {
564: tree.getSelectionModel().clearSelection();
565:
566: for (int i = 0; i < layers.length; i++) {
567: addSelectedLayer(layers[i]);
568: }
569: }
570:
571: protected void addSelectedLayer(Layer layer) {
572: tree.addSelectionPath(TreeUtil.findTreePath(layer, tree
573: .getModel()));
574: }
575:
576: public void layerChanged(final LayerEvent e) {
577: TreeModelEvent treeModelEvent = new TreeModelEvent(this ,
578: new Object[] { tree.getModel().getRoot(),
579: e.getCategory() }, new int[] { e
580: .getLayerableIndex() }, new Object[] { e
581: .getLayerable() });
582:
583: if (e.getType() == LayerEventType.ADDED) {
584: firableTreeModelWrapper
585: .fireTreeNodesInserted(treeModelEvent);
586:
587: // firableTreeModelWrapper.fireTreeStructureChanged(treeModelEvent);
588: if ((e.getType() == LayerEventType.ADDED)
589: && ((selectedNodes(Layerable.class)).size() == 0)
590: && e.getLayerable() instanceof Layer) {
591: addSelectedLayer((Layer) e.getLayerable());
592: }
593:
594: return;
595: }
596:
597: if (e.getType() == LayerEventType.REMOVED) {
598: firableTreeModelWrapper
599: .fireTreeNodesRemoved(treeModelEvent);
600:
601: return;
602: }
603:
604: if (e.getType() == LayerEventType.APPEARANCE_CHANGED) {
605: // For some reason, if we don't use #invokeLater to call #fireTreeStructureChanged,
606: // blank lines get inserted into the JTree. For more information, see Java Bug 4498762,
607: // "When expandPath() is called by a JTree method, extra blank lines appear",
608: // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4498762
609: // [Jon Aquino 2005-07-25]
610: SwingUtilities.invokeLater(new Runnable() {
611: public void run() {
612: // Specify the path of the subtree rooted at the layer -- the ColorThemingValues
613: // may have changed. [Jon Aquino 2005-07-25]
614: firableTreeModelWrapper
615: .fireTreeStructureChanged(new TreeModelEvent(
616: this , new Object[] {
617: tree.getModel().getRoot(),
618: e.getCategory(),
619: e.getLayerable() }));
620: }
621: });
622: return;
623: }
624:
625: if (e.getType() == LayerEventType.METADATA_CHANGED) {
626: firableTreeModelWrapper
627: .fireTreeNodesChanged(treeModelEvent);
628:
629: return;
630: }
631:
632: if (e.getType() == LayerEventType.VISIBILITY_CHANGED) {
633: firableTreeModelWrapper
634: .fireTreeNodesChanged(treeModelEvent);
635:
636: return;
637: }
638:
639: Assert.shouldNeverReachHere();
640: }
641:
642: public void categoryChanged(CategoryEvent e) {
643: TreeModelEvent treeModelEvent = new TreeModelEvent(this ,
644: new Object[] { tree.getModel().getRoot() },
645: new int[] { e.getCategoryIndex()
646: + indexOfFirstCategoryInTree() },
647: new Object[] { e.getCategory() });
648:
649: if (e.getType() == CategoryEventType.ADDED) {
650: firableTreeModelWrapper
651: .fireTreeNodesInserted(treeModelEvent);
652:
653: return;
654: }
655:
656: if (e.getType() == CategoryEventType.REMOVED) {
657: firableTreeModelWrapper
658: .fireTreeNodesRemoved(treeModelEvent);
659:
660: return;
661: }
662:
663: if (e.getType() == CategoryEventType.METADATA_CHANGED) {
664: firableTreeModelWrapper
665: .fireTreeNodesChanged(treeModelEvent);
666:
667: return;
668: }
669:
670: Assert.shouldNeverReachHere();
671: }
672:
673: private int indexOfFirstCategoryInTree() {
674: // Not 0 in ESE. [Jon Aquino]
675: for (int i = 0; i < tree.getModel().getChildCount(
676: tree.getModel().getRoot()); i++) {
677: if (tree.getModel().getChild(tree.getModel().getRoot(), i) instanceof Category) {
678: return i;
679: }
680: }
681:
682: Assert.shouldNeverReachHere();
683:
684: return -1;
685: }
686:
687: public void featuresChanged(FeatureEvent e) {
688: }
689:
690: public void dispose() {
691: // Layer events could still be fired after the TaskWindow containing
692: // this LayerNamePanel is closed (e.g. by clones of the TaskWindow, or
693: // by an attribute viewer). [Jon Aquino]
694: layerManagerProxy.getLayerManager().removeLayerListener(this );
695: }
696:
697: public JTree getTree() {
698: return tree;
699: }
700:
701: public void addListener(LayerNamePanelListener listener) {
702: listeners.add(listener);
703: }
704:
705: public void removeListener(LayerNamePanelListener listener) {
706: listeners.remove(listener);
707: }
708:
709: public void fireLayerSelectionChanged() {
710: for (Iterator i = listeners.iterator(); i.hasNext();) {
711: LayerNamePanelListener l = (LayerNamePanelListener) i
712: .next();
713: l.layerSelectionChanged();
714: }
715: }
716:
717: public LayerManager getLayerManager() {
718: return layerManagerProxy.getLayerManager();
719: }
720:
721: public static Layer chooseEditableLayer(LayerNamePanel panel) {
722: for (Iterator i = Arrays.asList(panel.getSelectedLayers())
723: .iterator(); i.hasNext();) {
724: Layer layer = (Layer) i.next();
725:
726: if (layer.isEditable()) {
727: return layer;
728: }
729: }
730:
731: if (panel.getLayerManager().getEditableLayers().isEmpty()) {
732: return null;
733: }
734:
735: return (Layer) panel.getLayerManager().getEditableLayers()
736: .iterator().next();
737: }
738:
739: public Layer chooseEditableLayer() {
740: return chooseEditableLayer(this );
741: }
742:
743: public LayerNamePanel getLayerNamePanel() {
744: return this ;
745: }
746:
747: protected FirableTreeModelWrapper getFirableTreeModelWrapper() {
748: return firableTreeModelWrapper;
749: }
750:
751: public Object getPopupNode() {
752: return popupNode;
753: }
754:
755: protected LayerTreeCellRenderer getLayerTreeCellRenderer() {
756: return layerTreeCellRenderer;
757: }
758:
759: }
|