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

Last change on this file since 2181 was 2181, checked in by stoecker, 15 years ago

lots of i18n fixes

  • Property svn:eol-style set to native
File size: 38.2 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.Rectangle;
11import java.awt.event.ActionEvent;
12import java.awt.event.KeyEvent;
13import java.awt.event.MouseAdapter;
14import java.awt.event.MouseEvent;
15import java.beans.PropertyChangeEvent;
16import java.beans.PropertyChangeListener;
17import java.util.ArrayList;
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.JComponent;
28import javax.swing.JLabel;
29import javax.swing.JList;
30import javax.swing.JPanel;
31import javax.swing.JScrollPane;
32import javax.swing.KeyStroke;
33import javax.swing.ListModel;
34import javax.swing.ListSelectionModel;
35import javax.swing.UIManager;
36import javax.swing.event.ListDataEvent;
37import javax.swing.event.ListDataListener;
38import javax.swing.event.ListSelectionEvent;
39import javax.swing.event.ListSelectionListener;
40
41import org.openstreetmap.josm.Main;
42import org.openstreetmap.josm.actions.MergeLayerAction;
43import org.openstreetmap.josm.gui.MapFrame;
44import org.openstreetmap.josm.gui.MapView;
45import org.openstreetmap.josm.gui.SideButton;
46import org.openstreetmap.josm.gui.io.SaveLayersDialog;
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 of the layers, to hide/show layers, to activate layers,
57 * and to delete layers.
58 *
59 */
60public class LayerListDialog extends ToggleDialog {
61 //static private final Logger logger = Logger.getLogger(LayerListDialog.class.getName());
62
63 /** the unique instance of the dialog */
64 static private LayerListDialog instance;
65
66 /**
67 * Creates the instance of the dialog. It's connected to the map frame <code>mapFrame</code>
68 *
69 * @param mapFrame the map frame
70 */
71 static public void createInstance(MapFrame mapFrame) {
72 instance = new LayerListDialog(mapFrame);
73 }
74
75 /**
76 * Replies the instance of the dialog
77 *
78 * @return the instance of the dialog
79 * @throws IllegalStateException thrown, if the dialog is not created yet
80 * @see #createInstance(MapFrame)
81 */
82 static public LayerListDialog getInstance() throws IllegalStateException {
83 if (instance == null)
84 throw new IllegalStateException(tr("Dialog not created yet. Invoke createInstance() first"));
85 return instance;
86 }
87
88 /** the model for the layer list */
89 private LayerListModel model;
90
91 /** the selection model */
92 private DefaultListSelectionModel selectionModel;
93
94 /** the list of layers */
95 private LayerList layerList;
96
97 protected JPanel createButtonPanel() {
98 JPanel buttonPanel = new JPanel(new GridLayout(1, 5));
99
100 // -- move up action
101 MoveUpAction moveUpAction = new MoveUpAction();
102 adaptTo(moveUpAction, model);
103 adaptTo(moveUpAction,selectionModel);
104 buttonPanel.add(new SideButton(moveUpAction));
105
106 // -- move down action
107 MoveDownAction moveDownAction = new MoveDownAction();
108 adaptTo(moveDownAction, model);
109 adaptTo(moveDownAction,selectionModel);
110 buttonPanel.add(new SideButton(moveDownAction));
111
112 // -- activate action
113 ActivateLayerAction activateLayerAction = new ActivateLayerAction();
114 adaptTo(activateLayerAction, selectionModel);
115 adaptToLayerChanges(activateLayerAction);
116 buttonPanel.add(new SideButton(activateLayerAction, "activate"));
117
118 // -- show hide action
119 ShowHideLayerAction showHideLayerAction = new ShowHideLayerAction();
120 adaptTo(showHideLayerAction, selectionModel);
121 buttonPanel.add(new SideButton(showHideLayerAction, "showhide"));
122
123 // -- merge layer action
124 MergeAction mergeLayerAction = new MergeAction();
125 adaptTo(mergeLayerAction, model);
126 adaptTo(mergeLayerAction,selectionModel);
127 buttonPanel.add(new SideButton(mergeLayerAction));
128
129 //-- delete layer action
130 DeleteLayerAction deleteLayerAction = new DeleteLayerAction();
131 layerList.getInputMap(JComponent.WHEN_FOCUSED).put(
132 KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),"deleteLayer"
133 );
134 layerList.getActionMap().put("deleteLayer", deleteLayerAction);
135 adaptTo(deleteLayerAction, selectionModel);
136 buttonPanel.add(new SideButton(deleteLayerAction, "delete"));
137
138 return buttonPanel;
139 }
140
141 /**
142 * Create an layer list and attach it to the given mapView.
143 */
144 protected LayerListDialog(MapFrame mapFrame) {
145 super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
146 Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L, Shortcut.GROUP_LAYER), 100);
147
148 // create the models
149 //
150 selectionModel = new DefaultListSelectionModel();
151 selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
152 model = new LayerListModel(selectionModel);
153 Layer.listeners.add(model);
154
155 // create the list control
156 //
157 layerList = new LayerList(model);
158 layerList.setSelectionModel(selectionModel);
159 layerList.addMouseListener(new LayerListMouseAdapter());
160 layerList.setBackground(UIManager.getColor("Button.background"));
161 layerList.setCellRenderer(new LayerListCellRenderer());
162 add(new JScrollPane(layerList), BorderLayout.CENTER);
163
164 // init the model
165 //
166 final MapView mapView = mapFrame.mapView;
167 model.populate();
168 model.setSelectedLayer(mapView.getActiveLayer());
169 model.addLayerListModelListener(
170 new LayerListModelListener() {
171 public void makeVisible(int index, Layer layer) {
172 layerList.ensureIndexIsVisible(index);
173 }
174
175 public void refresh() {
176 layerList.repaint();
177 }
178 }
179 );
180
181 add(createButtonPanel(), BorderLayout.SOUTH);
182 }
183
184 public LayerListModel getModel() {
185 return model;
186 }
187
188 private interface IEnabledStateUpdating {
189 void updateEnabledState();
190 }
191
192 /**
193 * Wires <code>listener</code> to <code>listSelectionModel</code> in such a way, that
194 * <code>listener</code> receives a {@see IEnabledStateUpdating#updateEnabledState()}
195 * on every {@see ListSelectionEvent}.
196 *
197 * @param listener the listener
198 * @param listSelectionModel the source emitting {@see ListSelectionEvent}s
199 */
200 protected void adaptTo(final IEnabledStateUpdating listener, ListSelectionModel listSelectionModel) {
201 listSelectionModel.addListSelectionListener(
202 new ListSelectionListener() {
203 public void valueChanged(ListSelectionEvent e) {
204 listener.updateEnabledState();
205 }
206 }
207 );
208 }
209
210 /**
211 * Wires <code>listener</code> to <code>listModel</code> in such a way, that
212 * <code>listener</code> receives a {@see IEnabledStateUpdating#updateEnabledState()}
213 * on every {@see ListDataEvent}.
214 *
215 * @param listener the listener
216 * @param listSelectionModel the source emitting {@see ListDataEvent}s
217 */
218 protected void adaptTo(final IEnabledStateUpdating listener, ListModel listModel) {
219 listModel.addListDataListener(
220 new ListDataListener() {
221 public void contentsChanged(ListDataEvent e) {
222 listener.updateEnabledState();
223 }
224
225 public void intervalAdded(ListDataEvent e) {
226 listener.updateEnabledState();
227 }
228
229 public void intervalRemoved(ListDataEvent e) {
230 listener.updateEnabledState();
231 }
232 }
233 );
234 }
235 /**
236 * Wires <code>listener</code> to {@see MapView} in such a way, that
237 * <code>listener</code> receives a {@see IEnabledStateUpdating#updateEnabledState()}
238 * on every {@see LayerChangeListener}-event emitted by {@see MapView}.
239 *
240 * @param listener the listener
241 */
242 protected void adaptToLayerChanges(final IEnabledStateUpdating listener) {
243 Layer.listeners.add(
244 new LayerChangeListener() {
245 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
246 listener.updateEnabledState();
247 }
248 public void layerAdded(Layer newLayer) {
249 listener.updateEnabledState();
250 }
251 public void layerRemoved(Layer oldLayer) {
252 listener.updateEnabledState();
253 }
254 }
255 );
256 }
257
258
259 private enum DeleteDecision {
260 deleteCurrent,
261 dontDeleteCurrent,
262 deleteAll,
263 cancel
264 }
265
266 /**
267 * The action to delete the currently selected layer
268 */
269 public final class DeleteLayerAction extends AbstractAction implements IEnabledStateUpdating {
270 private Layer layer;
271
272 /**
273 * Creates a {@see DeleteLayerAction} for a specific layer.
274 *
275 * @param layer the layer. Must not be null.
276 * @exception IllegalArgumentException thrown, if layer is null
277 */
278 public DeleteLayerAction(Layer layer) {
279 this();
280 if (layer == null)
281 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "layer"));
282 this.layer = layer;
283 putValue(NAME, tr("Delete"));
284 updateEnabledState();
285 }
286
287 /**
288 * Creates a {@see DeleteLayerAction} which will delete the currently
289 * selected layers in the layer dialog.
290 *
291 */
292 public DeleteLayerAction() {
293 putValue(SMALL_ICON,ImageProvider.get("dialogs", "delete"));
294 putValue(SHORT_DESCRIPTION, tr("Delete the selected layer."));
295 putValue("help", "Action/LayerDelete");
296 updateEnabledState();
297 }
298
299 protected boolean enforceUploadOrSaveModifiedData(List<Layer> selectedLayers) {
300 SaveLayersDialog dialog = new SaveLayersDialog(Main.parent);
301 List<OsmDataLayer> layersWithUnmodifiedChanges = new ArrayList<OsmDataLayer>();
302 for (Layer l: selectedLayers) {
303 if (! (l instanceof OsmDataLayer)) {
304 continue;
305 }
306 OsmDataLayer odl = (OsmDataLayer)l;
307 if (odl.requiresSaveToFile() || odl.requiresUploadToServer()) {
308 layersWithUnmodifiedChanges.add(odl);
309 }
310 }
311 dialog.prepareForSavingAndUpdatingLayersBeforeDelete();
312 if (!layersWithUnmodifiedChanges.isEmpty()) {
313 dialog.getModel().populate(layersWithUnmodifiedChanges);
314 dialog.setVisible(true);
315 switch(dialog.getUserAction()) {
316 case CANCEL: return false;
317 case PROCEED: return true;
318 default: return false;
319 }
320 }
321 return true;
322 }
323
324 public void actionPerformed(ActionEvent e) {
325 List<Layer> selectedLayers;
326 if (this.layer == null) {
327 selectedLayers = getModel().getSelectedLayers();
328 } else {
329 selectedLayers = Collections.singletonList(this.layer);
330 }
331 if (selectedLayers.isEmpty())
332 return;
333 if (! enforceUploadOrSaveModifiedData(selectedLayers))
334 return;
335 for(Layer l: selectedLayers) {
336 Main.main.removeLayer(l);
337 }
338 }
339
340 public void updateEnabledState() {
341 if (layer == null) {
342 setEnabled(! getModel().getSelectedLayers().isEmpty());
343 } else {
344 setEnabled(true);
345 }
346 }
347 }
348
349 public final class ShowHideLayerAction extends AbstractAction implements IEnabledStateUpdating {
350 private Layer layer;
351
352 /**
353 * Creates a {@see ShowHideLayerAction} which toggle the visibility of
354 * a specific layer.
355 *
356 * @param layer the layer. Must not be null.
357 * @exception IllegalArgumentException thrown, if layer is null
358 */
359 public ShowHideLayerAction(Layer layer) throws IllegalArgumentException {
360 this();
361 if (layer == null)
362 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "layer"));
363 this.layer = layer;
364 putValue(NAME, tr("Show/Hide"));
365 updateEnabledState();
366 }
367
368 /**
369 * Creates a {@see ShowHideLayerAction} which will toggle the visibility of
370 * the currently selected layers
371 *
372 */
373 public ShowHideLayerAction() {
374 putValue(SMALL_ICON, ImageProvider.get("dialogs", "showhide"));
375 putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the selected layer."));
376 putValue("help", "Action/LayerShowHide");
377 updateEnabledState();
378 }
379
380 public void actionPerformed(ActionEvent e) {
381 if (layer != null) {
382 layer.toggleVisible();
383 } else {
384 for(Layer layer: model.getSelectedLayers()) {
385 layer.toggleVisible();
386 }
387 }
388 }
389
390 public void updateEnabledState() {
391 if (layer == null) {
392 setEnabled(! getModel().getSelectedLayers().isEmpty());
393 } else {
394 setEnabled(true);
395 }
396 }
397 }
398
399 /**
400 * The action to activate the currently selected layer
401 */
402
403 public final class ActivateLayerAction extends AbstractAction implements IEnabledStateUpdating{
404 private Layer layer;
405
406 public ActivateLayerAction(Layer layer) throws IllegalArgumentException {
407 this();
408 if (layer == null)
409 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "layer"));
410 this.layer = layer;
411 putValue(NAME, tr("Activate"));
412 updateEnabledState();
413 }
414
415 public ActivateLayerAction() {
416 putValue(SMALL_ICON, ImageProvider.get("dialogs", "activate"));
417 putValue(SHORT_DESCRIPTION, tr("Activate the selected layer"));
418 putValue("help", "Action/ActivateLayer");
419 updateEnabledState();
420 }
421
422 public void actionPerformed(ActionEvent e) {
423 Layer toActivate;
424 if (layer != null) {
425 toActivate = layer;
426 } else {
427 toActivate = model.getSelectedLayers().get(0);
428 }
429 // model is going to be updated via LayerChangeListener
430 // and PropertyChangeEvents
431 Main.map.mapView.setActiveLayer(toActivate);
432 toActivate.setVisible(true);
433 }
434
435 protected boolean isActiveLayer(Layer layer) {
436 if (Main.map == null) return false;
437 if (Main.map.mapView == null) return false;
438 return Main.map.mapView.getActiveLayer() == layer;
439 }
440
441 public void updateEnabledState() {
442 if (layer == null) {
443 if (getModel().getSelectedLayers().size() != 1) {
444 setEnabled(false);
445 return;
446 }
447 Layer selectedLayer = getModel().getSelectedLayers().get(0);
448 setEnabled(!isActiveLayer(selectedLayer));
449 } else {
450 setEnabled(!isActiveLayer(layer));
451 }
452 }
453 }
454
455 /**
456 * The action to merge the currently selected layer into another layer.
457 */
458 public final class MergeAction extends AbstractAction implements IEnabledStateUpdating {
459 private Layer layer;
460
461 public MergeAction(Layer layer) throws IllegalArgumentException {
462 this();
463 if (layer == null)
464 throw new IllegalArgumentException(tr("Parameter ''{0}'' must not be null.", "layer"));
465 this.layer = layer;
466 putValue(NAME, tr("Merge"));
467 updateEnabledState();
468 }
469
470 public MergeAction() {
471 putValue(SMALL_ICON, ImageProvider.get("dialogs", "mergedown"));
472 putValue(SHORT_DESCRIPTION, tr("Merge this layer into another layer"));
473 putValue("help", "Action/MergeLayer");
474 updateEnabledState();
475 }
476
477 public void actionPerformed(ActionEvent e) {
478 if (layer != null) {
479 new MergeLayerAction().merge(layer);
480 } else {
481 Layer selectedLayer = getModel().getSelectedLayers().get(0);
482 new MergeLayerAction().merge(selectedLayer);
483 }
484 }
485
486 protected boolean isActiveLayer(Layer layer) {
487 if (Main.map == null) return false;
488 if (Main.map.mapView == null) return false;
489 return Main.map.mapView.getActiveLayer() == layer;
490 }
491
492 public void updateEnabledState() {
493 if (layer == null) {
494 if (getModel().getSelectedLayers().size() != 1) {
495 setEnabled(false);
496 return;
497 }
498 Layer selectedLayer = getModel().getSelectedLayers().get(0);
499 List<Layer> targets = getModel().getPossibleMergeTargets(selectedLayer);
500 setEnabled(!targets.isEmpty());
501 } else {
502 List<Layer> targets = getModel().getPossibleMergeTargets(layer);
503 setEnabled(!targets.isEmpty());
504 }
505 }
506 }
507
508 /**
509 * the list cell renderer used to render layer list entries
510 *
511 */
512 class LayerListCellRenderer extends DefaultListCellRenderer {
513
514 protected boolean isActiveLayer(Layer layer) {
515 if (Main.map == null) return false;
516 if (Main.map.mapView == null) return false;
517 return Main.map.mapView.getActiveLayer() == layer;
518 }
519
520 @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
521 Layer layer = (Layer)value;
522 JLabel label = (JLabel)super.getListCellRendererComponent(list,
523 layer.getName(), index, isSelected, cellHasFocus);
524 Icon icon = layer.getIcon();
525 if (isActiveLayer(layer)) {
526 icon = ImageProvider.overlay(icon, "overlay/active", OverlayPosition.SOUTHWEST);
527 }
528 if (!layer.isVisible()) {
529 icon = ImageProvider.overlay(icon, "overlay/invisiblenew", OverlayPosition.SOUTHEAST);
530 }
531 label.setIcon(icon);
532 label.setToolTipText(layer.getToolTipText());
533 return label;
534 }
535 }
536
537 class LayerListMouseAdapter extends MouseAdapter {
538
539 private void openPopup(MouseEvent e) {
540 Point p = e.getPoint();
541 int index = layerList.locationToIndex(p);
542 if (index < 0) return;
543 if (!layerList.getCellBounds(index, index).contains(e.getPoint()))
544 return;
545 Layer layer = model.getLayer(index);
546 LayerListPopup menu = new LayerListPopup(layerList, layer);
547 menu.show(LayerListDialog.this, p.x, p.y-3);
548 }
549 @Override public void mousePressed(MouseEvent e) {
550 if (e.isPopupTrigger()) {
551 openPopup(e);
552 }
553 }
554 @Override public void mouseReleased(MouseEvent e) {
555 if (e.isPopupTrigger()) {
556 openPopup(e);
557 }
558 }
559 @Override public void mouseClicked(MouseEvent e) {
560 if (e.getClickCount() == 2) {
561 int index = layerList.locationToIndex(e.getPoint());
562 if (!layerList.getCellBounds(index, index).contains(e.getPoint()))
563 return;
564 Layer layer = model.getLayer(index);
565 String current = Main.pref.get("marker.show "+layer.getName(),"show");
566 Main.pref.put("marker.show "+layer.getName(), current.equalsIgnoreCase("show") ? "hide" : "show");
567 layer.toggleVisible();
568 }
569 }
570 }
571
572 /**
573 * The action to move up the currently selected entries in the list.
574 */
575 class MoveUpAction extends AbstractAction implements IEnabledStateUpdating{
576 public MoveUpAction() {
577 putValue(SMALL_ICON, ImageProvider.get("dialogs", "up"));
578 putValue(SHORT_DESCRIPTION, tr("Move the selected layer one row up."));
579 updateEnabledState();
580 }
581
582 public void updateEnabledState() {
583 setEnabled(model.canMoveUp());
584 }
585
586 public void actionPerformed(ActionEvent e) {
587 model.moveUp();
588 }
589 }
590
591 /**
592 * The action to move down the currently selected entries in the list.
593 */
594 class MoveDownAction extends AbstractAction implements IEnabledStateUpdating {
595 public MoveDownAction() {
596 putValue(SMALL_ICON, ImageProvider.get("dialogs", "down"));
597 putValue(SHORT_DESCRIPTION, tr("Move the selected layer one row down."));
598 updateEnabledState();
599 }
600
601 public void updateEnabledState() {
602 setEnabled(model.canMoveDown());
603 }
604
605 public void actionPerformed(ActionEvent e) {
606 model.moveDown();
607 }
608 }
609
610 /**
611 * Observer interface to be implemented by views using {@see LayerListModel}
612 *
613 */
614 public interface LayerListModelListener {
615 public void makeVisible(int index, Layer layer);
616 public void refresh();
617 }
618
619 /**
620 * The layer list model. The model manages a list of layers and provides methods for
621 * moving layers up and down, for toggling their visibility, and for activating a layer.
622 *
623 * The model is a {@see ListModel} and it provides a {@see ListSelectionModel}. It expectes
624 * to be configured with a {@see DefaultListSelectionModel}. The selection model is used
625 * to update the selection state of views depending on messages sent to the model.
626 *
627 * The model manages a list of {@see LayerListModelListener} which are mainly notified if
628 * the model requires views to make a specific list entry visible.
629 *
630 * It also listens to {@see PropertyChangeEvent}s of every {@see Layer} it manages, in particular to
631 * the properties {@see Layer#VISIBLE_PROP} and {@see Layer#NAME_PROP}.
632 */
633 public class LayerListModel extends DefaultListModel implements LayerChangeListener, PropertyChangeListener{
634
635 /** manages list selection state*/
636 private DefaultListSelectionModel selectionModel;
637 private CopyOnWriteArrayList<LayerListModelListener> listeners;
638
639 /**
640 * constructor
641 *
642 * @param selectionModel the list selection model
643 */
644 private LayerListModel(DefaultListSelectionModel selectionModel) {
645 this.selectionModel = selectionModel;
646 listeners = new CopyOnWriteArrayList<LayerListModelListener>();
647 }
648
649 /**
650 * Adds a listener to this model
651 *
652 * @param listener the listener
653 */
654 public void addLayerListModelListener(LayerListModelListener listener) {
655 synchronized(listeners) {
656 if (listener != null && !listeners.contains(listener)) {
657 listeners.add(listener);
658 }
659 }
660 }
661
662 /**
663 * removes a listener from this model
664 * @param listener the listener
665 *
666 */
667 public void removeLayerListModelListener(LayerListModelListener listener) {
668 synchronized(listeners) {
669 if (listener != null && listeners.contains(listener)) {
670 listeners.remove(listener);
671 }
672 }
673 }
674
675 /**
676 * Fires a make visible event to listeners
677 *
678 * @param index the index of the row to make visible
679 * @param layer the layer at this index
680 * @see LayerListModelListener#makeVisible(int, Layer)
681 */
682 protected void fireMakeVisible(int index, Layer layer) {
683 for (LayerListModelListener listener : listeners) {
684 listener.makeVisible(index, layer);
685 }
686 }
687
688 /**
689 * Fires a refresh event to listeners of this model
690 *
691 * @see LayerListModelListener#refresh()
692 */
693 protected void fireRefresh() {
694 for (LayerListModelListener listener : listeners) {
695 listener.refresh();
696 }
697 }
698
699 /**
700 * Populates the model with the current layers managed by
701 * {@see MapView}.
702 *
703 */
704 public void populate() {
705 for (Layer layer: getLayers()) {
706 // make sure the model is registered exactly once
707 //
708 layer.removePropertyChangeListener(this);
709 layer.addPropertyChangeListener(this);
710 }
711 fireContentsChanged(this, 0, getSize());
712 }
713
714 /**
715 * Marks <code>layer</code> as selected layer. Ignored, if
716 * layer is null.
717 *
718 * @param layer the layer.
719 */
720 public void setSelectedLayer(Layer layer) {
721 if (layer == null)
722 return;
723 int idx = getLayers().indexOf(layer);
724 if (idx >= 0) {
725 selectionModel.setSelectionInterval(idx, idx);
726 }
727 fireContentsChanged(this, 0, getSize());
728 ensureSelectedIsVisible();
729 }
730
731 /**
732 * Replies the list of currently selected layers. Never null, but may
733 * be empty.
734 *
735 * @return the list of currently selected layers. Never null, but may
736 * be empty.
737 */
738 public List<Layer> getSelectedLayers() {
739 ArrayList<Layer> selected = new ArrayList<Layer>();
740 for (int i=0; i<getLayers().size(); i++) {
741 if (selectionModel.isSelectedIndex(i)) {
742 selected.add(getLayers().get(i));
743 }
744 }
745 return selected;
746 }
747
748 /**
749 * Replies a the list of indices of the selected rows. Never null,
750 * but may be empty.
751 *
752 * @return the list of indices of the selected rows. Never null,
753 * but may be empty.
754 */
755 public List<Integer> getSelectedRows() {
756 ArrayList<Integer> selected = new ArrayList<Integer>();
757 for (int i=0; i<getLayers().size();i++) {
758 if (selectionModel.isSelectedIndex(i)) {
759 selected.add(i);
760 }
761 }
762 return selected;
763 }
764
765 /**
766 * Invoked if a layer managed by {@see MapView} is removed
767 *
768 * @param layer the layer which is removed
769 */
770 protected void onRemoveLayer(Layer layer) {
771 if (layer == null)
772 return;
773 layer.removePropertyChangeListener(this);
774 int size = getSize();
775 List<Integer> rows = getSelectedRows();
776 if (rows.isEmpty() && size > 0) {
777 selectionModel.setSelectionInterval(size-1, size-1);
778 }
779 fireRefresh();
780 ensureSelectedIsVisible();
781 }
782
783 /**
784 * Invoked when a layer managed by {@see MapView} is added
785 *
786 * @param layer the layer
787 */
788 protected void onAddLayer(Layer layer) {
789 if (layer == null) return;
790 layer.addPropertyChangeListener(this);
791 fireContentsChanged(this, 0, getSize());
792 int idx = getLayers().indexOf(layer);
793 selectionModel.setSelectionInterval(idx, idx);
794 ensureSelectedIsVisible();
795 }
796
797 /**
798 * Replies the first layer. Null if no layers are present
799 *
800 * @return the first layer. Null if no layers are present
801 */
802 public Layer getFirstLayer() {
803 if (getSize() == 0) return null;
804 return getLayers().get(0);
805 }
806
807 /**
808 * Replies the layer at position <code>index</code>
809 *
810 * @param index the index
811 * @return the layer at position <code>index</code>. Null,
812 * if index is out of range.
813 */
814 public Layer getLayer(int index) {
815 if (index < 0 || index >= getSize())
816 return null;
817 return getLayers().get(index);
818 }
819
820 /**
821 * Replies true if the the currently selected layers can move up
822 * by one position
823 *
824 * @return true if the the currently selected layers can move up
825 * by one position
826 */
827 public boolean canMoveUp() {
828 List<Integer> sel = getSelectedRows();
829 return !sel.isEmpty() && sel.get(0) > 0;
830 }
831
832 /**
833 * Move up the currently selected layers by one position
834 *
835 */
836 public void moveUp() {
837 if (!canMoveUp()) return;
838 List<Integer> sel = getSelectedRows();
839 for (int row: sel) {
840 Layer l1 = getLayers().get(row);
841 Layer l2 = getLayers().get(row-1);
842 Main.map.mapView.moveLayer(l2,row);
843 Main.map.mapView.moveLayer(l1, row-1);
844 }
845 fireContentsChanged(this, 0, getSize());
846 selectionModel.clearSelection();
847 for(int row: sel) {
848 selectionModel.addSelectionInterval(row-1, row-1);
849 }
850 ensureSelectedIsVisible();
851 }
852
853 /**
854 * Replies true if the currently selected layers can move down
855 * by one position
856 *
857 * @return true if the currently selected layers can move down
858 * by one position
859 */
860 public boolean canMoveDown() {
861 List<Integer> sel = getSelectedRows();
862 return !sel.isEmpty() && sel.get(sel.size()-1) < getLayers().size()-1;
863 }
864
865 /**
866 * Move down the currently selected layers by one position
867 *
868 */
869 public void moveDown() {
870 if (!canMoveDown()) return;
871 List<Integer> sel = getSelectedRows();
872 Collections.reverse(sel);
873 for (int row: sel) {
874 Layer l1 = getLayers().get(row);
875 Layer l2 = getLayers().get(row+1);
876 Main.map.mapView.moveLayer(l1, row+1);
877 Main.map.mapView.moveLayer(l2, row);
878 }
879 fireContentsChanged(this, 0, getSize());
880 selectionModel.clearSelection();
881 for(int row: sel) {
882 selectionModel.addSelectionInterval(row+1, row+1);
883 }
884 ensureSelectedIsVisible();
885 }
886
887 /**
888 * Make sure the first of the selected layers is visible in the
889 * views of this model.
890 *
891 */
892 protected void ensureSelectedIsVisible() {
893 int index = selectionModel.getMinSelectionIndex();
894 if (index <0 )return;
895 if (index >= getLayers().size()) return;
896 Layer layer = getLayers().get(index);
897 fireMakeVisible(index, layer);
898 }
899
900 /**
901 * Replies a list of layers which are possible merge targets
902 * for <code>source</code>
903 *
904 * @param source the source layer
905 * @return a list of layers which are possible merge targets
906 * for <code>source</code>. Never null, but can be empty.
907 */
908 public List<Layer> getPossibleMergeTargets(Layer source) {
909 ArrayList<Layer> targets = new ArrayList<Layer>();
910 if (source == null)
911 return targets;
912 for(Layer target: getLayers()) {
913 if (source == target) {
914 continue;
915 }
916 if (target.isMergable(source)) {
917 targets.add(target);
918 }
919 }
920 return targets;
921 }
922
923 /**
924 * Replies the list of layers currently managed by {@see MapView}.
925 * Never null, but can be empty.
926 *
927 * @return the list of layers currently managed by {@see MapView}.
928 * Never null, but can be empty.
929 */
930 protected List<Layer> getLayers() {
931 if (Main.map == null || Main.map.mapView == null)
932 return Collections.<Layer>emptyList();
933 return Main.map.mapView.getAllLayersAsList();
934 }
935
936 /**
937 * Ensures that at least one layer is selected in the layer dialog
938 *
939 */
940 protected void ensureActiveSelected() {
941 if (getLayers().size() == 0) return;
942 if (getActiveLayer() != null) {
943 // there's an active layer - select it and make it
944 // visible
945 int idx = getLayers().indexOf(getActiveLayer());
946 selectionModel.setSelectionInterval(idx, idx);
947 ensureSelectedIsVisible();
948 } else {
949 // no active layer - select the first one and make
950 // it visible
951 selectionModel.setSelectionInterval(0, 0);
952 ensureSelectedIsVisible();
953 }
954 }
955
956 /**
957 * Replies the active layer. null, if no active layer is available
958 *
959 * @return the active layer. null, if no active layer is available
960 */
961 protected Layer getActiveLayer() {
962 if (Main.map == null || Main.map.mapView == null) return null;
963 return Main.map.mapView.getActiveLayer();
964 }
965
966 /* ------------------------------------------------------------------------------ */
967 /* Interface ListModel */
968 /* ------------------------------------------------------------------------------ */
969 @Override
970 public Object getElementAt(int index) {
971 return getLayers().get(index);
972 }
973
974 @Override
975 public int getSize() {
976 List<Layer> layers = getLayers();
977 if (layers == null) return 0;
978 return layers.size();
979 }
980
981 /* ------------------------------------------------------------------------------ */
982 /* Interface LayerChangeListener */
983 /* ------------------------------------------------------------------------------ */
984 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
985 if (oldLayer != null) {
986 int idx = getLayers().indexOf(oldLayer);
987 if (idx >= 0) {
988 fireContentsChanged(this, idx,idx);
989 }
990 }
991
992 if (newLayer != null) {
993 int idx = getLayers().indexOf(newLayer);
994 if (idx >= 0) {
995 fireContentsChanged(this, idx,idx);
996 }
997 }
998 ensureActiveSelected();
999 }
1000
1001 public void layerAdded(Layer newLayer) {
1002 onAddLayer(newLayer);
1003 }
1004
1005 public void layerRemoved(final Layer oldLayer) {
1006 onRemoveLayer(oldLayer);
1007 }
1008
1009 /* ------------------------------------------------------------------------------ */
1010 /* Interface PropertyChangeListener */
1011 /* ------------------------------------------------------------------------------ */
1012 public void propertyChange(PropertyChangeEvent evt) {
1013 if (evt.getSource() instanceof Layer) {
1014 Layer layer = (Layer)evt.getSource();
1015 final int idx = getLayers().indexOf(layer);
1016 if (idx < 0) return;
1017 fireRefresh();
1018 }
1019 }
1020 }
1021
1022 class LayerList extends JList {
1023 public LayerList(ListModel dataModel) {
1024 super(dataModel);
1025 }
1026
1027 @Override
1028 protected void processMouseEvent(MouseEvent e) {
1029 // if the layer list is embedded in a detached dialog, the last row is
1030 // selected if a user clicks in the empty space *below* the last row.
1031 // This mouse event filter prevents this.
1032 //
1033 int idx = locationToIndex(e.getPoint());
1034 // sometimes bounds can be null, see #3539
1035 Rectangle bounds = getCellBounds(idx,idx);
1036 if (bounds != null && bounds.contains(e.getPoint())) {
1037 super.processMouseEvent(e);
1038 }
1039 }
1040 }
1041
1042 /**
1043 * Creates a {@see ShowHideLayerAction} for <code>layer</code> in the
1044 * context of this {@see LayerListDialog}.
1045 *
1046 * @param layer the layer
1047 * @return the action
1048 */
1049 public ShowHideLayerAction createShowHideLayerAction(Layer layer) {
1050 return new ShowHideLayerAction(layer);
1051 }
1052
1053 /**
1054 * Creates a {@see DeleteLayerAction} for <code>layer</code> in the
1055 * context of this {@see LayerListDialog}.
1056 *
1057 * @param layer the layer
1058 * @return the action
1059 */
1060 public DeleteLayerAction createDeleteLayerAction(Layer layer) {
1061 return new DeleteLayerAction(layer);
1062 }
1063
1064 /**
1065 * Creates a {@see ActivateLayerAction} for <code>layer</code> in the
1066 * context of this {@see LayerListDialog}.
1067 *
1068 * @param layer the layer
1069 * @return the action
1070 */
1071 public ActivateLayerAction createActivateLayerAction(Layer layer) {
1072 return new ActivateLayerAction(layer);
1073 }
1074
1075 /**
1076 * Creates a {@see MergeLayerAction} for <code>layer</code> in the
1077 * context of this {@see LayerListDialog}.
1078 *
1079 * @param layer the layer
1080 * @return the action
1081 */
1082 public MergeAction createMergeLayerAction(Layer layer) {
1083 return new MergeAction(layer);
1084 }
1085}
Note: See TracBrowser for help on using the repository browser.