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

Last change on this file since 4953 was 4953, checked in by stoecker, 12 years ago

fix some shortcut deprecations

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