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

Last change on this file since 2864 was 2847, checked in by mjulius, 14 years ago

fix messages for gui/dialogs

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