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

Last change on this file since 4595 was 4595, checked in by jttt, 12 years ago

Multikey keyboard shorcuts - shorcuts like Ctrl+Alt+S,n will toggle n-th layer visibility, added shorcuts also for Jump to next/previous marker. Shorcuts for other actions can be easily added.

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