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

Last change on this file since 6084 was 6084, checked in by bastiK, 11 years ago

see #8902 - add missing @Override annotations (patch by shinigami)

  • Property svn:eol-style set to native
File size: 58.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5
6import java.awt.Color;
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.Font;
10import java.awt.Point;
11import java.awt.Rectangle;
12import java.awt.event.ActionEvent;
13import java.awt.event.InputEvent;
14import java.awt.event.KeyEvent;
15import java.awt.event.MouseEvent;
16import java.beans.PropertyChangeEvent;
17import java.beans.PropertyChangeListener;
18import java.lang.ref.WeakReference;
19import java.util.ArrayList;
20import java.util.Arrays;
21import java.util.Collections;
22import java.util.List;
23import java.util.concurrent.CopyOnWriteArrayList;
24
25import javax.swing.AbstractAction;
26import javax.swing.Action;
27import javax.swing.DefaultCellEditor;
28import javax.swing.DefaultListSelectionModel;
29import javax.swing.ImageIcon;
30import javax.swing.JCheckBox;
31import javax.swing.JComponent;
32import javax.swing.JLabel;
33import javax.swing.JMenuItem;
34import javax.swing.JPopupMenu;
35import javax.swing.JSlider;
36import javax.swing.JTable;
37import javax.swing.JViewport;
38import javax.swing.KeyStroke;
39import javax.swing.ListSelectionModel;
40import javax.swing.UIManager;
41import javax.swing.event.ChangeEvent;
42import javax.swing.event.ChangeListener;
43import javax.swing.event.ListDataEvent;
44import javax.swing.event.ListSelectionEvent;
45import javax.swing.event.ListSelectionListener;
46import javax.swing.event.TableModelEvent;
47import javax.swing.event.TableModelListener;
48import javax.swing.table.AbstractTableModel;
49import javax.swing.table.DefaultTableCellRenderer;
50import javax.swing.table.TableCellRenderer;
51import javax.swing.table.TableModel;
52
53import org.openstreetmap.josm.Main;
54import org.openstreetmap.josm.actions.MergeLayerAction;
55import org.openstreetmap.josm.gui.MapFrame;
56import org.openstreetmap.josm.gui.MapView;
57import org.openstreetmap.josm.gui.SideButton;
58import org.openstreetmap.josm.gui.help.HelpUtil;
59import org.openstreetmap.josm.gui.layer.JumpToMarkerActions;
60import org.openstreetmap.josm.gui.layer.Layer;
61import org.openstreetmap.josm.gui.layer.Layer.LayerAction;
62import org.openstreetmap.josm.gui.layer.OsmDataLayer;
63import org.openstreetmap.josm.gui.util.GuiHelper;
64import org.openstreetmap.josm.gui.widgets.JosmTextField;
65import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
66import org.openstreetmap.josm.tools.CheckParameterUtil;
67import org.openstreetmap.josm.tools.ImageProvider;
68import org.openstreetmap.josm.tools.InputMapUtils;
69import org.openstreetmap.josm.tools.MultikeyActionsHandler;
70import org.openstreetmap.josm.tools.MultikeyShortcutAction;
71import org.openstreetmap.josm.tools.MultikeyShortcutAction.MultikeyInfo;
72import org.openstreetmap.josm.tools.Shortcut;
73
74/**
75 * This is a toggle dialog which displays the list of layers. Actions allow to
76 * change the ordering of the layers, to hide/show layers, to activate layers,
77 * and to delete layers.
78 *
79 */
80public class LayerListDialog extends ToggleDialog {
81 /** the unique instance of the dialog */
82 static private LayerListDialog instance;
83
84 /**
85 * Creates the instance of the dialog. It's connected to the map frame <code>mapFrame</code>
86 *
87 * @param mapFrame the map frame
88 */
89 static public void createInstance(MapFrame mapFrame) {
90 if (instance != null)
91 throw new IllegalStateException("Dialog was already created");
92 instance = new LayerListDialog(mapFrame);
93 }
94
95 /**
96 * Replies the instance of the dialog
97 *
98 * @return the instance of the dialog
99 * @throws IllegalStateException thrown, if the dialog is not created yet
100 * @see #createInstance(MapFrame)
101 */
102 static public LayerListDialog getInstance() throws IllegalStateException {
103 if (instance == null)
104 throw new IllegalStateException("Dialog not created yet. Invoke createInstance() first");
105 return instance;
106 }
107
108 /** the model for the layer list */
109 private LayerListModel model;
110
111 /** the selection model */
112 private DefaultListSelectionModel selectionModel;
113
114 /** the list of layers (technically its a JTable, but appears like a list) */
115 private LayerList layerList;
116
117 private SideButton opacityButton;
118
119 ActivateLayerAction activateLayerAction;
120 ShowHideLayerAction showHideLayerAction;
121
122 //TODO This duplicates ShowHide actions functionality
123 /** stores which layer index to toggle and executes the ShowHide action if the layer is present */
124 private final class ToggleLayerIndexVisibility extends AbstractAction {
125 int layerIndex = -1;
126 public ToggleLayerIndexVisibility(int layerIndex) {
127 this.layerIndex = layerIndex;
128 }
129 @Override
130 public void actionPerformed(ActionEvent e) {
131 final Layer l = model.getLayer(model.getRowCount() - layerIndex - 1);
132 if(l != null) {
133 l.toggleVisible();
134 }
135 }
136 }
137
138 private final Shortcut[] visibilityToggleShortcuts = new Shortcut[10];
139 private final ToggleLayerIndexVisibility[] visibilityToggleActions = new ToggleLayerIndexVisibility[10];
140 /**
141 * registers (shortcut to toggle right hand side toggle dialogs)+(number keys) shortcuts
142 * to toggle the visibility of the first ten layers.
143 */
144 private final void createVisibilityToggleShortcuts() {
145 final int[] k = { KeyEvent.VK_1, KeyEvent.VK_2, KeyEvent.VK_3, KeyEvent.VK_4,
146 KeyEvent.VK_5, KeyEvent.VK_6, KeyEvent.VK_7, KeyEvent.VK_8,
147 KeyEvent.VK_9, KeyEvent.VK_0 };
148
149 for(int i=0; i < 10; i++) {
150 visibilityToggleShortcuts[i] = Shortcut.registerShortcut("subwindow:layers:toggleLayer" + (i+1),
151 tr("Toggle visibility of layer: {0}", (i+1)), k[i], Shortcut.ALT);
152 visibilityToggleActions[i] = new ToggleLayerIndexVisibility(i);
153 Main.registerActionShortcut(visibilityToggleActions[i], visibilityToggleShortcuts[i]);
154 }
155 }
156
157 /**
158 * Create an layer list and attach it to the given mapView.
159 */
160 protected LayerListDialog(MapFrame mapFrame) {
161 super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
162 Shortcut.registerShortcut("subwindow:layers", tr("Toggle: {0}", tr("Layers")), KeyEvent.VK_L,
163 Shortcut.ALT_SHIFT), 100, true);
164
165 // create the models
166 //
167 selectionModel = new DefaultListSelectionModel();
168 selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
169 model = new LayerListModel(selectionModel);
170
171 // create the list control
172 //
173 layerList = new LayerList(model);
174 layerList.setSelectionModel(selectionModel);
175 layerList.addMouseListener(new PopupMenuHandler());
176 layerList.setBackground(UIManager.getColor("Button.background"));
177 layerList.putClientProperty("terminateEditOnFocusLost", true);
178 layerList.putClientProperty("JTable.autoStartsEdit", false);
179 layerList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
180 layerList.setTableHeader(null);
181 layerList.setShowGrid(false);
182 layerList.setIntercellSpacing(new Dimension(0, 0));
183 layerList.getColumnModel().getColumn(0).setCellRenderer(new ActiveLayerCellRenderer());
184 layerList.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(new ActiveLayerCheckBox()));
185 layerList.getColumnModel().getColumn(0).setMaxWidth(12);
186 layerList.getColumnModel().getColumn(0).setPreferredWidth(12);
187 layerList.getColumnModel().getColumn(0).setResizable(false);
188 layerList.getColumnModel().getColumn(1).setCellRenderer(new LayerVisibleCellRenderer());
189 layerList.getColumnModel().getColumn(1).setCellEditor(new LayerVisibleCellEditor(new LayerVisibleCheckBox()));
190 layerList.getColumnModel().getColumn(1).setMaxWidth(16);
191 layerList.getColumnModel().getColumn(1).setPreferredWidth(16);
192 layerList.getColumnModel().getColumn(1).setResizable(false);
193 layerList.getColumnModel().getColumn(2).setCellRenderer(new LayerNameCellRenderer());
194 layerList.getColumnModel().getColumn(2).setCellEditor(new LayerNameCellEditor(new JosmTextField()));
195 for (KeyStroke ks : new KeyStroke[] {
196 KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_MASK),
197 KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_MASK),
198 KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.SHIFT_MASK),
199 KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.SHIFT_MASK),
200 KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.SHIFT_MASK),
201 KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.SHIFT_MASK),
202 KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
203 KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
204 })
205 {
206 layerList.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ks, new Object());
207 }
208
209 // init the model
210 //
211 final MapView mapView = mapFrame.mapView;
212 model.populate();
213 model.setSelectedLayer(mapView.getActiveLayer());
214 model.addLayerListModelListener(
215 new LayerListModelListener() {
216 @Override
217 public void makeVisible(int row, Layer layer) {
218 layerList.scrollToVisible(row, 0);
219 layerList.repaint();
220 }
221 @Override
222 public void refresh() {
223 layerList.repaint();
224 }
225 }
226 );
227
228 // -- move up action
229 MoveUpAction moveUpAction = new MoveUpAction();
230 adaptTo(moveUpAction, model);
231 adaptTo(moveUpAction,selectionModel);
232
233 // -- move down action
234 MoveDownAction moveDownAction = new MoveDownAction();
235 adaptTo(moveDownAction, model);
236 adaptTo(moveDownAction,selectionModel);
237
238 // -- activate action
239 activateLayerAction = new ActivateLayerAction();
240 activateLayerAction.updateEnabledState();
241 MultikeyActionsHandler.getInstance().addAction(activateLayerAction);
242 adaptTo(activateLayerAction, selectionModel);
243
244 JumpToMarkerActions.initialize();
245
246 // -- show hide action
247 showHideLayerAction = new ShowHideLayerAction();
248 MultikeyActionsHandler.getInstance().addAction(showHideLayerAction);
249 adaptTo(showHideLayerAction, selectionModel);
250
251 //-- layer opacity action
252 LayerOpacityAction layerOpacityAction = new LayerOpacityAction();
253 adaptTo(layerOpacityAction, selectionModel);
254 opacityButton = new SideButton(layerOpacityAction, false);
255
256 // -- merge layer action
257 MergeAction mergeLayerAction = new MergeAction();
258 adaptTo(mergeLayerAction, model);
259 adaptTo(mergeLayerAction,selectionModel);
260
261 // -- duplicate layer action
262 DuplicateAction duplicateLayerAction = new DuplicateAction();
263 adaptTo(duplicateLayerAction, model);
264 adaptTo(duplicateLayerAction, selectionModel);
265
266 //-- delete layer action
267 DeleteLayerAction deleteLayerAction = new DeleteLayerAction();
268 layerList.getActionMap().put("deleteLayer", deleteLayerAction);
269 adaptTo(deleteLayerAction, selectionModel);
270 getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
271 KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0),"delete"
272 );
273 getActionMap().put("delete", deleteLayerAction);
274
275 // Activate layer on Enter key press
276 InputMapUtils.addEnterAction(layerList, new AbstractAction() {
277 @Override
278 public void actionPerformed(ActionEvent e) {
279 activateLayerAction.actionPerformed(null);
280 layerList.requestFocus();
281 }
282 });
283
284 // Show/Activate layer on Enter key press
285 InputMapUtils.addSpacebarAction(layerList, showHideLayerAction);
286
287 createLayout(layerList, true, Arrays.asList(new SideButton[] {
288 new SideButton(moveUpAction, false),
289 new SideButton(moveDownAction, false),
290 new SideButton(activateLayerAction, false),
291 new SideButton(showHideLayerAction, false),
292 opacityButton,
293 new SideButton(mergeLayerAction, false),
294 new SideButton(duplicateLayerAction, false),
295 new SideButton(deleteLayerAction, false)
296 }));
297
298 createVisibilityToggleShortcuts();
299 }
300
301 @Override
302 public void showNotify() {
303 MapView.addLayerChangeListener(activateLayerAction);
304 MapView.addLayerChangeListener(model);
305 model.populate();
306 }
307
308 @Override
309 public void hideNotify() {
310 MapView.removeLayerChangeListener(model);
311 MapView.removeLayerChangeListener(activateLayerAction);
312 }
313
314 public LayerListModel getModel() {
315 return model;
316 }
317
318 protected interface IEnabledStateUpdating {
319 void updateEnabledState();
320 }
321
322 /**
323 * Wires <code>listener</code> to <code>listSelectionModel</code> in such a way, that
324 * <code>listener</code> receives a {@link IEnabledStateUpdating#updateEnabledState()}
325 * on every {@link ListSelectionEvent}.
326 *
327 * @param listener the listener
328 * @param listSelectionModel the source emitting {@link ListSelectionEvent}s
329 */
330 protected void adaptTo(final IEnabledStateUpdating listener, ListSelectionModel listSelectionModel) {
331 listSelectionModel.addListSelectionListener(
332 new ListSelectionListener() {
333 @Override
334 public void valueChanged(ListSelectionEvent e) {
335 listener.updateEnabledState();
336 }
337 }
338 );
339 }
340
341 /**
342 * Wires <code>listener</code> to <code>listModel</code> in such a way, that
343 * <code>listener</code> receives a {@link IEnabledStateUpdating#updateEnabledState()}
344 * on every {@link ListDataEvent}.
345 *
346 * @param listener the listener
347 * @param listModel the source emitting {@link ListDataEvent}s
348 */
349 protected void adaptTo(final IEnabledStateUpdating listener, LayerListModel listModel) {
350 listModel.addTableModelListener(
351 new TableModelListener() {
352
353 @Override
354 public void tableChanged(TableModelEvent e) {
355 listener.updateEnabledState();
356 }
357 }
358 );
359 }
360
361 @Override
362 public void destroy() {
363 for(int i=0; i < 10; i++) {
364 Main.unregisterActionShortcut(visibilityToggleActions[i], visibilityToggleShortcuts[i]);
365 }
366 MultikeyActionsHandler.getInstance().removeAction(activateLayerAction);
367 MultikeyActionsHandler.getInstance().removeAction(showHideLayerAction);
368 JumpToMarkerActions.unregisterActions();
369 super.destroy();
370 instance = null;
371 }
372
373 /**
374 * The action to delete the currently selected layer
375 */
376 public final class DeleteLayerAction extends AbstractAction implements IEnabledStateUpdating, LayerAction {
377 /**
378 * Creates a {@link DeleteLayerAction} which will delete the currently
379 * selected layers in the layer dialog.
380 *
381 */
382 public DeleteLayerAction() {
383 putValue(SMALL_ICON,ImageProvider.get("dialogs", "delete"));
384 putValue(SHORT_DESCRIPTION, tr("Delete the selected layers."));
385 putValue(NAME, tr("Delete"));
386 putValue("help", HelpUtil.ht("/Dialog/LayerList#DeleteLayer"));
387 updateEnabledState();
388 }
389
390 @Override
391 public void actionPerformed(ActionEvent e) {
392 List<Layer> selectedLayers = getModel().getSelectedLayers();
393 if (selectedLayers.isEmpty())
394 return;
395 if (!Main.saveUnsavedModifications(selectedLayers, false))
396 return;
397 for (Layer l: selectedLayers) {
398 Main.main.removeLayer(l);
399 }
400 }
401
402 @Override
403 public void updateEnabledState() {
404 setEnabled(! getModel().getSelectedLayers().isEmpty());
405 }
406
407 @Override
408 public Component createMenuComponent() {
409 return new JMenuItem(this);
410 }
411
412 @Override
413 public boolean supportLayers(List<Layer> layers) {
414 return true;
415 }
416
417 @Override
418 public boolean equals(Object obj) {
419 return obj instanceof DeleteLayerAction;
420 }
421
422 @Override
423 public int hashCode() {
424 return getClass().hashCode();
425 }
426 }
427
428 public final class ShowHideLayerAction extends AbstractAction implements IEnabledStateUpdating, LayerAction, MultikeyShortcutAction {
429
430 private WeakReference<Layer> lastLayer;
431 private Shortcut multikeyShortcut;
432
433 /**
434 * Creates a {@link ShowHideLayerAction} which will toggle the visibility of
435 * the currently selected layers
436 *
437 */
438 public ShowHideLayerAction(boolean init) {
439 putValue(NAME, tr("Show/hide"));
440 putValue(SMALL_ICON, ImageProvider.get("dialogs", "showhide"));
441 putValue(SHORT_DESCRIPTION, tr("Toggle visible state of the selected layer."));
442 putValue("help", HelpUtil.ht("/Dialog/LayerList#ShowHideLayer"));
443 multikeyShortcut = Shortcut.registerShortcut("core_multikey:showHideLayer", tr("Multikey: {0}",
444 tr("Show/hide layer")), KeyEvent.VK_S, Shortcut.SHIFT);
445 multikeyShortcut.setAccelerator(this);
446 if (init) {
447 updateEnabledState();
448 }
449 }
450
451 public ShowHideLayerAction() {
452 this(true);
453 }
454
455 @Override
456 public Shortcut getMultikeyShortcut() {
457 return multikeyShortcut;
458 }
459
460 @Override
461 public void actionPerformed(ActionEvent e) {
462 for(Layer l : model.getSelectedLayers()) {
463 l.toggleVisible();
464 }
465 }
466
467 @Override
468 public void executeMultikeyAction(int index, boolean repeat) {
469 Layer l = LayerListDialog.getLayerForIndex(index);
470 if (l != null) {
471 l.toggleVisible();
472 lastLayer = new WeakReference<Layer>(l);
473 } else if (repeat && lastLayer != null) {
474 l = lastLayer.get();
475 if (LayerListDialog.isLayerValid(l)) {
476 l.toggleVisible();
477 }
478 }
479 }
480
481 @Override
482 public void updateEnabledState() {
483 setEnabled(!model.getSelectedLayers().isEmpty());
484 }
485
486 @Override
487 public Component createMenuComponent() {
488 return new JMenuItem(this);
489 }
490
491 @Override
492 public boolean supportLayers(List<Layer> layers) {
493 return true;
494 }
495
496 @Override
497 public boolean equals(Object obj) {
498 return obj instanceof ShowHideLayerAction;
499 }
500
501 @Override
502 public int hashCode() {
503 return getClass().hashCode();
504 }
505
506 @Override
507 public List<MultikeyInfo> getMultikeyCombinations() {
508 return LayerListDialog.getLayerInfoByClass(Layer.class);
509 }
510
511 @Override
512 public MultikeyInfo getLastMultikeyAction() {
513 if (lastLayer != null)
514 return LayerListDialog.getLayerInfo(lastLayer.get());
515 return null;
516 }
517 }
518
519 public final class LayerOpacityAction extends AbstractAction implements IEnabledStateUpdating, LayerAction {
520 private Layer layer;
521 private JPopupMenu popup;
522 private JSlider slider = new JSlider(JSlider.VERTICAL);
523
524 /**
525 * Creates a {@link LayerOpacityAction} which allows to chenge the
526 * opacity of one or more layers.
527 *
528 * @param layer the layer. Must not be null.
529 * @exception IllegalArgumentException thrown, if layer is null
530 */
531 public LayerOpacityAction(Layer layer) throws IllegalArgumentException {
532 this();
533 putValue(NAME, tr("Opacity"));
534 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
535 this.layer = layer;
536 updateEnabledState();
537 }
538
539 /**
540 * Creates a {@link ShowHideLayerAction} which will toggle the visibility of
541 * the currently selected layers
542 *
543 */
544 public LayerOpacityAction() {
545 putValue(NAME, tr("Opacity"));
546 putValue(SHORT_DESCRIPTION, tr("Adjust opacity of the layer."));
547 putValue(SMALL_ICON, ImageProvider.get("dialogs/layerlist", "transparency"));
548 updateEnabledState();
549
550 popup = new JPopupMenu();
551 slider.addChangeListener(new ChangeListener() {
552 @Override
553 public void stateChanged(ChangeEvent e) {
554 setOpacity((double)slider.getValue()/100);
555 }
556 });
557 popup.add(slider);
558 }
559
560 private void setOpacity(double value) {
561 if (!isEnabled()) return;
562 if (layer != null) {
563 layer.setOpacity(value);
564 } else {
565 for(Layer layer: model.getSelectedLayers()) {
566 layer.setOpacity(value);
567 }
568 }
569 }
570
571 private double getOpacity() {
572 if (layer != null)
573 return layer.getOpacity();
574 else {
575 double opacity = 0;
576 List<Layer> layers = model.getSelectedLayers();
577 for(Layer layer: layers) {
578 opacity += layer.getOpacity();
579 }
580 return opacity / layers.size();
581 }
582 }
583
584 @Override
585 public void actionPerformed(ActionEvent e) {
586 slider.setValue((int)Math.round(getOpacity()*100));
587 if (e.getSource() == opacityButton) {
588 popup.show(opacityButton, 0, opacityButton.getHeight());
589 } else {
590 // Action can be trigger either by opacity button or by popup menu (in case toggle buttons are hidden).
591 // In that case, show it in the middle of screen (because opacityButton is not visible)
592 popup.show(Main.parent, Main.parent.getWidth() / 2, (Main.parent.getHeight() - popup.getHeight()) / 2);
593 }
594 }
595
596 @Override
597 public void updateEnabledState() {
598 if (layer == null) {
599 setEnabled(! getModel().getSelectedLayers().isEmpty());
600 } else {
601 setEnabled(true);
602 }
603 }
604
605 @Override
606 public Component createMenuComponent() {
607 return new JMenuItem(this);
608 }
609
610 @Override
611 public boolean supportLayers(List<Layer> layers) {
612 return true;
613 }
614
615 @Override
616 public boolean equals(Object obj) {
617 return obj instanceof LayerOpacityAction;
618 }
619
620 @Override
621 public int hashCode() {
622 return getClass().hashCode();
623 }
624 }
625
626 /**
627 * The action to activate the currently selected layer
628 */
629
630 public final class ActivateLayerAction extends AbstractAction implements IEnabledStateUpdating, MapView.LayerChangeListener, MultikeyShortcutAction{
631 private Layer layer;
632 private Shortcut multikeyShortcut;
633
634 public ActivateLayerAction(Layer layer) {
635 this();
636 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
637 this.layer = layer;
638 putValue(NAME, tr("Activate"));
639 updateEnabledState();
640 }
641
642 public ActivateLayerAction() {
643 putValue(NAME, tr("Activate"));
644 putValue(SMALL_ICON, ImageProvider.get("dialogs", "activate"));
645 putValue(SHORT_DESCRIPTION, tr("Activate the selected layer"));
646 multikeyShortcut = Shortcut.registerShortcut("core_multikey:activateLayer", tr("Multikey: {0}",
647 tr("Activate layer")), KeyEvent.VK_A, Shortcut.SHIFT);
648 multikeyShortcut.setAccelerator(this);
649 putValue("help", HelpUtil.ht("/Dialog/LayerList#ActivateLayer"));
650 }
651
652 @Override
653 public Shortcut getMultikeyShortcut() {
654 return multikeyShortcut;
655 }
656
657 @Override
658 public void actionPerformed(ActionEvent e) {
659 Layer toActivate;
660 if (layer != null) {
661 toActivate = layer;
662 } else {
663 toActivate = model.getSelectedLayers().get(0);
664 }
665 execute(toActivate);
666 }
667
668 private void execute(Layer layer) {
669 // model is going to be updated via LayerChangeListener
670 // and PropertyChangeEvents
671 Main.map.mapView.setActiveLayer(layer);
672 layer.setVisible(true);
673 }
674
675 protected boolean isActiveLayer(Layer layer) {
676 if (Main.map == null) return false;
677 if (Main.map.mapView == null) return false;
678 return Main.map.mapView.getActiveLayer() == layer;
679 }
680
681 @Override
682 public void updateEnabledState() {
683 GuiHelper.runInEDTAndWait(new Runnable() {
684 @Override
685 public void run() {
686 if (layer == null) {
687 if (getModel().getSelectedLayers().size() != 1) {
688 setEnabled(false);
689 return;
690 }
691 Layer selectedLayer = getModel().getSelectedLayers().get(0);
692 setEnabled(!isActiveLayer(selectedLayer));
693 } else {
694 setEnabled(!isActiveLayer(layer));
695 }
696 }
697 });
698 }
699
700 @Override
701 public void activeLayerChange(Layer oldLayer, Layer newLayer) {
702 updateEnabledState();
703 }
704 @Override
705 public void layerAdded(Layer newLayer) {
706 updateEnabledState();
707 }
708 @Override
709 public void layerRemoved(Layer oldLayer) {
710 updateEnabledState();
711 }
712
713 @Override
714 public void executeMultikeyAction(int index, boolean repeat) {
715 Layer l = LayerListDialog.getLayerForIndex(index);
716 if (l != null) {
717 execute(l);
718 }
719 }
720
721 @Override
722 public List<MultikeyInfo> getMultikeyCombinations() {
723 return LayerListDialog.getLayerInfoByClass(Layer.class);
724 }
725
726 @Override
727 public MultikeyInfo getLastMultikeyAction() {
728 return null; // Repeating action doesn't make much sense for activating
729 }
730 }
731
732 /**
733 * The action to merge the currently selected layer into another layer.
734 */
735 public final class MergeAction extends AbstractAction implements IEnabledStateUpdating {
736 private Layer layer;
737
738 public MergeAction(Layer layer) throws IllegalArgumentException {
739 this();
740 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
741 this.layer = layer;
742 putValue(NAME, tr("Merge"));
743 updateEnabledState();
744 }
745
746 public MergeAction() {
747 putValue(NAME, tr("Merge"));
748 putValue(SMALL_ICON, ImageProvider.get("dialogs", "mergedown"));
749 putValue(SHORT_DESCRIPTION, tr("Merge this layer into another layer"));
750 putValue("help", HelpUtil.ht("/Dialog/LayerList#MergeLayer"));
751 updateEnabledState();
752 }
753
754 @Override
755 public void actionPerformed(ActionEvent e) {
756 if (layer != null) {
757 new MergeLayerAction().merge(layer);
758 } else {
759 if (getModel().getSelectedLayers().size() == 1) {
760 Layer selectedLayer = getModel().getSelectedLayers().get(0);
761 new MergeLayerAction().merge(selectedLayer);
762 } else {
763 new MergeLayerAction().merge(getModel().getSelectedLayers());
764 }
765 }
766 }
767
768 protected boolean isActiveLayer(Layer layer) {
769 if (Main.map == null) return false;
770 if (Main.map.mapView == null) return false;
771 return Main.map.mapView.getActiveLayer() == layer;
772 }
773
774 @Override
775 public void updateEnabledState() {
776 if (layer == null) {
777 if (getModel().getSelectedLayers().isEmpty()) {
778 setEnabled(false);
779 } else if (getModel().getSelectedLayers().size() > 1) {
780 Layer firstLayer = getModel().getSelectedLayers().get(0);
781 for (Layer l: getModel().getSelectedLayers()) {
782 if (l != firstLayer && (!l.isMergable(firstLayer) || !firstLayer.isMergable(l))) {
783 setEnabled(false);
784 return;
785 }
786 }
787 setEnabled(true);
788 } else {
789 Layer selectedLayer = getModel().getSelectedLayers().get(0);
790 List<Layer> targets = getModel().getPossibleMergeTargets(selectedLayer);
791 setEnabled(!targets.isEmpty());
792 }
793 } else {
794 List<Layer> targets = getModel().getPossibleMergeTargets(layer);
795 setEnabled(!targets.isEmpty());
796 }
797 }
798 }
799
800 /**
801 * The action to merge the currently selected layer into another layer.
802 */
803 public final class DuplicateAction extends AbstractAction implements IEnabledStateUpdating {
804 private Layer layer;
805
806 public DuplicateAction(Layer layer) throws IllegalArgumentException {
807 this();
808 CheckParameterUtil.ensureParameterNotNull(layer, "layer");
809 this.layer = layer;
810 updateEnabledState();
811 }
812
813 public DuplicateAction() {
814 putValue(NAME, tr("Duplicate"));
815 putValue(SMALL_ICON, ImageProvider.get("dialogs", "duplicatelayer"));
816 putValue(SHORT_DESCRIPTION, tr("Duplicate this layer"));
817 putValue("help", HelpUtil.ht("/Dialog/LayerList#DuplicateLayer"));
818 updateEnabledState();
819 }
820
821 private void duplicate(Layer layer) {
822 if (Main.map == null || Main.map.mapView == null)
823 return;
824
825 List<String> layerNames = new ArrayList<String>();
826 for (Layer l: Main.map.mapView.getAllLayers()) {
827 layerNames.add(l.getName());
828 }
829 if (layer instanceof OsmDataLayer) {
830 OsmDataLayer oldLayer = (OsmDataLayer)layer;
831 // Translators: "Copy of {layer name}"
832 String newName = tr("Copy of {0}", oldLayer.getName());
833 int i = 2;
834 while (layerNames.contains(newName)) {
835 // Translators: "Copy {number} of {layer name}"
836 newName = tr("Copy {1} of {0}", oldLayer.getName(), i);
837 i++;
838 }
839 Main.main.addLayer(new OsmDataLayer(oldLayer.data.clone(), newName, null));
840 }
841 }
842
843 @Override
844 public void actionPerformed(ActionEvent e) {
845 if (layer != null) {
846 duplicate(layer);
847 } else {
848 duplicate(getModel().getSelectedLayers().get(0));
849 }
850 }
851
852 protected boolean isActiveLayer(Layer layer) {
853 if (Main.map == null || Main.map.mapView == null)
854 return false;
855 return Main.map.mapView.getActiveLayer() == layer;
856 }
857
858 @Override
859 public void updateEnabledState() {
860 if (layer == null) {
861 if (getModel().getSelectedLayers().size() == 1) {
862 setEnabled(getModel().getSelectedLayers().get(0) instanceof OsmDataLayer);
863 } else {
864 setEnabled(false);
865 }
866 } else {
867 setEnabled(layer instanceof OsmDataLayer);
868 }
869 }
870 }
871
872 private static class ActiveLayerCheckBox extends JCheckBox {
873 public ActiveLayerCheckBox() {
874 setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
875 ImageIcon blank = ImageProvider.get("dialogs/layerlist", "blank");
876 ImageIcon active = ImageProvider.get("dialogs/layerlist", "active");
877 setIcon(blank);
878 setSelectedIcon(active);
879 setRolloverIcon(blank);
880 setRolloverSelectedIcon(active);
881 setPressedIcon(ImageProvider.get("dialogs/layerlist", "active-pressed"));
882 }
883 }
884
885 private static class LayerVisibleCheckBox extends JCheckBox {
886 private final ImageIcon icon_eye;
887 private final ImageIcon icon_eye_translucent;
888 private boolean isTranslucent;
889 public LayerVisibleCheckBox() {
890 setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
891 icon_eye = ImageProvider.get("dialogs/layerlist", "eye");
892 icon_eye_translucent = ImageProvider.get("dialogs/layerlist", "eye-translucent");
893 setIcon(ImageProvider.get("dialogs/layerlist", "eye-off"));
894 setPressedIcon(ImageProvider.get("dialogs/layerlist", "eye-pressed"));
895 setSelectedIcon(icon_eye);
896 isTranslucent = false;
897 }
898
899 public void setTranslucent(boolean isTranslucent) {
900 if (this.isTranslucent == isTranslucent) return;
901 if (isTranslucent) {
902 setSelectedIcon(icon_eye_translucent);
903 } else {
904 setSelectedIcon(icon_eye);
905 }
906 this.isTranslucent = isTranslucent;
907 }
908
909 public void updateStatus(Layer layer) {
910 boolean visible = layer.isVisible();
911 setSelected(visible);
912 setTranslucent(layer.getOpacity()<1.0);
913 setToolTipText(visible ? tr("layer is currently visible (click to hide layer)") : tr("layer is currently hidden (click to show layer)"));
914 }
915 }
916
917 private static class ActiveLayerCellRenderer implements TableCellRenderer {
918 JCheckBox cb;
919 public ActiveLayerCellRenderer() {
920 cb = new ActiveLayerCheckBox();
921 }
922
923 @Override
924 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
925 boolean active = value != null && (Boolean) value;
926 cb.setSelected(active);
927 cb.setToolTipText(active ? tr("this layer is the active layer") : tr("this layer is not currently active (click to activate)"));
928 return cb;
929 }
930 }
931
932 private static class LayerVisibleCellRenderer implements TableCellRenderer {
933 LayerVisibleCheckBox cb;
934 public LayerVisibleCellRenderer() {
935 this.cb = new LayerVisibleCheckBox();
936 }
937
938 @Override
939 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
940 if (value != null) {
941 cb.updateStatus((Layer)value);
942 }
943 return cb;
944 }
945 }
946
947 private static class LayerVisibleCellEditor extends DefaultCellEditor {
948 LayerVisibleCheckBox cb;
949 public LayerVisibleCellEditor(LayerVisibleCheckBox cb) {
950 super(cb);
951 this.cb = cb;
952 }
953
954 @Override
955 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
956 cb.updateStatus((Layer)value);
957 return cb;
958 }
959 }
960
961 private class LayerNameCellRenderer extends DefaultTableCellRenderer {
962
963 protected boolean isActiveLayer(Layer layer) {
964 if (Main.map == null) return false;
965 if (Main.map.mapView == null) return false;
966 return Main.map.mapView.getActiveLayer() == layer;
967 }
968
969 @Override
970 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
971 if (value == null)
972 return this;
973 Layer layer = (Layer)value;
974 JLabel label = (JLabel)super.getTableCellRendererComponent(table,
975 layer.getName(), isSelected, hasFocus, row, column);
976 if (isActiveLayer(layer)) {
977 label.setFont(label.getFont().deriveFont(Font.BOLD));
978 }
979 if(Main.pref.getBoolean("dialog.layer.colorname", true)) {
980 Color c = layer.getColor(false);
981 if(c != null) {
982 Color oc = null;
983 for(Layer l : model.getLayers()) {
984 oc = l.getColor(false);
985 if(oc != null) {
986 if(oc.equals(c)) {
987 oc = null;
988 } else {
989 break;
990 }
991 }
992 }
993 /* not more than one color, don't use coloring */
994 if(oc == null) {
995 c = null;
996 }
997 }
998 if(c == null) {
999 c = Main.pref.getUIColor(isSelected ? "Table.selectionForeground" : "Table.foreground");
1000 }
1001 label.setForeground(c);
1002 }
1003 label.setIcon(layer.getIcon());
1004 label.setToolTipText(layer.getToolTipText());
1005 return label;
1006 }
1007 }
1008
1009 private static class LayerNameCellEditor extends DefaultCellEditor {
1010 public LayerNameCellEditor(JosmTextField tf) {
1011 super(tf);
1012 }
1013
1014 @Override
1015 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
1016 JosmTextField tf = (JosmTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);
1017 tf.setText(value == null ? "" : ((Layer) value).getName());
1018 return tf;
1019 }
1020 }
1021
1022 class PopupMenuHandler extends PopupMenuLauncher {
1023 @Override public void launch(MouseEvent evt) {
1024 Layer layer = getModel().getLayer(layerList.getSelectedRow());
1025 menu = new LayerListPopup(getModel().getSelectedLayers(), layer);
1026 super.launch(evt);
1027 }
1028 }
1029
1030 /**
1031 * The action to move up the currently selected entries in the list.
1032 */
1033 class MoveUpAction extends AbstractAction implements IEnabledStateUpdating{
1034 public MoveUpAction() {
1035 putValue(NAME, tr("Move up"));
1036 putValue(SMALL_ICON, ImageProvider.get("dialogs", "up"));
1037 putValue(SHORT_DESCRIPTION, tr("Move the selected layer one row up."));
1038 updateEnabledState();
1039 }
1040
1041 @Override
1042 public void updateEnabledState() {
1043 setEnabled(model.canMoveUp());
1044 }
1045
1046 @Override
1047 public void actionPerformed(ActionEvent e) {
1048 model.moveUp();
1049 }
1050 }
1051
1052 /**
1053 * The action to move down the currently selected entries in the list.
1054 */
1055 class MoveDownAction extends AbstractAction implements IEnabledStateUpdating {
1056 public MoveDownAction() {
1057 putValue(NAME, tr("Move down"));
1058 putValue(SMALL_ICON, ImageProvider.get("dialogs", "down"));
1059 putValue(SHORT_DESCRIPTION, tr("Move the selected layer one row down."));
1060 updateEnabledState();
1061 }
1062
1063 @Override
1064 public void updateEnabledState() {
1065 setEnabled(model.canMoveDown());
1066 }
1067
1068 @Override
1069 public void actionPerformed(ActionEvent e) {
1070 model.moveDown();
1071 }
1072 }
1073
1074 /**
1075 * Observer interface to be implemented by views using {@link LayerListModel}
1076 *
1077 */
1078 public interface LayerListModelListener {
1079 public void makeVisible(int index, Layer layer);
1080 public void refresh();
1081 }
1082
1083 /**
1084 * The layer list model. The model manages a list of layers and provides methods for
1085 * moving layers up and down, for toggling their visibility, and for activating a layer.
1086 *
1087 * The model is a {@link TableModel} and it provides a {@link ListSelectionModel}. It expects
1088 * to be configured with a {@link DefaultListSelectionModel}. The selection model is used
1089 * to update the selection state of views depending on messages sent to the model.
1090 *
1091 * The model manages a list of {@link LayerListModelListener} which are mainly notified if
1092 * the model requires views to make a specific list entry visible.
1093 *
1094 * It also listens to {@link PropertyChangeEvent}s of every {@link Layer} it manages, in particular to
1095 * the properties {@link Layer#VISIBLE_PROP} and {@link Layer#NAME_PROP}.
1096 */
1097 public class LayerListModel extends AbstractTableModel implements MapView.LayerChangeListener, PropertyChangeListener {
1098 /** manages list selection state*/
1099 private DefaultListSelectionModel selectionModel;
1100 private CopyOnWriteArrayList<LayerListModelListener> listeners;
1101
1102 /**
1103 * constructor
1104 *
1105 * @param selectionModel the list selection model
1106 */
1107 private LayerListModel(DefaultListSelectionModel selectionModel) {
1108 this.selectionModel = selectionModel;
1109 listeners = new CopyOnWriteArrayList<LayerListModelListener>();
1110 }
1111
1112 /**
1113 * Adds a listener to this model
1114 *
1115 * @param listener the listener
1116 */
1117 public void addLayerListModelListener(LayerListModelListener listener) {
1118 if (listener != null) {
1119 listeners.addIfAbsent(listener);
1120 }
1121 }
1122
1123 /**
1124 * removes a listener from this model
1125 * @param listener the listener
1126 *
1127 */
1128 public void removeLayerListModelListener(LayerListModelListener listener) {
1129 listeners.remove(listener);
1130 }
1131
1132 /**
1133 * Fires a make visible event to listeners
1134 *
1135 * @param index the index of the row to make visible
1136 * @param layer the layer at this index
1137 * @see LayerListModelListener#makeVisible(int, Layer)
1138 */
1139 protected void fireMakeVisible(int index, Layer layer) {
1140 for (LayerListModelListener listener : listeners) {
1141 listener.makeVisible(index, layer);
1142 }
1143 }
1144
1145 /**
1146 * Fires a refresh event to listeners of this model
1147 *
1148 * @see LayerListModelListener#refresh()
1149 */
1150 protected void fireRefresh() {
1151 for (LayerListModelListener listener : listeners) {
1152 listener.refresh();
1153 }
1154 }
1155
1156 /**
1157 * Populates the model with the current layers managed by
1158 * {@link MapView}.
1159 *
1160 */
1161 public void populate() {
1162 for (Layer layer: getLayers()) {
1163 // make sure the model is registered exactly once
1164 //
1165 layer.removePropertyChangeListener(this);
1166 layer.addPropertyChangeListener(this);
1167 }
1168 fireTableDataChanged();
1169 }
1170
1171 /**
1172 * Marks <code>layer</code> as selected layer. Ignored, if
1173 * layer is null.
1174 *
1175 * @param layer the layer.
1176 */
1177 public void setSelectedLayer(Layer layer) {
1178 if (layer == null)
1179 return;
1180 int idx = getLayers().indexOf(layer);
1181 if (idx >= 0) {
1182 selectionModel.setSelectionInterval(idx, idx);
1183 }
1184 ensureSelectedIsVisible();
1185 }
1186
1187 /**
1188 * Replies the list of currently selected layers. Never null, but may
1189 * be empty.
1190 *
1191 * @return the list of currently selected layers. Never null, but may
1192 * be empty.
1193 */
1194 public List<Layer> getSelectedLayers() {
1195 ArrayList<Layer> selected = new ArrayList<Layer>();
1196 for (int i=0; i<getLayers().size(); i++) {
1197 if (selectionModel.isSelectedIndex(i)) {
1198 selected.add(getLayers().get(i));
1199 }
1200 }
1201 return selected;
1202 }
1203
1204 /**
1205 * Replies a the list of indices of the selected rows. Never null,
1206 * but may be empty.
1207 *
1208 * @return the list of indices of the selected rows. Never null,
1209 * but may be empty.
1210 */
1211 public List<Integer> getSelectedRows() {
1212 ArrayList<Integer> selected = new ArrayList<Integer>();
1213 for (int i=0; i<getLayers().size();i++) {
1214 if (selectionModel.isSelectedIndex(i)) {
1215 selected.add(i);
1216 }
1217 }
1218 return selected;
1219 }
1220
1221 /**
1222 * Invoked if a layer managed by {@link MapView} is removed
1223 *
1224 * @param layer the layer which is removed
1225 */
1226 protected void onRemoveLayer(Layer layer) {
1227 if (layer == null)
1228 return;
1229 layer.removePropertyChangeListener(this);
1230 final int size = getRowCount();
1231 final List<Integer> rows = getSelectedRows();
1232 GuiHelper.runInEDTAndWait(new Runnable() {
1233 @Override
1234 public void run() {
1235 if (rows.isEmpty() && size > 0) {
1236 selectionModel.setSelectionInterval(size-1, size-1);
1237 }
1238 fireTableDataChanged();
1239 fireRefresh();
1240 ensureActiveSelected();
1241 }
1242 });
1243 }
1244
1245 /**
1246 * Invoked when a layer managed by {@link MapView} is added
1247 *
1248 * @param layer the layer
1249 */
1250 protected void onAddLayer(Layer layer) {
1251 if (layer == null) return;
1252 layer.addPropertyChangeListener(this);
1253 fireTableDataChanged();
1254 int idx = getLayers().indexOf(layer);
1255 layerList.setRowHeight(idx, Math.max(16, layer.getIcon().getIconHeight()));
1256 selectionModel.setSelectionInterval(idx, idx);
1257 ensureSelectedIsVisible();
1258 }
1259
1260 /**
1261 * Replies the first layer. Null if no layers are present
1262 *
1263 * @return the first layer. Null if no layers are present
1264 */
1265 public Layer getFirstLayer() {
1266 if (getRowCount() == 0) return null;
1267 return getLayers().get(0);
1268 }
1269
1270 /**
1271 * Replies the layer at position <code>index</code>
1272 *
1273 * @param index the index
1274 * @return the layer at position <code>index</code>. Null,
1275 * if index is out of range.
1276 */
1277 public Layer getLayer(int index) {
1278 if (index < 0 || index >= getRowCount())
1279 return null;
1280 return getLayers().get(index);
1281 }
1282
1283 /**
1284 * Replies true if the currently selected layers can move up
1285 * by one position
1286 *
1287 * @return true if the currently selected layers can move up
1288 * by one position
1289 */
1290 public boolean canMoveUp() {
1291 List<Integer> sel = getSelectedRows();
1292 return !sel.isEmpty() && sel.get(0) > 0;
1293 }
1294
1295 /**
1296 * Move up the currently selected layers by one position
1297 *
1298 */
1299 public void moveUp() {
1300 if (!canMoveUp()) return;
1301 List<Integer> sel = getSelectedRows();
1302 for (int row : sel) {
1303 Layer l1 = getLayers().get(row);
1304 Layer l2 = getLayers().get(row-1);
1305 Main.map.mapView.moveLayer(l2,row);
1306 Main.map.mapView.moveLayer(l1, row-1);
1307 }
1308 fireTableDataChanged();
1309 selectionModel.clearSelection();
1310 for (int row : sel) {
1311 selectionModel.addSelectionInterval(row-1, row-1);
1312 }
1313 ensureSelectedIsVisible();
1314 }
1315
1316 /**
1317 * Replies true if the currently selected layers can move down
1318 * by one position
1319 *
1320 * @return true if the currently selected layers can move down
1321 * by one position
1322 */
1323 public boolean canMoveDown() {
1324 List<Integer> sel = getSelectedRows();
1325 return !sel.isEmpty() && sel.get(sel.size()-1) < getLayers().size()-1;
1326 }
1327
1328 /**
1329 * Move down the currently selected layers by one position
1330 *
1331 */
1332 public void moveDown() {
1333 if (!canMoveDown()) return;
1334 List<Integer> sel = getSelectedRows();
1335 Collections.reverse(sel);
1336 for (int row : sel) {
1337 Layer l1 = getLayers().get(row);
1338 Layer l2 = getLayers().get(row+1);
1339 Main.map.mapView.moveLayer(l1, row+1);
1340 Main.map.mapView.moveLayer(l2, row);
1341 }
1342 fireTableDataChanged();
1343 selectionModel.clearSelection();
1344 for (int row : sel) {
1345 selectionModel.addSelectionInterval(row+1, row+1);
1346 }
1347 ensureSelectedIsVisible();
1348 }
1349
1350 /**
1351 * Make sure the first of the selected layers is visible in the
1352 * views of this model.
1353 *
1354 */
1355 protected void ensureSelectedIsVisible() {
1356 int index = selectionModel.getMinSelectionIndex();
1357 if (index < 0) return;
1358 if (index >= getLayers().size()) return;
1359 Layer layer = getLayers().get(index);
1360 fireMakeVisible(index, layer);
1361 }
1362
1363 /**
1364 * Replies a list of layers which are possible merge targets
1365 * for <code>source</code>
1366 *
1367 * @param source the source layer
1368 * @return a list of layers which are possible merge targets
1369 * for <code>source</code>. Never null, but can be empty.
1370 */
1371 public List<Layer> getPossibleMergeTargets(Layer source) {
1372 ArrayList<Layer> targets = new ArrayList<Layer>();
1373 if (source == null)
1374 return targets;
1375 for (Layer target : getLayers()) {
1376 if (source == target) {
1377 continue;
1378 }
1379 if (target.isMergable(source) && source.isMergable(target)) {
1380 targets.add(target);
1381 }
1382 }
1383 return targets;
1384 }
1385
1386 /**
1387 * Replies the list of layers currently managed by {@link MapView}.
1388 * Never null, but can be empty.
1389 *
1390 * @return the list of layers currently managed by {@link MapView}.
1391 * Never null, but can be empty.
1392 */
1393 public List<Layer> getLayers() {
1394 if (Main.map == null || Main.map.mapView == null)
1395 return Collections.<Layer>emptyList();
1396 return Main.map.mapView.getAllLayersAsList();
1397 }
1398
1399 /**
1400 * Ensures that at least one layer is selected in the layer dialog
1401 *
1402 */
1403 protected void ensureActiveSelected() {
1404 if (getLayers().isEmpty())
1405 return;
1406 final Layer activeLayer = getActiveLayer();
1407 if (activeLayer != null) {
1408 // there's an active layer - select it and make it
1409 // visible
1410 int idx = getLayers().indexOf(activeLayer);
1411 selectionModel.setSelectionInterval(idx, idx);
1412 ensureSelectedIsVisible();
1413 } else {
1414 // no active layer - select the first one and make
1415 // it visible
1416 selectionModel.setSelectionInterval(0, 0);
1417 ensureSelectedIsVisible();
1418 }
1419 }
1420
1421 /**
1422 * Replies the active layer. null, if no active layer is available
1423 *
1424 * @return the active layer. null, if no active layer is available
1425 */
1426 protected Layer getActiveLayer() {
1427 if (Main.map == null || Main.map.mapView == null) return null;
1428 return Main.map.mapView.getActiveLayer();
1429 }
1430
1431 /* ------------------------------------------------------------------------------ */
1432 /* Interface TableModel */
1433 /* ------------------------------------------------------------------------------ */
1434
1435 @Override
1436 public int getRowCount() {
1437 List<Layer> layers = getLayers();
1438 if (layers == null) return 0;
1439 return layers.size();
1440 }
1441
1442 @Override
1443 public int getColumnCount() {
1444 return 3;
1445 }
1446
1447 @Override
1448 public Object getValueAt(int row, int col) {
1449 if (row >= 0 && row < getLayers().size()) {
1450 switch (col) {
1451 case 0: return getLayers().get(row) == getActiveLayer();
1452 case 1: return getLayers().get(row);
1453 case 2: return getLayers().get(row);
1454 default: throw new RuntimeException();
1455 }
1456 }
1457 return null;
1458 }
1459
1460 @Override
1461 public boolean isCellEditable(int row, int col) {
1462 if (col == 0 && getActiveLayer() == getLayers().get(row))
1463 return false;
1464 return true;
1465 }
1466
1467 @Override
1468 public void setValueAt(Object value, int row, int col) {
1469 Layer l = getLayers().get(row);
1470 switch (col) {
1471 case 0:
1472 Main.map.mapView.setActiveLayer(l);
1473 l.setVisible(true);
1474 break;
1475 case 1:
1476 l.setVisible((Boolean) value);
1477 break;
1478 case 2:
1479 l.setName((String) value);
1480 break;
1481 default: throw new RuntimeException();
1482 }
1483 fireTableCellUpdated(row, col);
1484 }
1485
1486 /* ------------------------------------------------------------------------------ */
1487 /* Interface LayerChangeListener */
1488 /* ------------------------------------------------------------------------------ */
1489 @Override
1490 public void activeLayerChange(final Layer oldLayer, final Layer newLayer) {
1491 GuiHelper.runInEDTAndWait(new Runnable() {
1492 @Override
1493 public void run() {
1494 if (oldLayer != null) {
1495 int idx = getLayers().indexOf(oldLayer);
1496 if (idx >= 0) {
1497 fireTableRowsUpdated(idx,idx);
1498 }
1499 }
1500
1501 if (newLayer != null) {
1502 int idx = getLayers().indexOf(newLayer);
1503 if (idx >= 0) {
1504 fireTableRowsUpdated(idx,idx);
1505 }
1506 }
1507 ensureActiveSelected();
1508 }
1509 });
1510 }
1511
1512 @Override
1513 public void layerAdded(Layer newLayer) {
1514 onAddLayer(newLayer);
1515 }
1516
1517 @Override
1518 public void layerRemoved(final Layer oldLayer) {
1519 onRemoveLayer(oldLayer);
1520 }
1521
1522 /* ------------------------------------------------------------------------------ */
1523 /* Interface PropertyChangeListener */
1524 /* ------------------------------------------------------------------------------ */
1525 @Override
1526 public void propertyChange(PropertyChangeEvent evt) {
1527 if (evt.getSource() instanceof Layer) {
1528 Layer layer = (Layer)evt.getSource();
1529 final int idx = getLayers().indexOf(layer);
1530 if (idx < 0) return;
1531 fireRefresh();
1532 }
1533 }
1534 }
1535
1536 static class LayerList extends JTable {
1537 public LayerList(TableModel dataModel) {
1538 super(dataModel);
1539 }
1540
1541 public void scrollToVisible(int row, int col) {
1542 if (!(getParent() instanceof JViewport))
1543 return;
1544 JViewport viewport = (JViewport) getParent();
1545 Rectangle rect = getCellRect(row, col, true);
1546 Point pt = viewport.getViewPosition();
1547 rect.setLocation(rect.x - pt.x, rect.y - pt.y);
1548 viewport.scrollRectToVisible(rect);
1549 }
1550 }
1551
1552 /**
1553 * Creates a {@link ShowHideLayerAction} for <code>layer</code> in the
1554 * context of this {@link LayerListDialog}.
1555 *
1556 * @return the action
1557 */
1558 public ShowHideLayerAction createShowHideLayerAction() {
1559 ShowHideLayerAction act = new ShowHideLayerAction(true);
1560 act.putValue(Action.NAME, tr("Show/Hide"));
1561 return act;
1562 }
1563
1564 /**
1565 * Creates a {@link DeleteLayerAction} for <code>layer</code> in the
1566 * context of this {@link LayerListDialog}.
1567 *
1568 * @return the action
1569 */
1570 public DeleteLayerAction createDeleteLayerAction() {
1571 // the delete layer action doesn't depend on the current layer
1572 return new DeleteLayerAction();
1573 }
1574
1575 /**
1576 * Creates a {@link ActivateLayerAction} for <code>layer</code> in the
1577 * context of this {@link LayerListDialog}.
1578 *
1579 * @param layer the layer
1580 * @return the action
1581 */
1582 public ActivateLayerAction createActivateLayerAction(Layer layer) {
1583 return new ActivateLayerAction(layer);
1584 }
1585
1586 /**
1587 * Creates a {@link MergeLayerAction} for <code>layer</code> in the
1588 * context of this {@link LayerListDialog}.
1589 *
1590 * @param layer the layer
1591 * @return the action
1592 */
1593 public MergeAction createMergeLayerAction(Layer layer) {
1594 return new MergeAction(layer);
1595 }
1596
1597 public static Layer getLayerForIndex(int index) {
1598
1599 if (!Main.isDisplayingMapView())
1600 return null;
1601
1602 List<Layer> layers = Main.map.mapView.getAllLayersAsList();
1603
1604 if (index < layers.size() && index >= 0)
1605 return layers.get(index);
1606 else
1607 return null;
1608 }
1609
1610 // This is not Class<? extends Layer> on purpose, to allow asking for layers implementing some interface
1611 public static List<MultikeyInfo> getLayerInfoByClass(Class<?> layerClass) {
1612
1613 List<MultikeyInfo> result = new ArrayList<MultikeyShortcutAction.MultikeyInfo>();
1614
1615 if (!Main.isDisplayingMapView())
1616 return result;
1617
1618 List<Layer> layers = Main.map.mapView.getAllLayersAsList();
1619
1620 int index = 0;
1621 for (Layer l: layers) {
1622 if (layerClass.isAssignableFrom(l.getClass())) {
1623 result.add(new MultikeyInfo(index, l.getName()));
1624 }
1625 index++;
1626 }
1627
1628 return result;
1629 }
1630
1631 public static boolean isLayerValid(Layer l) {
1632 if (l == null)
1633 return false;
1634
1635 if (!Main.isDisplayingMapView())
1636 return false;
1637
1638 return Main.map.mapView.getAllLayersAsList().indexOf(l) >= 0;
1639 }
1640
1641 public static MultikeyInfo getLayerInfo(Layer l) {
1642
1643 if (l == null)
1644 return null;
1645
1646 if (!Main.isDisplayingMapView())
1647 return null;
1648
1649 int index = Main.map.mapView.getAllLayersAsList().indexOf(l);
1650 if (index < 0)
1651 return null;
1652
1653 return new MultikeyInfo(index, l.getName());
1654 }
1655}
Note: See TracBrowser for help on using the repository browser.