source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java@ 1890

Last change on this file since 1890 was 1890, checked in by Gubaer, 15 years ago

update: rewrite of layer dialog
new: allows multiple selection of layers in the dialog
new: move up, move down, toggle visibility, and delete on multiple layers
new: merge from an arbitrary layer into another layer, not only from the first into the second
new: new action for merging of the currently selected primitives on an arbitrary layer
new: make "active" layer explicit (special icon); activating a layer automatically moves it in the first position
refactoring: public fields 'name' and 'visible' on Layer are @deprecated. Use the setter/getters instead, Layer now emits PropertyChangeEvents if name or visibility are changed.

  • Property svn:eol-style set to native
File size: 30.5 KB
Line 
1// License: GPL. Copyright 2007 by Immanuel Scholz and others
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.BorderLayout;
7import java.awt.Component;
8import java.awt.GridLayout;
9import java.awt.Point;
10import java.awt.event.ActionEvent;
11import java.awt.event.KeyEvent;
12import java.awt.event.MouseAdapter;
13import java.awt.event.MouseEvent;
14import java.beans.PropertyChangeEvent;
15import java.beans.PropertyChangeListener;
16import java.util.ArrayList;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.List;
20import java.util.concurrent.CopyOnWriteArrayList;
21
22import javax.swing.AbstractAction;
23import javax.swing.DefaultListCellRenderer;
24import javax.swing.DefaultListModel;
25import javax.swing.DefaultListSelectionModel;
26import javax.swing.Icon;
27import javax.swing.JLabel;
28import javax.swing.JList;
29import javax.swing.JOptionPane;
30import javax.swing.JPanel;
31import javax.swing.JScrollPane;
32import javax.swing.ListModel;
33import javax.swing.ListSelectionModel;
34import javax.swing.UIManager;
35import javax.swing.event.ListDataEvent;
36import javax.swing.event.ListDataListener;
37import javax.swing.event.ListSelectionEvent;
38import javax.swing.event.ListSelectionListener;
39
40import org.openstreetmap.josm.Main;
41import org.openstreetmap.josm.actions.MergeLayerAction;
42import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil;
43import org.openstreetmap.josm.gui.ExtendedDialog;
44import org.openstreetmap.josm.gui.MapFrame;
45import org.openstreetmap.josm.gui.MapView;
46import org.openstreetmap.josm.gui.SideButton;
47import org.openstreetmap.josm.gui.layer.Layer;
48import org.openstreetmap.josm.gui.layer.OsmDataLayer;
49import org.openstreetmap.josm.gui.layer.Layer.LayerChangeListener;
50import org.openstreetmap.josm.tools.ImageProvider;
51import org.openstreetmap.josm.tools.Shortcut;
52import org.openstreetmap.josm.tools.ImageProvider.OverlayPosition;
53
54/**
55 * This is a toggle dialog which displays the list of layers. Actions allow to
56 * change the ordering the layer, to hide/show layers, to activate layers
57 * and to delete layers.
58 *
59 */
60public class LayerListDialog extends ToggleDialog {
61
62 /** the unique instance of the dialog */
63 static private LayerListDialog instance;
64
65 /**
66 * Creates the instance of the dialog. It's connected to the map frame <code>mapFrame</code>
67 *
68 * @param mapFrame the map frame
69 */
70 static public void createInstance(MapFrame mapFrame) {
71 instance = new LayerListDialog(mapFrame);
72 }
73
74 /**
75 * Replies the instance of the dialog
76 *
77 * @return the instance of the dialog
78 * @throws IllegalStateException thrown, if the dialog is not created yet
79 * @see #createInstance(MapFrame)
80 */
81 static public LayerListDialog getInstance() throws IllegalStateException {
82 if (instance == null)
83 throw new IllegalStateException(tr("Dialog not created yet. Invoke createInstance() first"));
84 return instance;
85 }
86
87 /** the model for the layer list */
88 private LayerListModel model;
89
90 /** the selection model */
91 private DefaultListSelectionModel selectionModel;
92
93 /** the list of layers */
94 private LayerList layerList;
95
96 protected JPanel createButtonPanel() {
97 JPanel buttonPanel = new JPanel(new GridLayout(1, 5));
98
99 // -- move up action
100 MoveUpAction moveUpAction = new MoveUpAction();
101 adaptTo(moveUpAction, model);
102 adaptTo(moveUpAction,selectionModel);
103 buttonPanel.add(new SideButton(moveUpAction));
104
105 // -- move down action
106 MoveDownAction moveDownAction = new MoveDownAction();
107 adaptTo(moveDownAction, model);
108 adaptTo(moveDownAction,selectionModel);
109 buttonPanel.add(new SideButton(moveDownAction));
110
111 // -- activate action
112 ActivateLayerAction activateLayerAction = new ActivateLayerAction();
113 adaptTo(activateLayerAction, selectionModel);
114 buttonPanel.add(new SideButton(activateLayerAction, "activate"));
115
116 // -- show hide action
117 ShowHideLayerAction showHideLayerAction = new ShowHideLayerAction();
118 adaptTo(showHideLayerAction, selectionModel);
119 buttonPanel.add(new SideButton(showHideLayerAction, "showhide"));
120
121 // -- merge layer action
122 MergeAction mergeLayerAction = new MergeAction();
123 adaptTo(mergeLayerAction, model);
124 adaptTo(mergeLayerAction,selectionModel);
125 buttonPanel.add(new SideButton(mergeLayerAction));
126
127 //-- delete layer action
128 DeleteLayerAction deleteLayerAction = new DeleteLayerAction();
129 adaptTo(deleteLayerAction, selectionModel);
130 buttonPanel.add(new SideButton(deleteLayerAction, "delete"));
131
132 return buttonPanel;
133 }
134
135 /**
136 * Create an layerlist and attach it to the given mapView.
137 */
138 protected LayerListDialog(MapFrame mapFrame) {
139 super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
140 Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100);
141
142 // create the models
143 //
144 selectionModel = new DefaultListSelectionModel();
145 selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
146 model = new LayerListModel(selectionModel);
147 Layer.listeners.add(model);
148
149 // create the list control
150 //
151 layerList = new LayerList(model);
152 layerList.setSelectionModel(selectionModel);
153 layerList.addMouseListener(new LayerListMouseAdapter());
154 layerList.setBackground(UIManager.getColor("Button.background"));
155 layerList.setCellRenderer(new LayerListCellRenderer());
156 add(new JScrollPane(layerList), BorderLayout.CENTER);
157
158 // init the model
159 //
160 final MapView mapView = mapFrame.mapView;
161 model.populate(mapView.getAllLayers());
162 model.setSelectedLayer(mapView.getActiveLayer());
163 model.addLayerListModelListener(
164 new LayerListModelListener() {
165 public void makeVisible(int index, Layer layer) {
166 layerList.ensureIndexIsVisible(index);
167 }
168 }
169 );
170
171 add(createButtonPanel(), BorderLayout.SOUTH);
172 }
173
174 public LayerListModel getModel() {
175 return model;
176 }
177
178 private interface IEnabledStateUpdating {
179 void updateEnabledState();
180 }
181
182 protected void adaptTo(final IEnabledStateUpdating listener, ListSelectionModel listSelectionModel) {
183 listSelectionModel.addListSelectionListener(
184 new ListSelectionListener() {
185 public void valueChanged(ListSelectionEvent e) {
186 listener.updateEnabledState();
187 }
188 }
189 );
190 }
191
192 protected void adaptTo(final IEnabledStateUpdating listener, ListModel listModel) {
193 listModel.addListDataListener(
194 new ListDataListener() {
195 public void contentsChanged(ListDataEvent e) {
196 listener.updateEnabledState();
197 }
198
199 public void intervalAdded(ListDataEvent e) {
200 listener.updateEnabledState();
201 }
202
203 public void intervalRemoved(ListDataEvent e) {
204 listener.updateEnabledState();
205 }
206 }
207 );
208 }
209
210 /**
211 * The action to delete the currently selected layer
212 */
213 public final class DeleteLayerAction extends AbstractAction implements IEnabledStateUpdating {
214
215 private Layer layer;
216
217 /**
218 * Creates a {@see DeleteLayerAction} for a specific layer.
219 *
220 * @param layer the layer. Must not be null.
221 * @exception IllegalArgumentException thrown, if layer is null
222 */
223 public DeleteLayerAction(Layer layer) {
224 this();
225 if (layer == null)
226 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
227 this.layer = layer;
228 putValue(NAME, tr("Delete"));
229 updateEnabledState();
230 }
231
232 /**
233 * Creates a {@see DeleteLayerAction} which will delete the currently
234 * selected layers in the layer dialog.
235 *
236 */
237 public DeleteLayerAction() {
238 putValue(SMALL_ICON,ImageProvider.get("dialogs", "delete"));
239 putValue(SHORT_DESCRIPTION, tr("Delete the selected layer."));
240 putValue("help", "Action/LayerDelete");
241 updateEnabledState();
242 }
243
244 protected boolean confirmSkipSaving(OsmDataLayer layer) {
245 int result = new ExtendedDialog(Main.parent,
246 tr("Unsaved Changes"),
247 tr("There are unsaved changes in the layer''{0}''. Delete the layer anwyay?",layer.getName()),
248 new String[] {tr("Delete Layer"), tr("Cancel")},
249 new String[] {"dialogs/delete.png", "cancel.png"}).getValue();
250
251 return result != 1;
252 }
253
254 protected boolean confirmDeleteLayer(Layer layer) {
255 return ConditionalOptionPaneUtil.showConfirmationDialog(
256 "delete_layer",
257 Main.parent,
258 tr("Do you really want to delete the whole layer ''{0}''?", layer.getName()),
259 tr("Confirmation"),
260 JOptionPane.YES_NO_OPTION,
261 JOptionPane.QUESTION_MESSAGE,
262 JOptionPane.YES_OPTION);
263 }
264
265 public void deleteLayer(Layer layer) {
266 if (layer == null)
267 return;
268 if (layer instanceof OsmDataLayer) {
269 OsmDataLayer dataLayer = (OsmDataLayer)layer;
270 if (dataLayer.isModified() && ! confirmSkipSaving(dataLayer))
271 return;
272 else if (!confirmDeleteLayer(dataLayer))
273 return;
274
275 } else {
276 if (!confirmDeleteLayer(layer))
277 return;
278 }
279 // model and view are going to be updated via LayerChangeListener
280 //
281 Main.main.removeLayer(layer);
282 }
283
284 public void actionPerformed(ActionEvent e) {
285 if (this.layer == null) {
286 List<Layer> selectedLayers = getModel().getSelectedLayers();
287 for (Layer layer: selectedLayers) {
288 deleteLayer(layer);
289 }
290 } else {
291 deleteLayer(this.layer);
292 }
293 }
294
295 public void updateEnabledState() {
296 if (layer == null) {
297 setEnabled(! getModel().getSelectedLayers().isEmpty());
298 } else {
299 setEnabled(true);
300 }
301 }
302 }
303
304 public final class ShowHideLayerAction extends AbstractAction implements IEnabledStateUpdating {
305 private Layer layer;
306
307 /**
308 * Creates a {@see ShowHideLayerAction} which toggle the visibility of
309 * a specific layer.
310 *
311 * @param layer the layer. Must not be null.
312 * @exception IllegalArgumentException thrown, if layer is null
313 */
314 public ShowHideLayerAction(Layer layer) throws IllegalArgumentException {
315 this();
316 if (layer == null)
317 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
318 this.layer = layer;
319 putValue(NAME, tr("Show/Hide"));
320 updateEnabledState();
321 }
322
323 /**
324 * Creates a {@see ShowHideLayerAction} which will toggle the visibility of
325 * the currently selected layers
326 *
327 */
328 public ShowHideLayerAction() {
329 putValue(SMALL_ICON, ImageProvider.get("dialogs", "showhide"));
330 putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the selected layer."));
331 putValue("help", "Action/LayerShowHide");
332 updateEnabledState();
333 }
334
335 public void actionPerformed(ActionEvent e) {
336 if (layer != null) {
337 layer.toggleVisible();
338 } else {
339 for(Layer layer: model.getSelectedLayers()) {
340 layer.toggleVisible();
341 }
342 }
343 }
344
345 public void updateEnabledState() {
346 if (layer == null) {
347 setEnabled(! getModel().getSelectedLayers().isEmpty());
348 } else {
349 setEnabled(true);
350 }
351 }
352 }
353
354 /**
355 * The action to activate the currently selected layer
356 */
357
358 public final class ActivateLayerAction extends AbstractAction implements IEnabledStateUpdating{
359 private Layer layer;
360
361 public ActivateLayerAction(Layer layer) throws IllegalArgumentException {
362 this();
363 if (layer == null)
364 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
365 this.layer = layer;
366 putValue(NAME, tr("Activate"));
367 updateEnabledState();
368 }
369
370 public ActivateLayerAction() {
371 putValue(SMALL_ICON, ImageProvider.get("dialogs", "activate"));
372 putValue(SHORT_DESCRIPTION, tr("Activate the selected layer"));
373 putValue("help", "Action/ActivateLayer");
374 updateEnabledState();
375 }
376
377 public void actionPerformed(ActionEvent e) {
378 Layer toActivate;
379 if (layer != null) {
380 toActivate = layer;
381 } else {
382 toActivate = model.getSelectedLayers().get(0);
383 }
384 getModel().activateLayer(toActivate);
385 }
386
387 protected boolean isActiveLayer(Layer layer) {
388 if (Main.map == null) return false;
389 if (Main.map.mapView == null) return false;
390 return Main.map.mapView.getActiveLayer() == layer;
391 }
392
393 public void updateEnabledState() {
394 if (layer == null) {
395 if (getModel().getSelectedLayers().size() != 1) {
396 setEnabled(false);
397 return;
398 }
399 Layer selectedLayer = getModel().getSelectedLayers().get(0);
400 setEnabled(!isActiveLayer(selectedLayer));
401 } else {
402 setEnabled(!isActiveLayer(layer));
403 }
404 }
405 }
406
407 /**
408 * The action to merge the currently selected layer into another layer.
409 */
410 public final class MergeAction extends AbstractAction implements IEnabledStateUpdating {
411 private Layer layer;
412
413 public MergeAction(Layer layer) throws IllegalArgumentException {
414 this();
415 if (layer == null)
416 throw new IllegalArgumentException(tr("parameter ''{0}'' must not be null", "layer"));
417 this.layer = layer;
418 putValue(NAME, tr("Merge"));
419 updateEnabledState();
420 }
421
422 public MergeAction() {
423 putValue(SMALL_ICON, ImageProvider.get("dialogs", "mergedown"));
424 putValue(SHORT_DESCRIPTION, tr("Merge this layer into another layer"));
425 putValue("help", "Action/MergeLayer");
426 updateEnabledState();
427 }
428
429 public void actionPerformed(ActionEvent e) {
430 if (layer != null) {
431 new MergeLayerAction().merge(layer);
432 } else {
433 Layer selectedLayer = getModel().getSelectedLayers().get(0);
434 new MergeLayerAction().merge(selectedLayer);
435 }
436 }
437
438 protected boolean isActiveLayer(Layer layer) {
439 if (Main.map == null) return false;
440 if (Main.map.mapView == null) return false;
441 return Main.map.mapView.getActiveLayer() == layer;
442 }
443
444 public void updateEnabledState() {
445 if (layer == null) {
446 if (getModel().getSelectedLayers().size() != 1) {
447 setEnabled(false);
448 return;
449 }
450 Layer selectedLayer = getModel().getSelectedLayers().get(0);
451 List<Layer> targets = getModel().getPossibleMergeTargets(selectedLayer);
452 setEnabled(!targets.isEmpty());
453 } else {
454 List<Layer> targets = getModel().getPossibleMergeTargets(layer);
455 setEnabled(!targets.isEmpty());
456 }
457 }
458 }
459
460 /**
461 * the list cell renderer used to render layer list entries
462 *
463 */
464 class LayerListCellRenderer extends DefaultListCellRenderer {
465
466 protected boolean isActiveLayer(Layer layer) {
467 if (Main.map == null) return false;
468 if (Main.map.mapView == null) return false;
469 return Main.map.mapView.getActiveLayer() == layer;
470 }
471
472 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
473 Layer layer = (Layer)value;
474 JLabel label = (JLabel)super.getListCellRendererComponent(list,
475 layer.getName(), index, isSelected, cellHasFocus);
476 Icon icon = layer.getIcon();
477 if (isActiveLayer(layer)) {
478 icon = ImageProvider.overlay(icon, "overlay/active", OverlayPosition.SOUTHWEST);
479 }
480 if (!layer.isVisible()) {
481 icon = ImageProvider.overlay(icon, "overlay/invisiblenew", OverlayPosition.SOUTHEAST);
482 }
483 label.setIcon(icon);
484 label.setToolTipText(layer.getToolTipText());
485 return label;
486 }
487 }
488
489 class LayerListMouseAdapter extends MouseAdapter {
490
491 private void openPopup(MouseEvent e) {
492 Point p = e.getPoint();
493 int index = layerList.locationToIndex(p);
494 if (index < 0) return;
495 if (!layerList.getCellBounds(index, index).contains(e.getPoint()))
496 return;
497 Layer layer = model.getLayer(index);
498 LayerListPopup menu = new LayerListPopup(layerList, layer);
499 menu.show(LayerListDialog.this, p.x, p.y-3);
500 }
501 @Override public void mousePressed(MouseEvent e) {
502 if (e.isPopupTrigger()) {
503 openPopup(e);
504 }
505 }
506 @Override public void mouseReleased(MouseEvent e) {
507 if (e.isPopupTrigger()) {
508 openPopup(e);
509 }
510 }
511 @Override public void mouseClicked(MouseEvent e) {
512 if (e.getClickCount() == 2) {
513 int index = layerList.locationToIndex(e.getPoint());
514 if (!layerList.getCellBounds(index, index).contains(e.getPoint()))
515 return;
516 Layer layer = model.getLayer(index);
517 String current = Main.pref.get("marker.show "+layer.getName(),"show");
518 Main.pref.put("marker.show "+layer.getName(), current.equalsIgnoreCase("show") ? "hide" : "show");
519 layer.toggleVisible();
520 }
521 }
522 }
523
524 /**
525 * The action to move up the currently selected entries in the list.
526 */
527 class MoveUpAction extends AbstractAction implements IEnabledStateUpdating{
528 public MoveUpAction() {
529 putValue(SMALL_ICON, ImageProvider.get("dialogs", "up"));
530 putValue(SHORT_DESCRIPTION, tr("Move the selected layer one row up."));
531 updateEnabledState();
532 }
533
534 public void updateEnabledState() {
535 setEnabled(model.canMoveUp());
536 }
537
538 public void actionPerformed(ActionEvent e) {
539 model.moveUp();
540 }
541 }
542
543 /**
544 * The action to move down the currently selected entries in the list.
545 */
546 class MoveDownAction extends AbstractAction implements IEnabledStateUpdating {
547 public MoveDownAction() {
548 putValue(SMALL_ICON, ImageProvider.get("dialogs", "down"));
549 putValue(SHORT_DESCRIPTION, tr("Move the selected layer one row down."));
550 updateEnabledState();
551 }
552
553 public void updateEnabledState() {
554 setEnabled(model.canMoveDown());
555 }
556
557 public void actionPerformed(ActionEvent e) {
558 model.moveDown();
559 }
560 }
561
562 /**
563 * Observer interface to be implemented by views using {@see LayerListModel}
564 *
565 */
566 public interface LayerListModelListener {
567 public void makeVisible(int index, Layer layer);
568 }
569
570 /**
571 * The layer list model. The model manages a list of layers and provides methods for
572 * moving layers up and down, for toggling their visibiliy, for activating a layer.
573 *
574 * The model is a {@see ListModel} and it provides a {@see ListSelectionModel}. It expectes
575 * to be configured with a {@see DefaultListSelectionModel}. The selection model is used
576 * to update the selection state of views depending on messages sent to the model.
577 *
578 * The model manages a list of {@see LayerListModelListener} which are mainly notified if
579 * the model requires views to make a specific list entry visible.
580 */
581 public class LayerListModel extends DefaultListModel implements LayerChangeListener, PropertyChangeListener{
582
583 private ArrayList<Layer> layers;
584 private DefaultListSelectionModel selectionModel;
585 private CopyOnWriteArrayList<LayerListModelListener> listeners;
586
587 private LayerListModel(DefaultListSelectionModel selectionModel) {
588 layers = new ArrayList<Layer>();
589 this.selectionModel = selectionModel;
590 listeners = new CopyOnWriteArrayList<LayerListModelListener>();
591 }
592
593 public void addLayerListModelListener(LayerListModelListener listener) {
594 synchronized(listeners) {
595 if (listener != null && !listeners.contains(listener)) {
596 listeners.add(listener);
597 }
598 }
599 }
600
601 public void removeLayerListModelListener(LayerListModelListener listener) {
602 synchronized(listeners) {
603 if (listener != null && listeners.contains(listener)) {
604 listeners.remove(listener);
605 }
606 }
607 }
608
609 protected void fireMakeVisible(int index, Layer layer) {
610 for (LayerListModelListener listener : listeners) {
611 listener.makeVisible(index, layer);
612 }
613 }
614
615 public void populate(Collection<Layer> layers) {
616 if (layers == null)
617 return;
618 this.layers.clear();
619 this.layers.addAll(layers);
620 for (Layer layer: this.layers) {
621 layer.addPropertyChangeListener(this);
622 }
623 fireContentsChanged(this, 0, getSize());
624 }
625
626 public void setSelectedLayer(Layer layer) {
627 int idx = layers.indexOf(layer);
628 if (idx >= 0) {
629 selectionModel.setSelectionInterval(idx, idx);
630 }
631 fireContentsChanged(this, 0, getSize());
632 ensureSelectedIsVisible();
633 }
634
635 public List<Layer> getSelectedLayers() {
636 ArrayList<Layer> selected = new ArrayList<Layer>();
637 for (int i=0; i<layers.size(); i++) {
638 if (selectionModel.isSelectedIndex(i)) {
639 selected.add(layers.get(i));
640 }
641 }
642 return selected;
643 }
644
645 public List<Integer> getSelectedRows() {
646 ArrayList<Integer> selected = new ArrayList<Integer>();
647 for (int i=0; i<layers.size();i++) {
648 if (selectionModel.isSelectedIndex(i)) {
649 selected.add(i);
650 }
651 }
652 return selected;
653 }
654
655 public void removeLayer(Layer layer) {
656 if (layer == null)
657 return;
658 List<Integer> selectedRows = getSelectedRows();
659 int deletedRow = layers.indexOf(layer);
660 if (deletedRow < 0)
661 // layer not found in the list of layers
662 return;
663 layers.remove(layer);
664 fireContentsChanged(this, 0,getSize());
665 for (int row: selectedRows) {
666 if (row < deletedRow) {
667 selectionModel.addSelectionInterval(row, row);
668 } else if (row == deletedRow){
669 // do nothing
670 } else {
671 selectionModel.addSelectionInterval(row-1, row-1);
672 }
673 }
674 ensureSelectedIsVisible();
675 }
676
677 public void addLayer(Layer layer) {
678 if (layer == null) return;
679 if (layers.contains(layer)) return;
680 layers.add(layer);
681 layer.addPropertyChangeListener(this);
682 fireContentsChanged(this, 0, getSize());
683 }
684
685 public Layer getFirstLayer() {
686 if (getSize() == 0) return null;
687 return layers.get(0);
688 }
689
690 public Layer getLayer(int index) {
691 if (index < 0 || index >= getSize())
692 return null;
693 return layers.get(index);
694 }
695
696 public boolean canMoveUp() {
697 List<Integer> sel = getSelectedRows();
698 return !sel.isEmpty() && sel.get(0) > 0;
699 }
700
701 public void moveUp() {
702 if (!canMoveUp()) return;
703 List<Integer> sel = getSelectedRows();
704 for (int row: sel) {
705 Layer l1 = layers.get(row);
706 Layer l2 = layers.get(row-1);
707 layers.set(row, l2);
708 layers.set(row-1,l1);
709 Main.map.mapView.moveLayer(l1, row-1);
710 }
711 fireContentsChanged(this, 0, getSize());
712 selectionModel.clearSelection();
713 for(int row: sel) {
714 selectionModel.addSelectionInterval(row-1, row-1);
715 }
716 ensureSelectedIsVisible();
717 }
718
719 public boolean canMoveDown() {
720 List<Integer> sel = getSelectedRows();
721 return !sel.isEmpty() && sel.get(sel.size()-1) < layers.size()-1;
722 }
723
724 public void moveDown() {
725 if (!canMoveDown()) return;
726 List<Integer> sel = getSelectedRows();
727 Collections.reverse(sel);
728 for (int row: sel) {
729 Layer l1 = layers.get(row);
730 Layer l2 = layers.get(row+1);
731 layers.set(row, l2);
732 layers.set(row+1,l1);
733 Main.map.mapView.moveLayer(l1, row+1);
734 }
735 fireContentsChanged(this, 0, getSize());
736 selectionModel.clearSelection();
737 for(int row: sel) {
738 selectionModel.addSelectionInterval(row+1, row+1);
739 }
740 ensureSelectedIsVisible();
741 }
742
743 protected void ensureSelectedIsVisible() {
744 int index = selectionModel.getMinSelectionIndex();
745 if (index <0 )return;
746 if (index >= layers.size()) return;
747 Layer layer = layers.get(index);
748 fireMakeVisible(index, layer);
749 }
750
751 public List<Layer> getPossibleMergeTargets(Layer layer) {
752 ArrayList<Layer> targets = new ArrayList<Layer>();
753 if (layer == null)
754 return targets;
755 for(Layer target: layers) {
756 if (layer == target) {
757 continue;
758 }
759 if (target.isMergable(layer)) {
760 targets.add(target);
761 }
762 }
763 return targets;
764 }
765
766 public void activateLayer(Layer layer) {
767 layers.remove(layer);
768 layers.add(0,layer);
769 Main.map.mapView.setActiveLayer(layer);
770 layer.setVisible(true);
771 selectionModel.setSelectionInterval(0,0);
772 ensureSelectedIsVisible();
773 }
774
775 /* ------------------------------------------------------------------------------ */
776 /* Interface ListModel */
777 /* ------------------------------------------------------------------------------ */
778 @Override
779 public Object getElementAt(int index) {
780 return layers.get(index);
781 }
782
783 @Override
784 public int getSize() {
785 return layers.size();
786 }
787
788 /* ------------------------------------------------------------------------------ */
789 /* Interface LayerChangeListener */
790 /* ------------------------------------------------------------------------------ */
791 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
792 if (oldLayer != null) {
793 int idx = layers.indexOf(oldLayer);
794 if (idx >= 0) {
795 fireContentsChanged(this, idx,idx);
796 }
797 }
798
799 if (newLayer != null) {
800 int idx = layers.indexOf(newLayer);
801 if (idx >= 0) {
802 fireContentsChanged(this, idx,idx);
803 }
804 }
805 }
806
807 public void layerAdded(Layer newLayer) {
808 addLayer(newLayer);
809 }
810
811 public void layerRemoved(Layer oldLayer) {
812 removeLayer(oldLayer);
813 }
814
815 /* ------------------------------------------------------------------------------ */
816 /* Interface PropertyChangeListener */
817 /* ------------------------------------------------------------------------------ */
818 public void propertyChange(PropertyChangeEvent evt) {
819 if (evt.getSource() instanceof Layer) {
820 Layer layer = (Layer)evt.getSource();
821 int idx = layers.indexOf(layer);
822 if (idx < 0) return;
823 fireContentsChanged(this, idx, idx);
824 }
825 }
826 }
827
828 class LayerList extends JList {
829 public LayerList(ListModel dataModel) {
830 super(dataModel);
831 }
832
833 @Override
834 protected void processMouseEvent(MouseEvent e) {
835 // if the layer list is embedded in a detached dialog, the last row is
836 // is selected if a user clicks in the empty space *below* the last row.
837 // This mouse event filter prevents this.
838 //
839 int idx = locationToIndex(e.getPoint());
840 if (getCellBounds(idx, idx).contains(e.getPoint())) {
841 super.processMouseEvent(e);
842 }
843 }
844 }
845
846 public ShowHideLayerAction createShowHideLayerAction(Layer layer) {
847 return new ShowHideLayerAction(layer);
848 }
849
850 public DeleteLayerAction createDeleteLayerAction(Layer layer) {
851 return new DeleteLayerAction(layer);
852 }
853
854 public ActivateLayerAction createActivateLayerAction(Layer layer) {
855 return new ActivateLayerAction(layer);
856 }
857
858 public MergeAction createMergeLayerAction(Layer layer) {
859 return new MergeAction(layer);
860 }
861}
Note: See TracBrowser for help on using the repository browser.