Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.app / org.gvsig.app.mainplugin / src / main / java / org / gvsig / app / project / documents / view / toc / gui / TOC.java @ 43955

History | View | Annotate | Download (40.3 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.app.project.documents.view.toc.gui;
24

    
25
import java.awt.BorderLayout;
26
import java.awt.Dimension;
27
import java.awt.Font;
28
import java.awt.FontMetrics;
29
import java.awt.Image;
30
import java.awt.Point;
31
import java.awt.Rectangle;
32
import java.awt.event.ComponentEvent;
33
import java.awt.event.ComponentListener;
34
import java.awt.event.InputEvent;
35
import java.awt.event.MouseAdapter;
36
import java.awt.event.MouseEvent;
37
import java.text.MessageFormat;
38
import java.util.ArrayList;
39
import java.util.Enumeration;
40
import java.util.HashMap;
41
import java.util.List;
42
import java.util.Map;
43

    
44
import javax.swing.BorderFactory;
45
import javax.swing.JColorChooser;
46
import javax.swing.JComponent;
47
import javax.swing.JDialog;
48
import javax.swing.JScrollPane;
49
import javax.swing.JTree;
50
import javax.swing.SwingUtilities;
51
import javax.swing.event.TreeExpansionEvent;
52
import javax.swing.event.TreeExpansionListener;
53
import javax.swing.tree.DefaultMutableTreeNode;
54
import javax.swing.tree.DefaultTreeModel;
55
import javax.swing.tree.TreeNode;
56
import javax.swing.tree.TreePath;
57
import javax.swing.tree.TreeSelectionModel;
58

    
59
import org.apache.commons.lang.BooleanUtils;
60
import org.slf4j.Logger;
61
import org.slf4j.LoggerFactory;
62

    
63
import org.gvsig.andami.PluginServices;
64
import org.gvsig.andami.messages.NotificationManager;
65
import org.gvsig.app.ApplicationLocator;
66
import org.gvsig.app.project.ProjectPreferences;
67
import org.gvsig.app.project.documents.view.IContextMenuAction;
68
import org.gvsig.app.project.documents.view.toc.DnDJTree;
69
import org.gvsig.app.project.documents.view.toc.ITocItem;
70
import org.gvsig.app.project.documents.view.toc.ITocOrderListener;
71
import org.gvsig.app.project.documents.view.toc.TocItemBranch;
72
import org.gvsig.app.project.documents.view.toc.TocItemLeaf;
73
import org.gvsig.fmap.mapcontext.MapContext;
74
import org.gvsig.fmap.mapcontext.events.AtomicEvent;
75
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
76
import org.gvsig.fmap.mapcontext.layers.FLayer;
77
import org.gvsig.fmap.mapcontext.layers.FLayers;
78
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
79
import org.gvsig.fmap.mapcontext.layers.operations.Classifiable;
80
import org.gvsig.fmap.mapcontext.layers.operations.IHasImageLegend;
81
import org.gvsig.fmap.mapcontext.layers.operations.LayerCollection;
82
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
83
import org.gvsig.fmap.mapcontext.rendering.legend.IClassifiedLegend;
84
import org.gvsig.fmap.mapcontext.rendering.legend.ILegend;
85
import org.gvsig.fmap.mapcontext.rendering.legend.ISingleSymbolLegend;
86
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendChangedEvent;
87
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
88
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
89

    
90
/**
91
 *
92
 * @author fjp
93
 */
94
public class TOC extends JComponent {
95

    
96
    /**
97
     *
98
     */
99
    private static final long serialVersionUID = 5689047685537359038L;
100

    
101
    private static final Logger logger = LoggerFactory.getLogger(TOC.class);
102

    
103
    private static class ItemsExpandeds {
104

    
105
        private final Map<Object, Boolean> itemsExpanded = new HashMap<>();
106
        private boolean expandingNodes = false;
107

    
108
        public boolean isMarked(ITocItem item) {
109
            Object key = item.getLabel();
110
            boolean isItemExpanded = BooleanUtils.isTrue(this.itemsExpanded.get(key));
111
            return isItemExpanded;
112
        }
113

    
114
        public void setMark(ITocItem item, boolean expanded) {
115
            Object key = item.getLabel();
116
            this.itemsExpanded.put(key, expanded);
117
        }
118

    
119
        public void removeLayer(FLayer layer) {
120
            Object key = layer.getName();
121
            this.itemsExpanded.remove(key);
122
        }
123

    
124
        public void update(JTree tree, TreeNode node) {
125
            if (expandingNodes) {
126
                return;
127
            }
128
            try {
129
                expandingNodes = true;
130
                DefaultTreeModel treeModel = (DefaultTreeModel) tree.getModel();
131
                Enumeration<TreeNode> nodes = (Enumeration<TreeNode>)(node.children());
132
                while (nodes.hasMoreElements()) {
133
                    DefaultMutableTreeNode curNode = (DefaultMutableTreeNode) nodes.nextElement();
134
                    if (curNode.getChildCount() > 0) {
135
                        update(tree, curNode);
136
                    }
137
                    TreePath path = new TreePath(treeModel.getPathToRoot(curNode));
138
                    ITocItem item = (ITocItem) curNode.getUserObject();
139
                    if (this.isMarked(item)) {
140
                        tree.expandPath(path);
141
                    } else {
142
                        tree.collapsePath(path);
143
                    }
144
                }
145
            } finally {
146
                expandingNodes = false;
147
            }
148

    
149
        }
150
    }
151

    
152
    private MapContext mapContext;
153

    
154
    private final DnDJTree m_Tree;
155
    private final DefaultTreeModel m_TreeModel;
156
    private final DefaultMutableTreeNode m_Root;
157
    private final TOCRenderer m_TocRenderer;
158
    private final JScrollPane m_Scroller;
159

    
160
    private final ItemsExpandeds itemsExpandeds = new ItemsExpandeds();
161

    
162
    private NodeSelectionListener nodeSelectionListener = null;
163

    
164
    /**
165
     * Crea un nuevo TOC.
166
     */
167
    public TOC() {
168
        this.setName("TOC");
169
        this.setLayout(new BorderLayout());
170
        this.setMinimumSize(new Dimension(100, 80));
171
        this.setPreferredSize(new Dimension(100, 80));
172

    
173
        m_Root = new DefaultMutableTreeNode(java.lang.Object.class);
174
        m_TreeModel = new DefaultTreeModel(m_Root);
175
        m_Tree = new DnDJTree(m_TreeModel);
176

    
177
        m_TocRenderer = new TOCRenderer(m_Tree.getBackground());
178
        m_Tree.setCellRenderer(m_TocRenderer);
179
        m_Tree.setRootVisible(false);
180
        m_Tree.setShowsRootHandles(true);
181
        m_Tree.getSelectionModel().setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
182
        nodeSelectionListener = new NodeSelectionListener(m_Tree);
183
        m_Tree.addMouseListener(nodeSelectionListener);
184

    
185
        this.addComponentListener(new ComponentListener() {
186

    
187
            @Override
188
            public void componentResized(ComponentEvent e) {
189
                tocResized();
190
            }
191

    
192
            @Override
193
            public void componentMoved(ComponentEvent e) {
194
            }
195

    
196
            @Override
197
            public void componentShown(ComponentEvent e) {
198
            }
199

    
200
            @Override
201
            public void componentHidden(ComponentEvent e) {
202
            }
203
        });
204

    
205
        m_Tree.addOrderListener(new ITocOrderListener() {
206

    
207
            @Override
208
            public void orderChanged(int oldPos, int newPos, FLayers layers) {
209
                try {
210
                    layers.moveTo(oldPos, newPos);
211
                } catch (Exception e) {
212
                    logger.warn("Can't change order of layers in TOC", e);
213
                }
214
                mapContext.invalidate();
215
            }
216

    
217
            @Override
218
            public void parentChanged(FLayers source, FLayers targer, FLayer layer) {
219
                try {
220
                    source.move(layer, targer);
221
                } catch (Exception e) {
222
                    logger.warn("Can't move layers in TOC", e);
223
                }
224
                mapContext.invalidate();
225
            }
226
        });
227

    
228
        m_Tree.addTreeExpansionListener(new TreeExpansionListener() {
229

    
230
            @Override
231
            public void treeCollapsed(TreeExpansionEvent event) {
232
                TreePath path = event.getPath();
233
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
234
                if (node.getUserObject() instanceof ITocItem) {
235
                    itemsExpandeds.setMark((ITocItem) node.getUserObject(), false);
236
                }
237
            }
238

    
239
            @Override
240
            public void treeExpanded(TreeExpansionEvent event) {
241
                TreePath path = event.getPath();
242
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
243
                if (node.getUserObject() instanceof ITocItem) {
244
                    itemsExpandeds.setMark((ITocItem) node.getUserObject(), true);
245
                }
246
            }
247
        });
248

    
249
        m_Tree.setRowHeight(0); // Para que lo determine el renderer
250

    
251
        m_Scroller = new JScrollPane(m_Tree);
252
        m_Scroller.setBorder(BorderFactory.createEmptyBorder());
253

    
254
        add(m_Scroller);
255

    
256
    }
257

    
258
    /**
259
     * Elimina los listeners que actuan sobre el TOC, lo ?nico que deja hacer es
260
     * desplegar la leyenda de las capas.
261
     */
262
    public void removeListeners() {
263
        m_Tree.removeMouseListener(nodeSelectionListener);
264
        m_Tree.invalidateListeners();
265
    }
266

    
267
    /**
268
     * @param mc
269
     */
270
    public void setMapContext(MapContext mc) {
271
        this.mapContext = mc;
272
        this.mapContext.addAtomicEventListener(new AtomicEventListener() {
273

    
274
            @Override
275
            public void atomicEvent(final AtomicEvent e) {
276
                if (!SwingUtilities.isEventDispatchThread()) {
277
                    SwingUtilities.invokeLater(new Runnable() {
278

    
279
                        @Override
280
                        public void run() {
281
                            atomicEvent(e);
282
                        }
283
                    });
284
                    return;
285
                }
286

    
287
                if ((e.getLayerCollectionEvents().length > 0) || (e.getLegendEvents().length > 0)) {
288
                    reloadLayers();
289
                }
290

    
291
                if (e.getLayerEvents().length > 0) {
292
                    repaint();
293
                }
294

    
295
                if (e.getExtentEvents().length > 0) {
296
                    repaint();
297
                }
298
                LayerCollectionEvent[] events = e.getLayerCollectionEvents();
299
                for (int i = 0; i < events.length; i++) {
300
                    if (events[i].getEventType() == LayerCollectionEvent.LAYER_ADDED) {
301
                        if (PluginServices.getMainFrame() != null) {
302
                            PluginServices.getMainFrame().enableControls();
303
                        }
304
                    }
305
                    // ===================================================
306
                    // Change visibility of layer before adding, according to app preferences
307
                    if (events[i].getEventType() == LayerCollectionEvent.LAYER_ADDING) {
308
                        if (haveToAddNewLayersInInvisibleMode()) {
309
                            events[i].getAffectedLayer().setVisible(false);
310
                        }
311
                    }
312
                }
313
            }
314
        });
315

    
316
        this.mapContext.getLayers().addLegendListener(new LegendListener() {
317

    
318
            @Override
319
            public void legendChanged(LegendChangedEvent e) {
320
                reloadLayers();
321
            }
322
        });
323

    
324
        reloadLayers();
325
    }
326

    
327
    /**
328
     *
329
     */
330
    public void refresh() {
331
        reloadLayers();
332
    }
333

    
334
    /**
335
     *
336
     */
337
    public void reloadLayers() {
338
        if (!SwingUtilities.isEventDispatchThread()) {
339
            SwingUtilities.invokeLater(new Runnable() {
340

    
341
                @Override
342
                public void run() {
343
                    reloadLayers();
344
                }
345
            });
346
            return;
347
        }
348
        LayerCollection theLayers = mapContext.getLayers();
349
        m_Root.removeAllChildren();
350
        m_Root.setAllowsChildren(true);
351
        reloadLayers(theLayers, m_Root);
352

    
353
        m_TreeModel.reload();
354

    
355
        itemsExpandeds.update(m_Tree, m_Root);
356
    }
357

    
358
    private void reloadLayers(LayerCollection theLayers, DefaultMutableTreeNode parentNode) {
359

    
360
        int width = m_Tree.getWidth();
361
        if (width == 0) {
362
            width = 300;
363
        }
364

    
365
        // Get the tree font height
366
        Font font = m_Tree.getFont();
367
        FontMetrics metrics = this.getFontMetrics(font);
368
        Dimension sizeBranch = new Dimension(width, metrics.getHeight() + 4);
369

    
370
        for (int i = theLayers.getLayersCount() - 1; i >= 0; i--) {
371
            FLayer lyr = theLayers.getLayer(i);
372
            if (!lyr.isInTOC()) {
373
                continue;
374
            }
375
            TocItemBranch elTema = new TocItemBranch(lyr);
376
            elTema.setSize(sizeBranch);
377

    
378
            DefaultMutableTreeNode nodeLayer = new DefaultMutableTreeNode(elTema);
379

    
380
            m_TreeModel.insertNodeInto(nodeLayer, parentNode, parentNode.getChildCount());
381
            if (lyr instanceof LayerCollection) {
382
                LayerCollection group = (LayerCollection) lyr;
383
                reloadLayers(group, nodeLayer);
384

    
385
            } else if (haveToShowLeyendOfLayer(lyr)) {
386
                this.addLegend(nodeLayer, lyr);
387
            }
388
        }
389
    }
390

    
391
    private void addLegend(final DefaultMutableTreeNode nodeLayer, FLayer lyr) {
392
        int width = m_Tree.getWidth();
393
        if (width == 0) {
394
            width = 300;
395
        }
396
        Dimension sizeLeaf = new Dimension(width, 15);
397

    
398
        if (lyr instanceof Classifiable) {
399
            Classifiable classifiable = (Classifiable) lyr;
400
            ILegend legendInfo = classifiable.getLegend();
401

    
402
            try {
403
                if (legendInfo instanceof IClassifiedLegend) {
404
                    IClassifiedLegend cl = (IClassifiedLegend) legendInfo;
405
                    String[] descriptions = cl.getDescriptions();
406
                    ISymbol[] symbols = cl.getSymbols();
407

    
408
                    for (int j = 0; j < descriptions.length; j++) {
409
                        TocItemLeaf itemLeaf;
410
                        itemLeaf = new TocItemLeaf(symbols[j], descriptions[j], classifiable.getShapeType());
411
                        itemLeaf.setSize(sizeLeaf);
412

    
413
                        DefaultMutableTreeNode nodeValue = new DefaultMutableTreeNode(itemLeaf);
414
                        m_TreeModel.insertNodeInto(nodeValue, nodeLayer, nodeLayer.getChildCount());
415

    
416
                    }
417
                }
418

    
419
                if (legendInfo instanceof ISingleSymbolLegend && (legendInfo.getDefaultSymbol() != null)) {
420
                    TocItemLeaf itemLeaf;
421
                    itemLeaf =
422
                        new TocItemLeaf(legendInfo.getDefaultSymbol(), legendInfo.getDefaultSymbol().getDescription(),
423
                            classifiable.getShapeType());
424
                    itemLeaf.setSize(sizeLeaf);
425

    
426
                    DefaultMutableTreeNode nodeValue = new DefaultMutableTreeNode(itemLeaf);
427
                    m_TreeModel.insertNodeInto(nodeValue, nodeLayer, nodeLayer.getChildCount());
428
                }
429

    
430
                if (legendInfo instanceof IHasImageLegend) {
431
                    final IHasImageLegend imageLegend = (IHasImageLegend) legendInfo;
432
                    // Las imagenes de la leyenda que vienen de servicios
433
                    // remotos pueden
434
                    // ser lentas de pintar y relentizan todo gvSIG, asi que
435
                    // mejor si
436
                    // podemos hacerlo en segundo plano.
437
                    Thread thread = new Thread(new Runnable() {
438

    
439
                        @Override
440
                        public void run() {
441
                            final Image image = imageLegend.getImageLegend();
442
                            // Una vez obtenida la imagen, la actualizacion del
443
                            // ToC
444
                            // la hacemos en el thread de Swing para evitar
445
                            // problemas.
446
                            SwingUtilities.invokeLater(new Runnable() {
447

    
448
                                @Override
449
                                public void run() {
450
                                    int w = 0;
451
                                    int h = 0;
452

    
453
                                    if (image != null) {
454
                                        w = image.getWidth(null);
455
                                        h = image.getHeight(null);
456
                                    }
457

    
458
                                    if (image != null && w > 0 && h > 0) {
459
                                        TocItemLeaf itemLeaf = new TocItemLeaf();
460
                                        itemLeaf.setImageLegend(image, "", new Dimension(w, h));
461
                                        DefaultMutableTreeNode nodeValue = new DefaultMutableTreeNode(itemLeaf);
462
                                        m_TreeModel.insertNodeInto(nodeValue, nodeLayer, nodeLayer.getChildCount());
463
                                    }
464
                                    // Aqui habria que forzar a refrescar el
465
                                    // nodo nodeLayer
466
                                }
467
                            });
468
                        }
469
                    }, "ToCImageLegendUpdate");
470
                    thread.start();
471
                }
472
            } catch (Throwable e) {
473
                logger.warn(MessageFormat.format("Can't add leyend of layer {0} to the TOC.", lyr), e);
474
            }
475
        }
476

    
477
    }
478

    
479
    private void tocResized() {
480
        DefaultMutableTreeNode n;
481
        Enumeration<TreeNode> enumeration = m_Root.children();
482

    
483
        while (enumeration.hasMoreElements()) {
484
            n = (DefaultMutableTreeNode) enumeration.nextElement();
485

    
486
            if (n.getUserObject() instanceof TocItemBranch) {
487
                ITocItem item = (ITocItem) n.getUserObject();
488
                Dimension szAnt = item.getSize();
489
                item.setSize(new Dimension(this.getWidth() - 40, szAnt.height));
490
            }
491

    
492
        }
493
    }
494

    
495
    /**
496
     * Obtiene el JScrollPane que contiene el TOC
497
     *
498
     * @return JScrollPane que contiene el TOC
499
     */
500
    public JScrollPane getJScrollPane() {
501
        return this.m_Scroller;
502
    }
503

    
504
    /**
505
     * @return
506
     */
507
    public DnDJTree getTree() {
508
        return m_Tree;
509
    }
510

    
511
    /**
512
     * Clase Listener que reacciona al pulsar sobre el checkbox de un nodo y
513
     * crea un popupmenu al pulsar el bot?n derecho.
514
     */
515
    class NodeSelectionListener extends MouseAdapter { // implements
516

    
517
                                                       // ActionListener {
518

    
519
        JTree tree;
520
        JDialog dlg;
521
        JColorChooser colorChooser;
522
        FPopupMenu popmenu = null;
523
        DefaultMutableTreeNode node;
524

    
525
        /**
526
         * Crea un nuevo NodeSelectionListener.
527
         *
528
         * @param tree
529
         *            !
530
         */
531
        NodeSelectionListener(JTree tree) {
532
            this.tree = tree;
533
        }
534

    
535
        @Override
536
        public void mouseClicked(MouseEvent e) {
537
            int x = e.getX();
538
            int y = e.getY();
539
            int row = tree.getRowForLocation(x, y);
540
            TreePath path = tree.getPathForRow(row);
541
            final FLayers layers = mapContext.getLayers();
542
            List<FLayer> plainListLayers = toPlainList(layers);
543
            plainListLayers.remove(plainListLayers.size() - 1);
544
            int countDeepActiveLayers = countDeepActiveLayers(plainListLayers);
545

    
546
            if (path != null) {
547
                if (e.getClickCount() == 1) {
548
                    // this fixes a bug when double-clicking. JTree by default
549
                    // expands the tree when double-clicking, so we capture a
550
                    // different node in the second click than in the first
551
                    node = (DefaultMutableTreeNode) path.getLastPathComponent();
552
                }
553

    
554
                if (node != null && node.getUserObject() instanceof TocItemBranch) {
555
                    // double click with left button ON A BRANCH/NODE (layer)
556
                    if (e.getClickCount() >= 2 && e.getButton() == MouseEvent.BUTTON1) {
557
                        e.consume();
558
                        PluginServices.getMDIManager().setWaitCursor();
559
                        try {
560
                            TocItemBranch leaf = (TocItemBranch) node.getUserObject();
561
                            IContextMenuAction action = leaf.getDoubleClickAction();
562
                            if (action != null) {
563
                                /*
564
                                 * if there is an action associated with the
565
                                 * double-clicked element it will be fired for
566
                                 * it and FOR ALL OTHER COMPATIBLES THAT HAVE
567
                                 * BEEN ACTIVATED.
568
                                 */
569
                                ArrayList<FLayer> targetLayers = new ArrayList<>();
570

    
571
                                TocItemBranch owner = (TocItemBranch) node.getUserObject();
572

    
573
                                FLayer masterLayer = owner.getLayer();
574
                                targetLayers.add(masterLayer);
575
                                FLayer[] actives = mapContext.getLayers().getActives();
576
                                for (int i = 0; i < actives.length; i++) {
577
                                    if (actives[i].getClass().equals(masterLayer.getClass())) {
578
                                        if (actives[i] instanceof FLyrVect) {
579
                                            FLyrVect vectorLayer = (FLyrVect) actives[i];
580
                                            FLyrVect vectorMaster = (FLyrVect) masterLayer;
581
                                            if (vectorLayer.getShapeType() == vectorMaster.getShapeType()) {
582
                                                targetLayers.add(vectorLayer);
583
                                                vectorLayer.setActive(true);
584
                                            } else {
585
                                                vectorLayer.setActive(false);
586
                                            }
587
                                        }
588
                                        // TODO for the rest of layer types
589
                                        // (i.e. FLyrRaster)
590
                                    } else {
591
                                        actives[i].setActive(false);
592
                                    }
593
                                }
594

    
595
                                // Do nothing if there is a non-available layer
596
                                for (int k = 0; k < targetLayers.size(); k++) {
597
                                    if (!targetLayers.get(k).isAvailable()) {
598
                                        return;
599
                                    }
600
                                    targetLayers.get(k).setActive(true);
601
                                }
602

    
603
                                action.execute(leaf, targetLayers.toArray(new FLayer[0]));
604
                            }
605
                        } catch (Exception ex) {
606
                            NotificationManager.addError(ex);
607
                        } finally {
608
                            PluginServices.getMDIManager().restoreCursor();
609
                        }
610
                        return;
611
                    }
612

    
613
                    TocItemBranch elTema = (TocItemBranch) node.getUserObject();
614
                    FLayer lyr = elTema.getLayer();
615
                    if( lyr.isAvailable() ) {
616
                        lyr.getMapContext().beginAtomicEvent();
617
                    }
618

    
619
                    // Si est? pulsado SHIFT
620
                    if (((e.getModifiers() & InputEvent.SHIFT_MASK) != 0)
621
                        && (e.getButton() == MouseEvent.BUTTON1 || e.getButton() == MouseEvent.BUTTON3)) {
622
                        if (countDeepActiveLayers > 0) {
623
                            selectInterval(plainListLayers, lyr);
624
                        } else {
625
                            lyr.setActive(!lyr.isActive());
626
                        }
627

    
628
                    } else { // Si no est? pulsado SHIFT
629
                        // Si no est? pulsado CTRL
630
                        if (!((e.getModifiers() & InputEvent.CTRL_MASK) != 0)) {
631
                            if (e.getButton() == MouseEvent.BUTTON1) {
632
                                if (countDeepActiveLayers > 1) {
633
                                    layers.setAllActives(false);
634
                                    lyr.setActive(true);
635
                                } else {
636
                                    boolean active = lyr.isActive();
637
                                    layers.setAllActives(false);
638
                                    lyr.setActive(!active);
639
                                }
640
                            }
641
                            if (e.getButton() == MouseEvent.BUTTON3) {
642
                                if (lyr.isActive()) {
643
                                    // No modificamos la selecci?n porque lo que
644
                                    // se va a hacer m?s abajo es abrir el men?
645
                                    // contextual
646
                                } else {
647
                                    boolean active = lyr.isActive();
648
                                    layers.setAllActives(false);
649
                                    lyr.setActive(!active);
650
                                }
651
                            }
652
                        } else { // Si s? est? pulsado CTRL
653
                            if (e.getButton() == MouseEvent.BUTTON1) {
654
                                // BUTTON1 cambiamos la activaci?n de la lyr
655
                                // seleccionada
656
                                lyr.setActive(!lyr.isActive());
657
                            }
658
                            if (e.getButton() == MouseEvent.BUTTON3) {
659
                                if (lyr.isActive()) {
660
                                    // No modificamos la selecci?n porque lo que
661
                                    // se va a hacer m?s abajo es abrir el men?
662
                                    // contextual
663
                                } else {
664
                                    lyr.setActive(true);
665
                                }
666
                            }
667
                        }
668
                    }
669

    
670
                    Point layerNodeLocation = tree.getUI().getPathBounds(tree, path).getLocation();
671

    
672
                    // Rect?ngulo que representa el checkbox
673
                    Rectangle checkBoxBounds = m_TocRenderer.getCheckBoxBounds();
674
                    checkBoxBounds.translate((int) layerNodeLocation.getX(), (int) layerNodeLocation.getY());
675

    
676
                    if (checkBoxBounds.contains(e.getPoint())) {
677
                        updateVisible(lyr);
678
                        // si node no tiene leyenda llamar a addLegend
679
                    }
680

    
681
                    if (e.getButton() == MouseEvent.BUTTON3) {
682
                        popmenu = new FPopupMenu(mapContext, node);
683
                        tree.add(popmenu);
684
                        popmenu.show(e.getComponent(), e.getX(), e.getY());
685
                    }
686
                    if( lyr.isAvailable() ) {
687
                        lyr.getMapContext().endAtomicEvent();
688
                    }                    
689
                }
690

    
691
                if (node != null && node.getUserObject() instanceof TocItemLeaf) {
692
                    TocItemBranch owner = (TocItemBranch) ((DefaultMutableTreeNode) node.getParent()).getUserObject();
693

    
694
                    FLayer masterLayer = owner.getLayer();
695

    
696
                    // double click with left button ON A LEAF (ISymbol)
697
                    if (e.getClickCount() >= 2 && e.getButton() == MouseEvent.BUTTON1) {
698
                        e.consume();
699

    
700
                        PluginServices.getMDIManager().setWaitCursor();
701
                        try {
702
                            TocItemLeaf leaf = (TocItemLeaf) node.getUserObject();
703
                            IContextMenuAction action = leaf.getDoubleClickAction();
704
                            if (action != null) {
705
                                /*
706
                                 * if there is an action associated with the
707
                                 * double-clicked element it will be fired for
708
                                 * it and FOR ALL OTHER COMPATIBLES THAT HAVE
709
                                 * BEEN ACTIVATED.
710
                                 */
711
                                /*
712
                                 * #3035: Symbology is applied to active layers
713
                                 * too
714
                                 * Now it will be done only on the
715
                                 * double-clicked one
716
                                 */
717
                                action.execute(leaf, new FLayer[] { masterLayer });
718
                            }
719
                        } catch (Exception ex) {
720
                            logger.warn("Problems executing action in the ToC.", ex);
721
                        } finally {
722
                            PluginServices.getMDIManager().restoreCursor();
723
                        }
724
                        return;
725
                    }
726
                }
727

    
728
                ((DefaultTreeModel) tree.getModel()).nodeChanged(node);
729

    
730
                if (row == 0) {
731
                    tree.revalidate();
732
                    tree.repaint();
733
                }
734

    
735
                // FIXME Is it really necessary?
736
                if (PluginServices.getMainFrame() != null) {
737
                    PluginServices.getMainFrame().enableControls();
738
                }
739
            } else {
740
                if (e.getButton() == MouseEvent.BUTTON3) {
741
                    popmenu = new FPopupMenu(mapContext, null);
742
                    tree.add(popmenu);
743
                    popmenu.show(e.getComponent(), e.getX(), e.getY());
744
                }
745

    
746
            }
747
        }
748

    
749
        private void selectInterval(List<FLayer> layers, FLayer lyr) {
750
            // FLayer[] activeLayers = layers.getActives();
751
            int firstActive = Integer.MAX_VALUE;
752
            int lastActive = -1;
753
            int myLayer = -1;
754

    
755
            for (int j = 0; j < layers.size(); j++) {
756
                FLayer layerAux = layers.get(j);
757

    
758
                if (layerAux.isActive()) {
759
                    if (firstActive > j) {
760
                        firstActive = j;
761
                    }
762
                    if (lastActive < j) {
763
                        lastActive = j;
764
                    }
765
                    if (layerAux.equals(lyr)) {
766
                        myLayer = j;
767
                    }
768
                }
769
                if (myLayer < 0 && layerAux.equals(lyr)) {
770
                    myLayer = j;
771
                }
772
            }
773

    
774
            if (firstActive < Integer.MAX_VALUE && myLayer >= 0) {
775
                for (int pasada = 0; pasada < 2; pasada++) {
776
                    if (myLayer < firstActive) {
777
                        for (int j = 0; j < myLayer; j++) {
778
                            FLayer layerAux = layers.get(j);
779
                            if (pasada == 0) {
780
                                if (layerAux instanceof FLayers) {
781
                                    layerAux.setActive(false);
782
                                }
783
                            } else {
784
                                if (!(layerAux instanceof FLayers)) {
785
                                    layerAux.setActive(false);
786
                                }
787
                            }
788
                        }
789
                        for (int j = myLayer; j <= lastActive; j++) {
790
                            FLayer layerAux = layers.get(j);
791
                            if (pasada == 0) {
792
                                if (layerAux instanceof FLayers) {
793
                                    layerAux.setActive(true);
794
                                }
795
                            } else {
796
                                if (!(layerAux instanceof FLayers)) {
797
                                    layerAux.setActive(true);
798
                                }
799
                            }
800
                        }
801
                        for (int j = lastActive + 1; j < layers.size(); j++) {
802
                            FLayer layerAux = layers.get(j);
803
                            if (pasada == 0) {
804
                                if (layerAux instanceof FLayers) {
805
                                    layerAux.setActive(false);
806
                                }
807
                            } else {
808
                                if (!(layerAux instanceof FLayers)) {
809
                                    layerAux.setActive(false);
810
                                }
811
                            }
812
                        }
813
                    } else {
814
                        if (myLayer > lastActive) {
815
                            for (int j = 0; j < firstActive; j++) {
816
                                FLayer layerAux = layers.get(j);
817
                                if (pasada == 0) {
818
                                    if (layerAux instanceof FLayers) {
819
                                        layerAux.setActive(false);
820
                                    }
821
                                } else {
822
                                    if (!(layerAux instanceof FLayers)) {
823
                                        layerAux.setActive(false);
824
                                    }
825
                                }
826
                            }
827
                            for (int j = firstActive; j <= myLayer; j++) {
828
                                FLayer layerAux = layers.get(j);
829
                                if (pasada == 0) {
830
                                    if (layerAux instanceof FLayers) {
831
                                        layerAux.setActive(true);
832
                                    }
833
                                } else {
834
                                    if (!(layerAux instanceof FLayers)) {
835
                                        layerAux.setActive(true);
836
                                    }
837
                                }
838
                            }
839
                            for (int j = myLayer + 1; j < layers.size(); j++) {
840
                                FLayer layerAux = layers.get(j);
841
                                if (pasada == 0) {
842
                                    if (layerAux instanceof FLayers) {
843
                                        layerAux.setActive(false);
844
                                    }
845
                                } else {
846
                                    if (!(layerAux instanceof FLayers)) {
847
                                        layerAux.setActive(false);
848
                                    }
849
                                }
850
                            }
851
                        } else { // Si myLayer est? entre firstActive y lastActive
852
                                 // seleccionamos desde myLayer hasta lastLayer
853
                            for (int j = 0; j < myLayer; j++) {
854
                                FLayer layerAux = layers.get(j);
855
                                if (pasada == 0) {
856
                                    if (layerAux instanceof FLayers) {
857
                                        layerAux.setActive(false);
858
                                    }
859
                                } else {
860
                                    if (!(layerAux instanceof FLayers)) {
861
                                        layerAux.setActive(false);
862
                                    }
863
                                }
864
                            }
865
                            for (int j = myLayer; j <= lastActive; j++) {
866
                                FLayer layerAux = layers.get(j);
867
                                if (pasada == 0) {
868
                                    if (layerAux instanceof FLayers) {
869
                                        layerAux.setActive(true);
870
                                    }
871
                                } else {
872
                                    if (!(layerAux instanceof FLayers)) {
873
                                        layerAux.setActive(true);
874
                                    }
875
                                }
876
                            }
877
                            for (int j = lastActive + 1; j < layers.size(); j++) {
878
                                FLayer layerAux = layers.get(j);
879
                                if (pasada == 0) {
880
                                    if (layerAux instanceof FLayers) {
881
                                        layerAux.setActive(false);
882
                                    }
883
                                } else {
884
                                    if (!(layerAux instanceof FLayers)) {
885
                                        layerAux.setActive(false);
886
                                    }
887
                                }
888
                            }
889
                        }
890
                    }
891
                }
892
            }
893
        }
894

    
895
        private List<FLayer> toPlainList(FLayer layer) {
896
            return toPlainList(layer, new ArrayList<FLayer>());
897
        }
898

    
899
        private List<FLayer> toPlainList(FLayer layer, List<FLayer> result) {
900
            if (layer instanceof FLayers) {
901
                FLayers layerGroup = (FLayers) layer;
902
                for (int i = 0; i < layerGroup.getLayersCount(); i++) {
903
                    toPlainList(layerGroup.getLayer(i), result);
904
                }
905
            }
906
            result.add(layer);
907
            return result;
908
        }
909

    
910
        private int countDeepActiveLayers(List<FLayer> layers) {
911
            int count = 0;
912
            for (FLayer fLayer : layers) {
913
                if (fLayer.isActive()) {
914
                    count++;
915
                }
916
            }
917
            return count;
918
        }
919

    
920
        /**
921
         * Actualiza la visibilidad de la capas.
922
         *
923
         * @param lyr
924
         *            Capa sobre la que se est? clickando.
925
         */
926
        private void updateVisible(FLayer lyr) {
927
            if (lyr.isAvailable()) {
928
                lyr.setVisible(!lyr.visibleRequired());
929
                updateVisibleChild(lyr);
930
                updateVisibleParent(lyr);
931
                if (node != null && !(lyr instanceof FLayers)) {
932
                    if (haveToShowLeyendOfLayer(lyr)) {
933
                        if (node.getChildCount() == 0) {
934
                            addLegend(node, lyr);
935
                        }
936
                    } else {
937
                        this.node.removeAllChildren();
938
                        m_TreeModel.reload(node);
939
                    }
940
                }
941
            }
942
        }
943

    
944
        /**
945
         * Actualiza de forma recursiva la visibilidad de los hijos de la capa
946
         * que se pasa como par?metro.
947
         *
948
         * @param lyr
949
         *            Capa a actualizar.
950
         */
951
        private void updateVisibleChild(FLayer lyr) {
952
            if (lyr instanceof FLayers) { // Es la raiz de una rama o
953
                // cualquier nodo intermedio.
954

    
955
                FLayers layergroup = (FLayers) lyr;
956

    
957
                for (int i = 0; i < layergroup.getLayersCount(); i++) {
958
                    layergroup.getLayer(i).setVisible(lyr.visibleRequired());
959
                    updateVisibleChild(layergroup.getLayer(i));
960
                }
961
            }
962
        }
963

    
964
        /**
965
         * Actualiza de forma recursiva la visibilidad del padre de la capa que
966
         * se pasa como par?metro.
967
         *
968
         * @param lyr
969
         *            Capa a actualizar.
970
         */
971
        private void updateVisibleParent(FLayer lyr) {
972
            FLayers parent = lyr.getParentLayer();
973

    
974
            if (parent != null) {
975
                boolean parentVisible = false;
976

    
977
                for (int i = 0; i < parent.getLayersCount(); i++) {
978
                    if (parent.getLayer(i).visibleRequired()) {
979
                        parentVisible = true;
980
                    }
981
                }
982

    
983
                parent.setVisible(parentVisible);
984
                updateVisibleParent(parent);
985
            }
986
        }
987

    
988
        @Override
989
        public void mouseReleased(MouseEvent arg0) {
990
            super.mouseReleased(arg0);
991
        }
992

    
993
        @Override
994
        public void mouseEntered(MouseEvent arg0) {
995
            super.mouseEntered(arg0);
996
        }
997
    }
998

    
999
    private boolean haveToAddNewLayersInInvisibleMode() {
1000
        ProjectPreferences projectPreferences = ApplicationLocator.getProjectManager().getProjectPreferences();
1001

    
1002
        return projectPreferences.getAddNewLayersInInvisibleMode();
1003
    }
1004

    
1005
    private boolean haveToShowLeyendOfLayer(FLayer layer) {
1006
        ProjectPreferences projectPreferences = ApplicationLocator.getProjectManager().getProjectPreferences();
1007
        return !(!layer.isVisible() && projectPreferences.getHideLegendInToCOfNonVisibleLayers());
1008

    
1009
    }
1010

    
1011
}