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

Last change on this file since 17188 was 17188, checked in by Klumbumbus, 4 years ago

fix #19851 - Fix shortcut names

  • Property svn:eol-style set to native
File size: 52.1 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.Graphics;
11import java.awt.Graphics2D;
12import java.awt.GraphicsEnvironment;
13import java.awt.RenderingHints;
14import java.awt.event.ActionEvent;
15import java.awt.event.InputEvent;
16import java.awt.event.KeyEvent;
17import java.awt.event.MouseEvent;
18import java.beans.PropertyChangeEvent;
19import java.beans.PropertyChangeListener;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.List;
23import java.util.Locale;
24import java.util.concurrent.CopyOnWriteArrayList;
25import java.util.stream.Collectors;
26import java.util.stream.IntStream;
27
28import javax.swing.AbstractAction;
29import javax.swing.DefaultCellEditor;
30import javax.swing.DefaultListSelectionModel;
31import javax.swing.DropMode;
32import javax.swing.Icon;
33import javax.swing.ImageIcon;
34import javax.swing.JCheckBox;
35import javax.swing.JComponent;
36import javax.swing.JLabel;
37import javax.swing.JTable;
38import javax.swing.KeyStroke;
39import javax.swing.ListSelectionModel;
40import javax.swing.UIManager;
41import javax.swing.table.AbstractTableModel;
42import javax.swing.table.DefaultTableCellRenderer;
43import javax.swing.table.TableCellRenderer;
44import javax.swing.table.TableModel;
45
46import org.openstreetmap.josm.actions.ExpertToggleAction;
47import org.openstreetmap.josm.actions.ExpertToggleAction.ExpertModeChangeListener;
48import org.openstreetmap.josm.actions.MergeLayerAction;
49import org.openstreetmap.josm.data.coor.EastNorth;
50import org.openstreetmap.josm.data.imagery.OffsetBookmark;
51import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeEvent;
52import org.openstreetmap.josm.data.preferences.AbstractProperty.ValueChangeListener;
53import org.openstreetmap.josm.data.preferences.BooleanProperty;
54import org.openstreetmap.josm.gui.MainApplication;
55import org.openstreetmap.josm.gui.MapFrame;
56import org.openstreetmap.josm.gui.MapView;
57import org.openstreetmap.josm.gui.SideButton;
58import org.openstreetmap.josm.gui.dialogs.layer.ActivateLayerAction;
59import org.openstreetmap.josm.gui.dialogs.layer.CycleLayerDownAction;
60import org.openstreetmap.josm.gui.dialogs.layer.CycleLayerUpAction;
61import org.openstreetmap.josm.gui.dialogs.layer.DeleteLayerAction;
62import org.openstreetmap.josm.gui.dialogs.layer.DuplicateAction;
63import org.openstreetmap.josm.gui.dialogs.layer.LayerListTransferHandler;
64import org.openstreetmap.josm.gui.dialogs.layer.LayerVisibilityAction;
65import org.openstreetmap.josm.gui.dialogs.layer.MergeAction;
66import org.openstreetmap.josm.gui.dialogs.layer.MoveDownAction;
67import org.openstreetmap.josm.gui.dialogs.layer.MoveUpAction;
68import org.openstreetmap.josm.gui.dialogs.layer.ShowHideLayerAction;
69import org.openstreetmap.josm.gui.layer.AbstractTileSourceLayer;
70import org.openstreetmap.josm.gui.layer.JumpToMarkerActions;
71import org.openstreetmap.josm.gui.layer.Layer;
72import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
73import org.openstreetmap.josm.gui.layer.LayerManager.LayerChangeListener;
74import org.openstreetmap.josm.gui.layer.LayerManager.LayerOrderChangeEvent;
75import org.openstreetmap.josm.gui.layer.LayerManager.LayerRemoveEvent;
76import org.openstreetmap.josm.gui.layer.MainLayerManager;
77import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
78import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
79import org.openstreetmap.josm.gui.layer.NativeScaleLayer;
80import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings;
81import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings.DisplaySettingsChangeEvent;
82import org.openstreetmap.josm.gui.layer.imagery.TileSourceDisplaySettings.DisplaySettingsChangeListener;
83import org.openstreetmap.josm.gui.util.MultikeyActionsHandler;
84import org.openstreetmap.josm.gui.util.MultikeyShortcutAction.MultikeyInfo;
85import org.openstreetmap.josm.gui.util.ReorderableTableModel;
86import org.openstreetmap.josm.gui.util.TableHelper;
87import org.openstreetmap.josm.gui.widgets.DisableShortcutsOnFocusGainedTextField;
88import org.openstreetmap.josm.gui.widgets.JosmTextField;
89import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
90import org.openstreetmap.josm.gui.widgets.ScrollableTable;
91import org.openstreetmap.josm.spi.preferences.Config;
92import org.openstreetmap.josm.tools.ArrayUtils;
93import org.openstreetmap.josm.tools.ImageProvider;
94import org.openstreetmap.josm.tools.ImageProvider.ImageSizes;
95import org.openstreetmap.josm.tools.InputMapUtils;
96import org.openstreetmap.josm.tools.PlatformManager;
97import org.openstreetmap.josm.tools.Shortcut;
98
99/**
100 * This is a toggle dialog which displays the list of layers. Actions allow to
101 * change the ordering of the layers, to hide/show layers, to activate layers,
102 * and to delete layers.
103 * <p>
104 * Support for multiple {@link LayerListDialog} is currently not complete but intended for the future.
105 * @since 17
106 */
107public class LayerListDialog extends ToggleDialog implements DisplaySettingsChangeListener {
108 /** the unique instance of the dialog */
109 private static volatile LayerListDialog instance;
110
111 private static final BooleanProperty DISPLAY_NUMBERS = new BooleanProperty("layerlist.display.numbers", true);
112
113 /**
114 * Creates the instance of the dialog. It's connected to the layer manager
115 *
116 * @param layerManager the layer manager
117 * @since 11885 (signature)
118 */
119 public static void createInstance(MainLayerManager layerManager) {
120 if (instance != null)
121 throw new IllegalStateException("Dialog was already created");
122 instance = new LayerListDialog(layerManager);
123 }
124
125 /**
126 * Replies the instance of the dialog
127 *
128 * @return the instance of the dialog
129 * @throws IllegalStateException if the dialog is not created yet
130 * @see #createInstance(MainLayerManager)
131 */
132 public static LayerListDialog getInstance() {
133 if (instance == null)
134 throw new IllegalStateException("Dialog not created yet. Invoke createInstance() first");
135 return instance;
136 }
137
138 /** the model for the layer list */
139 private final LayerListModel model;
140
141 /** the list of layers (technically its a JTable, but appears like a list) */
142 private final LayerList layerList;
143 private final ColumnWidthAdaptionListener visibilityWidthListener;
144
145 private final ActivateLayerAction activateLayerAction;
146 private final ShowHideLayerAction showHideLayerAction;
147
148 private final CycleLayerUpAction cycleLayerUpAction;
149 private final CycleLayerDownAction cycleLayerDownAction;
150
151 //TODO This duplicates ShowHide actions functionality
152 /** stores which layer index to toggle and executes the ShowHide action if the layer is present */
153 private final class ToggleLayerIndexVisibility extends AbstractAction {
154 private final int layerIndex;
155
156 ToggleLayerIndexVisibility(int layerIndex) {
157 this.layerIndex = layerIndex;
158 }
159
160 @Override
161 public void actionPerformed(ActionEvent e) {
162 final Layer l = model.getLayer(model.getRowCount() - layerIndex - 1);
163 if (l != null) {
164 l.toggleVisible();
165 }
166 }
167 }
168
169 private final transient Shortcut[] visibilityToggleShortcuts = new Shortcut[10];
170 private final ToggleLayerIndexVisibility[] visibilityToggleActions = new ToggleLayerIndexVisibility[10];
171
172 /**
173 * The {@link MainLayerManager} this list is for.
174 */
175 private final transient MainLayerManager layerManager;
176
177 /**
178 * registers (shortcut to toggle right hand side toggle dialogs)+(number keys) shortcuts
179 * to toggle the visibility of the first ten layers.
180 */
181 private void createVisibilityToggleShortcuts() {
182 for (int i = 0; i < 10; i++) {
183 final int i1 = i + 1;
184 /* POSSIBLE SHORTCUTS: 1,2,3,4,5,6,7,8,9,0=10 */
185 visibilityToggleShortcuts[i] = Shortcut.registerShortcut("subwindow:layers:toggleLayer" + i1,
186 tr("Toggle visibility of layer: {0}", i1), KeyEvent.VK_0 + (i1 % 10), Shortcut.ALT);
187 visibilityToggleActions[i] = new ToggleLayerIndexVisibility(i);
188 MainApplication.registerActionShortcut(visibilityToggleActions[i], visibilityToggleShortcuts[i]);
189 }
190 }
191
192 /**
193 * Creates a layer list and attach it to the given layer manager.
194 * @param layerManager The layer manager this list is for
195 * @since 10467
196 */
197 public LayerListDialog(MainLayerManager layerManager) {
198 super(tr("Layers"), "layerlist", tr("Open a list of all loaded layers."),
199 Shortcut.registerShortcut("subwindow:layers", tr("Windows: {0}", tr("Layers")), KeyEvent.VK_L,
200 Shortcut.ALT_SHIFT), 100, true);
201 this.layerManager = layerManager;
202
203 // create the models
204 //
205 DefaultListSelectionModel selectionModel = new DefaultListSelectionModel();
206 selectionModel.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
207 model = new LayerListModel(layerManager, selectionModel);
208
209 // create the list control
210 //
211 layerList = new LayerList(model);
212 TableHelper.setFont(layerList, getClass());
213 layerList.setSelectionModel(selectionModel);
214 layerList.addMouseListener(new PopupMenuHandler());
215 layerList.setBackground(UIManager.getColor("Button.background"));
216 layerList.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);
217 layerList.putClientProperty("JTable.autoStartsEdit", Boolean.FALSE);
218 layerList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
219 layerList.setTableHeader(null);
220 layerList.setShowGrid(false);
221 layerList.setIntercellSpacing(new Dimension(0, 0));
222 layerList.getColumnModel().getColumn(0).setCellRenderer(new ActiveLayerCellRenderer());
223 layerList.getColumnModel().getColumn(0).setCellEditor(new DefaultCellEditor(new ActiveLayerCheckBox()));
224 layerList.getColumnModel().getColumn(0).setMaxWidth(12);
225 layerList.getColumnModel().getColumn(0).setPreferredWidth(12);
226 layerList.getColumnModel().getColumn(0).setResizable(false);
227
228 layerList.getColumnModel().getColumn(1).setCellRenderer(new NativeScaleLayerCellRenderer());
229 layerList.getColumnModel().getColumn(1).setCellEditor(new DefaultCellEditor(new NativeScaleLayerCheckBox()));
230 layerList.getColumnModel().getColumn(1).setMaxWidth(12);
231 layerList.getColumnModel().getColumn(1).setPreferredWidth(12);
232 layerList.getColumnModel().getColumn(1).setResizable(false);
233
234 layerList.getColumnModel().getColumn(2).setCellRenderer(new OffsetLayerCellRenderer());
235 layerList.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(new OffsetLayerCheckBox()));
236 layerList.getColumnModel().getColumn(2).setMaxWidth(16);
237 layerList.getColumnModel().getColumn(2).setPreferredWidth(16);
238 layerList.getColumnModel().getColumn(2).setResizable(false);
239
240 layerList.getColumnModel().getColumn(3).setCellRenderer(new LayerVisibleCellRenderer());
241 layerList.getColumnModel().getColumn(3).setCellEditor(new LayerVisibleCellEditor(new LayerVisibleCheckBox()));
242 layerList.getColumnModel().getColumn(3).setResizable(false);
243
244 layerList.getColumnModel().getColumn(4).setCellRenderer(new LayerNameCellRenderer());
245 layerList.getColumnModel().getColumn(4).setCellEditor(new LayerNameCellEditor(new DisableShortcutsOnFocusGainedTextField()));
246 // Disable some default JTable shortcuts to use JOSM ones (see #5678, #10458)
247 for (KeyStroke ks : new KeyStroke[] {
248 KeyStroke.getKeyStroke(KeyEvent.VK_C, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx()),
249 KeyStroke.getKeyStroke(KeyEvent.VK_V, PlatformManager.getPlatform().getMenuShortcutKeyMaskEx()),
250 KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.SHIFT_DOWN_MASK),
251 KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.SHIFT_DOWN_MASK),
252 KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.SHIFT_DOWN_MASK),
253 KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.SHIFT_DOWN_MASK),
254 KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, InputEvent.CTRL_DOWN_MASK),
255 KeyStroke.getKeyStroke(KeyEvent.VK_UP, InputEvent.CTRL_DOWN_MASK),
256 KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, InputEvent.CTRL_DOWN_MASK),
257 KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, InputEvent.CTRL_DOWN_MASK),
258 KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
259 KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
260 KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0),
261 KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0),
262 }) {
263 layerList.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ks, new Object());
264 }
265
266 visibilityWidthListener = new ColumnWidthAdaptionListener(3, 16);
267 DISPLAY_NUMBERS.addListener(visibilityWidthListener);
268 ExpertToggleAction.addExpertModeChangeListener(visibilityWidthListener);
269 layerManager.addLayerChangeListener(visibilityWidthListener);
270 visibilityWidthListener.updateColumnWidth();
271
272 // init the model
273 //
274 model.populate();
275 model.setSelectedLayer(layerManager.getActiveLayer());
276 model.addLayerListModelListener(
277 new LayerListModelListener() {
278 @Override
279 public void makeVisible(int row, Layer layer) {
280 layerList.scrollToVisible(row, 0);
281 layerList.repaint();
282 }
283
284 @Override
285 public void refresh() {
286 layerList.repaint();
287 }
288 }
289 );
290
291 // -- move up action
292 MoveUpAction moveUpAction = new MoveUpAction(model);
293 TableHelper.adaptTo(moveUpAction, model);
294 TableHelper.adaptTo(moveUpAction, selectionModel);
295
296 // -- move down action
297 MoveDownAction moveDownAction = new MoveDownAction(model);
298 TableHelper.adaptTo(moveDownAction, model);
299 TableHelper.adaptTo(moveDownAction, selectionModel);
300
301 // -- activate action
302 activateLayerAction = new ActivateLayerAction(model);
303 activateLayerAction.updateEnabledState();
304 MultikeyActionsHandler.getInstance().addAction(activateLayerAction);
305 TableHelper.adaptTo(activateLayerAction, selectionModel);
306
307 JumpToMarkerActions.initialize();
308
309 // -- show hide action
310 showHideLayerAction = new ShowHideLayerAction(model);
311 MultikeyActionsHandler.getInstance().addAction(showHideLayerAction);
312 TableHelper.adaptTo(showHideLayerAction, selectionModel);
313
314 LayerVisibilityAction visibilityAction = new LayerVisibilityAction(model);
315 TableHelper.adaptTo(visibilityAction, selectionModel);
316 SideButton visibilityButton = new SideButton(visibilityAction, false);
317 visibilityAction.setCorrespondingSideButton(visibilityButton);
318
319 // -- delete layer action
320 DeleteLayerAction deleteLayerAction = new DeleteLayerAction(model);
321 layerList.getActionMap().put("deleteLayer", deleteLayerAction);
322 TableHelper.adaptTo(deleteLayerAction, selectionModel);
323 getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
324 KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete"
325 );
326 getActionMap().put("delete", deleteLayerAction);
327
328 // Activate layer on Enter key press
329 InputMapUtils.addEnterAction(layerList, new AbstractAction() {
330 @Override
331 public void actionPerformed(ActionEvent e) {
332 activateLayerAction.actionPerformed(null);
333 layerList.requestFocus();
334 }
335 });
336
337 // Show/Activate layer on Enter key press
338 InputMapUtils.addSpacebarAction(layerList, showHideLayerAction);
339
340 // Cycle layer actions
341 cycleLayerUpAction = new CycleLayerUpAction();
342 cycleLayerDownAction = new CycleLayerDownAction();
343
344 createLayout(layerList, true, Arrays.asList(
345 new SideButton(moveUpAction, false),
346 new SideButton(moveDownAction, false),
347 new SideButton(activateLayerAction, false),
348 visibilityButton,
349 new SideButton(deleteLayerAction, false)
350 ));
351
352 createVisibilityToggleShortcuts();
353 }
354
355 private static boolean displayLayerNumbers() {
356 return ExpertToggleAction.isExpert() && DISPLAY_NUMBERS.get();
357 }
358
359 /**
360 * Gets the layer manager this dialog is for.
361 * @return The layer manager.
362 * @since 10288
363 */
364 public MainLayerManager getLayerManager() {
365 return layerManager;
366 }
367
368 @Override
369 public void showNotify() {
370 layerManager.addActiveLayerChangeListener(activateLayerAction);
371 layerManager.addAndFireLayerChangeListener(model);
372 layerManager.addAndFireActiveLayerChangeListener(model);
373 model.populate();
374 }
375
376 @Override
377 public void hideNotify() {
378 layerManager.removeAndFireLayerChangeListener(model);
379 layerManager.removeActiveLayerChangeListener(model);
380 layerManager.removeActiveLayerChangeListener(activateLayerAction);
381 }
382
383 /**
384 * Returns the layer list model.
385 * @return the layer list model
386 */
387 public LayerListModel getModel() {
388 return model;
389 }
390
391 @Override
392 public void destroy() {
393 for (int i = 0; i < 10; i++) {
394 MainApplication.unregisterActionShortcut(visibilityToggleActions[i], visibilityToggleShortcuts[i]);
395 }
396 MultikeyActionsHandler.getInstance().removeAction(activateLayerAction);
397 MultikeyActionsHandler.getInstance().removeAction(showHideLayerAction);
398 JumpToMarkerActions.unregisterActions();
399 layerList.setTransferHandler(null);
400 DISPLAY_NUMBERS.removeListener(visibilityWidthListener);
401 ExpertToggleAction.removeExpertModeChangeListener(visibilityWidthListener);
402 layerManager.removeLayerChangeListener(visibilityWidthListener);
403 cycleLayerUpAction.destroy();
404 cycleLayerDownAction.destroy();
405 super.destroy();
406 instance = null;
407 }
408
409 static ImageIcon createBlankIcon() {
410 return ImageProvider.createBlankIcon(ImageSizes.LAYER);
411 }
412
413 private class ColumnWidthAdaptionListener implements ValueChangeListener<Boolean>, ExpertModeChangeListener, LayerChangeListener {
414 private final int minWidth;
415 private final int column;
416
417 ColumnWidthAdaptionListener(int column, int minWidth) {
418 this.column = column;
419 this.minWidth = minWidth;
420 }
421
422 @Override
423 public void expertChanged(boolean isExpert) {
424 updateColumnWidth();
425 }
426
427 @Override
428 public void valueChanged(ValueChangeEvent<? extends Boolean> e) {
429 updateColumnWidth();
430 }
431
432 @Override
433 public void layerAdded(LayerAddEvent e) {
434 updateColumnWidth();
435 }
436
437 @Override
438 public void layerRemoving(LayerRemoveEvent e) {
439 updateColumnWidth();
440 }
441
442 @Override
443 public void layerOrderChanged(LayerOrderChangeEvent e) {
444 //not needed
445 }
446
447 public void updateColumnWidth() {
448 int width = minWidth;
449 for (int row = 0; row < layerList.getRowCount(); row++) {
450 TableCellRenderer renderer = layerList.getCellRenderer(row, column);
451 Component comp = layerList.prepareRenderer(renderer, row, column);
452 width = Math.max(comp.getPreferredSize().width + 1, width);
453 }
454 layerList.getColumnModel().getColumn(column).setMaxWidth(width);
455 layerList.getColumnModel().getColumn(column).setPreferredWidth(width);
456 repaint();
457 }
458 }
459
460 private static class ActiveLayerCheckBox extends JCheckBox {
461 ActiveLayerCheckBox() {
462 setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
463 ImageIcon blank = createBlankIcon();
464 ImageIcon active = ImageProvider.get("dialogs/layerlist", "active");
465 setIcon(blank);
466 setSelectedIcon(active);
467 setRolloverIcon(blank);
468 setRolloverSelectedIcon(active);
469 }
470 }
471
472 private static class LayerVisibleCheckBox extends JCheckBox {
473 private final ImageIcon iconEye;
474 private final ImageIcon iconEyeTranslucent;
475 private boolean isTranslucent;
476 private Layer layer;
477
478 /**
479 * Constructs a new {@code LayerVisibleCheckBox}.
480 */
481 LayerVisibleCheckBox() {
482 iconEye = new EyeIcon(/* ICON(dialogs/layerlist/) */ "eye");
483 iconEyeTranslucent = new EyeIcon(/* ICON(dialogs/layerlist/) */ "eye-translucent", true);
484 setIcon(ImageProvider.get("dialogs/layerlist", "eye-off"));
485 setSelectedIcon(iconEye);
486 isTranslucent = false;
487 }
488
489 public void setTranslucent(boolean isTranslucent) {
490 if (this.isTranslucent == isTranslucent) return;
491 if (isTranslucent) {
492 setSelectedIcon(iconEyeTranslucent);
493 } else {
494 setSelectedIcon(iconEye);
495 }
496 this.isTranslucent = isTranslucent;
497 }
498
499 public void updateStatus(Layer layer) {
500 this.layer = layer;
501 boolean visible = layer.isVisible();
502 setSelected(visible);
503 if (displayLayerNumbers()) {
504 List<Layer> layers = MainApplication.getLayerManager().getLayers();
505 int num = layers.size() - layers.indexOf(layer);
506 setText(String.format("%s[%d]", num < 10 ? " " : "", num));
507 } else {
508 setText(null);
509 }
510 setTranslucent(layer.getOpacity() < 1.0);
511 setToolTipText(visible ?
512 tr("layer is currently visible (click to hide layer)") :
513 tr("layer is currently hidden (click to show layer)"));
514 }
515
516 private class EyeIcon extends ImageIcon {
517 private final boolean translucent;
518
519 EyeIcon(String name) {
520 this(name, false);
521 }
522
523 EyeIcon(String name, boolean translucent) {
524 super(ImageProvider.get("dialogs/layerlist", name).getImage());
525 this.translucent = translucent;
526 }
527
528 @Override
529 public synchronized void paintIcon(Component comp, Graphics g, int x, int y) {
530 Color c;
531 if (Config.getPref().getBoolean("dialog.layer.colorname", true)
532 && layer != null && (c = layer.getColor()) != null) {
533 if (g instanceof Graphics2D) {
534 ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
535 }
536 if (translucent) {
537 g.setColor(new Color(c.getRed(), c.getGreen(), c.getBlue(), 125));
538 } else {
539 g.setColor(c);
540 }
541 g.fillOval(x, y + 2, getIconWidth(), getIconHeight() - 3);
542 }
543 super.paintIcon(comp, g, x, y);
544 }
545 }
546 }
547
548 private static class NativeScaleLayerCheckBox extends JCheckBox {
549 NativeScaleLayerCheckBox() {
550 setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
551 ImageIcon blank = createBlankIcon();
552 ImageIcon active = ImageProvider.get("dialogs/layerlist", "scale");
553 setIcon(blank);
554 setSelectedIcon(active);
555 }
556 }
557
558 private static class OffsetLayerCheckBox extends JCheckBox {
559 OffsetLayerCheckBox() {
560 setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
561 ImageIcon blank = createBlankIcon();
562 ImageIcon withOffset = ImageProvider.get("dialogs/layerlist", "offset");
563 setIcon(blank);
564 setSelectedIcon(withOffset);
565 }
566 }
567
568 private static class ActiveLayerCellRenderer implements TableCellRenderer {
569 private final JCheckBox cb;
570
571 /**
572 * Constructs a new {@code ActiveLayerCellRenderer}.
573 */
574 ActiveLayerCellRenderer() {
575 cb = new ActiveLayerCheckBox();
576 }
577
578 @Override
579 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
580 boolean active = value != null && (Boolean) value;
581 cb.setSelected(active);
582 cb.setToolTipText(active ? tr("this layer is the active layer") : tr("this layer is not currently active (click to activate)"));
583 return cb;
584 }
585 }
586
587 private static class LayerVisibleCellRenderer implements TableCellRenderer {
588 private final LayerVisibleCheckBox cb;
589
590 /**
591 * Constructs a new {@code LayerVisibleCellRenderer}.
592 */
593 LayerVisibleCellRenderer() {
594 this.cb = new LayerVisibleCheckBox();
595 }
596
597 @Override
598 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
599 if (value != null) {
600 cb.updateStatus((Layer) value);
601 }
602 return cb;
603 }
604 }
605
606 private static class LayerVisibleCellEditor extends DefaultCellEditor {
607 private final LayerVisibleCheckBox cb;
608
609 LayerVisibleCellEditor(LayerVisibleCheckBox cb) {
610 super(cb);
611 this.cb = cb;
612 }
613
614 @Override
615 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
616 cb.updateStatus((Layer) value);
617 return cb;
618 }
619 }
620
621 private static class NativeScaleLayerCellRenderer implements TableCellRenderer {
622 private final JCheckBox cb;
623
624 /**
625 * Constructs a new {@code ActiveLayerCellRenderer}.
626 */
627 NativeScaleLayerCellRenderer() {
628 cb = new NativeScaleLayerCheckBox();
629 }
630
631 @Override
632 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
633 Layer layer = (Layer) value;
634 if (layer instanceof NativeScaleLayer) {
635 boolean active = ((NativeScaleLayer) layer) == MainApplication.getMap().mapView.getNativeScaleLayer();
636 cb.setSelected(active);
637 if (MainApplication.getMap().mapView.getNativeScaleLayer() != null) {
638 cb.setToolTipText(active
639 ? tr("scale follows native resolution of this layer")
640 : tr("scale follows native resolution of another layer (click to set this layer)"));
641 } else {
642 cb.setToolTipText(tr("scale does not follow native resolution of any layer (click to set this layer)"));
643 }
644 } else {
645 cb.setSelected(false);
646 cb.setToolTipText(tr("this layer has no native resolution"));
647 }
648 return cb;
649 }
650 }
651
652 private static class OffsetLayerCellRenderer implements TableCellRenderer {
653 private final JCheckBox cb;
654
655 /**
656 * Constructs a new {@code OffsetLayerCellRenderer}.
657 */
658 OffsetLayerCellRenderer() {
659 cb = new OffsetLayerCheckBox();
660 cb.setEnabled(false);
661 }
662
663 @Override
664 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
665 Layer layer = (Layer) value;
666 if (layer instanceof AbstractTileSourceLayer<?>) {
667 final TileSourceDisplaySettings displaySettings = ((AbstractTileSourceLayer<?>) layer).getDisplaySettings();
668 if (EastNorth.ZERO.equals(displaySettings.getDisplacement())) {
669 final boolean hasPreviousOffset = displaySettings.getPreviousOffsetBookmark() != null;
670 cb.setSelected(false);
671 cb.setEnabled(hasPreviousOffset);
672 cb.setToolTipText(tr("layer is without a user-defined offset") +
673 (hasPreviousOffset ? " " + tr("(click to activate previous offset)") : ""));
674 } else {
675 cb.setSelected(true);
676 cb.setEnabled(true);
677 cb.setToolTipText(tr("layer has an offset of {0} (click to remove offset)",
678 displaySettings.getDisplacementString(Locale.getDefault())));
679 }
680
681 } else {
682 cb.setSelected(false);
683 cb.setEnabled(false);
684 cb.setToolTipText(tr("this layer can not have an offset"));
685 }
686 return cb;
687 }
688 }
689
690 private class LayerNameCellRenderer extends DefaultTableCellRenderer {
691
692 protected boolean isActiveLayer(Layer layer) {
693 return getLayerManager().getActiveLayer() == layer;
694 }
695
696 @Override
697 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
698 if (value == null)
699 return this;
700 Layer layer = (Layer) value;
701 JLabel label = (JLabel) super.getTableCellRendererComponent(table,
702 layer.getName(), isSelected, hasFocus, row, column);
703 if (isActiveLayer(layer)) {
704 label.setFont(label.getFont().deriveFont(Font.BOLD));
705 }
706 label.setIcon(layer.getIcon());
707 label.setToolTipText(layer.getToolTipText());
708 return label;
709 }
710 }
711
712 private static class LayerNameCellEditor extends DefaultCellEditor {
713 LayerNameCellEditor(DisableShortcutsOnFocusGainedTextField tf) {
714 super(tf);
715 }
716
717 @Override
718 public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
719 JosmTextField tf = (JosmTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);
720 tf.setText(value == null ? "" : ((Layer) value).getName());
721 return tf;
722 }
723 }
724
725 class PopupMenuHandler extends PopupMenuLauncher {
726 @Override
727 public void showMenu(MouseEvent evt) {
728 menu = new LayerListPopup(getModel().getSelectedLayers());
729 super.showMenu(evt);
730 }
731 }
732
733 /**
734 * Observer interface to be implemented by views using {@link LayerListModel}.
735 */
736 public interface LayerListModelListener {
737
738 /**
739 * Fired when a layer is made visible.
740 * @param index the layer index
741 * @param layer the layer
742 */
743 void makeVisible(int index, Layer layer);
744
745
746 /**
747 * Fired when something has changed in the layer list model.
748 */
749 void refresh();
750 }
751
752 /**
753 * The layer list model. The model manages a list of layers and provides methods for
754 * moving layers up and down, for toggling their visibility, and for activating a layer.
755 *
756 * The model is a {@link TableModel} and it provides a {@link ListSelectionModel}. It expects
757 * to be configured with a {@link DefaultListSelectionModel}. The selection model is used
758 * to update the selection state of views depending on messages sent to the model.
759 *
760 * The model manages a list of {@link LayerListModelListener} which are mainly notified if
761 * the model requires views to make a specific list entry visible.
762 *
763 * It also listens to {@link PropertyChangeEvent}s of every {@link Layer} it manages, in particular to
764 * the properties {@link Layer#VISIBLE_PROP} and {@link Layer#NAME_PROP}.
765 */
766 public static final class LayerListModel extends AbstractTableModel
767 implements LayerChangeListener, ActiveLayerChangeListener, PropertyChangeListener, ReorderableTableModel<Layer> {
768 /** manages list selection state*/
769 private final DefaultListSelectionModel selectionModel;
770 private final CopyOnWriteArrayList<LayerListModelListener> listeners;
771 private LayerList layerList;
772 private final MainLayerManager layerManager;
773
774 /**
775 * constructor
776 * @param layerManager The layer manager to use for the list.
777 * @param selectionModel the list selection model
778 */
779 LayerListModel(MainLayerManager layerManager, DefaultListSelectionModel selectionModel) {
780 this.layerManager = layerManager;
781 this.selectionModel = selectionModel;
782 listeners = new CopyOnWriteArrayList<>();
783 }
784
785 void setLayerList(LayerList layerList) {
786 this.layerList = layerList;
787 }
788
789 /**
790 * The layer manager this model is for.
791 * @return The layer manager.
792 */
793 public MainLayerManager getLayerManager() {
794 return layerManager;
795 }
796
797 /**
798 * Adds a listener to this model
799 *
800 * @param listener the listener
801 */
802 public void addLayerListModelListener(LayerListModelListener listener) {
803 if (listener != null) {
804 listeners.addIfAbsent(listener);
805 }
806 }
807
808 /**
809 * removes a listener from this model
810 * @param listener the listener
811 */
812 public void removeLayerListModelListener(LayerListModelListener listener) {
813 listeners.remove(listener);
814 }
815
816 /**
817 * Fires a make visible event to listeners
818 *
819 * @param index the index of the row to make visible
820 * @param layer the layer at this index
821 * @see LayerListModelListener#makeVisible(int, Layer)
822 */
823 private void fireMakeVisible(int index, Layer layer) {
824 for (LayerListModelListener listener : listeners) {
825 listener.makeVisible(index, layer);
826 }
827 }
828
829 /**
830 * Fires a refresh event to listeners of this model
831 *
832 * @see LayerListModelListener#refresh()
833 */
834 private void fireRefresh() {
835 for (LayerListModelListener listener : listeners) {
836 listener.refresh();
837 }
838 }
839
840 /**
841 * Populates the model with the current layers managed by {@link MapView}.
842 */
843 public void populate() {
844 for (Layer layer: getLayers()) {
845 // make sure the model is registered exactly once
846 layer.removePropertyChangeListener(this);
847 layer.addPropertyChangeListener(this);
848 }
849 fireTableDataChanged();
850 }
851
852 /**
853 * Marks <code>layer</code> as selected layer. Ignored, if layer is null.
854 *
855 * @param layer the layer.
856 */
857 public void setSelectedLayer(Layer layer) {
858 if (layer == null)
859 return;
860 int idx = getLayers().indexOf(layer);
861 if (idx >= 0) {
862 selectionModel.setSelectionInterval(idx, idx);
863 }
864 ensureSelectedIsVisible();
865 }
866
867 /**
868 * Replies the list of currently selected layers. Never null, but may be empty.
869 *
870 * @return the list of currently selected layers. Never null, but may be empty.
871 */
872 public List<Layer> getSelectedLayers() {
873 List<Layer> layers = getLayers();
874 return IntStream.range(0, layers.size())
875 .filter(selectionModel::isSelectedIndex)
876 .mapToObj(layers::get)
877 .collect(Collectors.toList());
878 }
879
880 /**
881 * Replies a the list of indices of the selected rows. Never null, but may be empty.
882 *
883 * @return the list of indices of the selected rows. Never null, but may be empty.
884 */
885 public List<Integer> getSelectedRows() {
886 return ArrayUtils.toList(TableHelper.getSelectedIndices(selectionModel));
887 }
888
889 /**
890 * Invoked if a layer managed by {@link MapView} is removed
891 *
892 * @param layer the layer which is removed
893 */
894 private void onRemoveLayer(Layer layer) {
895 if (layer == null)
896 return;
897 layer.removePropertyChangeListener(this);
898 final int size = getRowCount();
899 final int[] rows = TableHelper.getSelectedIndices(selectionModel);
900
901 if (rows.length == 0 && size > 0) {
902 selectionModel.setSelectionInterval(size-1, size-1);
903 }
904 fireTableDataChanged();
905 fireRefresh();
906 ensureActiveSelected();
907 }
908
909 /**
910 * Invoked when a layer managed by {@link MapView} is added
911 *
912 * @param layer the layer
913 */
914 private void onAddLayer(Layer layer) {
915 if (layer == null)
916 return;
917 layer.addPropertyChangeListener(this);
918 fireTableDataChanged();
919 int idx = getLayers().indexOf(layer);
920 Icon icon = layer.getIcon();
921 if (layerList != null && icon != null) {
922 layerList.setRowHeight(idx, Math.max(layerList.getRowHeight(), icon.getIconHeight()));
923 }
924 selectionModel.setSelectionInterval(idx, idx);
925 ensureSelectedIsVisible();
926 if (layer instanceof AbstractTileSourceLayer<?>) {
927 ((AbstractTileSourceLayer<?>) layer).getDisplaySettings().addSettingsChangeListener(LayerListDialog.getInstance());
928 }
929 }
930
931 /**
932 * Replies the first layer. Null if no layers are present
933 *
934 * @return the first layer. Null if no layers are present
935 */
936 public Layer getFirstLayer() {
937 if (getRowCount() == 0)
938 return null;
939 return getLayers().get(0);
940 }
941
942 /**
943 * Replies the layer at position <code>index</code>
944 *
945 * @param index the index
946 * @return the layer at position <code>index</code>. Null,
947 * if index is out of range.
948 */
949 public Layer getLayer(int index) {
950 if (index < 0 || index >= getRowCount())
951 return null;
952 return getLayers().get(index);
953 }
954
955 @Override
956 public DefaultListSelectionModel getSelectionModel() {
957 return selectionModel;
958 }
959
960 @Override
961 public Layer getValue(int index) {
962 return getLayer(index);
963 }
964
965 @Override
966 public Layer setValue(int index, Layer value) {
967 throw new UnsupportedOperationException();
968 }
969
970 @Override
971 public boolean doMove(int delta, int... selectedRows) {
972 if (delta != 0) {
973 List<Layer> layers = getLayers();
974 MapView mapView = MainApplication.getMap().mapView;
975 if (delta < 0) {
976 for (int row : selectedRows) {
977 mapView.moveLayer(layers.get(row), row + delta);
978 }
979 } else {
980 for (int i = selectedRows.length - 1; i >= 0; i--) {
981 mapView.moveLayer(layers.get(selectedRows[i]), selectedRows[i] + delta);
982 }
983 }
984 fireTableDataChanged();
985 }
986 return delta != 0;
987 }
988
989 @Override
990 public boolean move(int delta, int... selectedRows) {
991 if (!ReorderableTableModel.super.move(delta, selectedRows))
992 return false;
993 ensureSelectedIsVisible();
994 return true;
995 }
996
997 /**
998 * Make sure the first of the selected layers is visible in the views of this model.
999 */
1000 private void ensureSelectedIsVisible() {
1001 int index = selectionModel.getMinSelectionIndex();
1002 if (index < 0)
1003 return;
1004 List<Layer> layers = getLayers();
1005 if (index >= layers.size())
1006 return;
1007 Layer layer = layers.get(index);
1008 fireMakeVisible(index, layer);
1009 }
1010
1011 /**
1012 * Replies a list of layers which are possible merge targets for <code>source</code>
1013 *
1014 * @param source the source layer
1015 * @return a list of layers which are possible merge targets
1016 * for <code>source</code>. Never null, but can be empty.
1017 */
1018 public List<Layer> getPossibleMergeTargets(Layer source) {
1019 if (source == null) {
1020 return new ArrayList<>();
1021 }
1022 return getLayers().stream()
1023 .filter(target -> source != target && target.isMergable(source) && source.isMergable(target))
1024 .collect(Collectors.toList());
1025 }
1026
1027 /**
1028 * Replies the list of layers currently managed by {@link MapView}.
1029 * Never null, but can be empty.
1030 *
1031 * @return the list of layers currently managed by {@link MapView}.
1032 * Never null, but can be empty.
1033 */
1034 public List<Layer> getLayers() {
1035 return getLayerManager().getLayers();
1036 }
1037
1038 /**
1039 * Ensures that at least one layer is selected in the layer dialog
1040 *
1041 */
1042 private void ensureActiveSelected() {
1043 List<Layer> layers = getLayers();
1044 if (layers.isEmpty())
1045 return;
1046 final Layer activeLayer = getActiveLayer();
1047 if (activeLayer != null) {
1048 // there's an active layer - select it and make it visible
1049 int idx = layers.indexOf(activeLayer);
1050 selectionModel.setSelectionInterval(idx, idx);
1051 ensureSelectedIsVisible();
1052 } else {
1053 // no active layer - select the first one and make it visible
1054 selectionModel.setSelectionInterval(0, 0);
1055 ensureSelectedIsVisible();
1056 }
1057 }
1058
1059 /**
1060 * Replies the active layer. null, if no active layer is available
1061 *
1062 * @return the active layer. null, if no active layer is available
1063 */
1064 private Layer getActiveLayer() {
1065 return getLayerManager().getActiveLayer();
1066 }
1067
1068 /* ------------------------------------------------------------------------------ */
1069 /* Interface TableModel */
1070 /* ------------------------------------------------------------------------------ */
1071
1072 @Override
1073 public int getRowCount() {
1074 List<Layer> layers = getLayers();
1075 return layers == null ? 0 : layers.size();
1076 }
1077
1078 @Override
1079 public int getColumnCount() {
1080 return 5;
1081 }
1082
1083 @Override
1084 public Object getValueAt(int row, int col) {
1085 List<Layer> layers = getLayers();
1086 if (row >= 0 && row < layers.size()) {
1087 switch (col) {
1088 case 0: return layers.get(row) == getActiveLayer();
1089 case 1:
1090 case 2:
1091 case 3:
1092 case 4: return layers.get(row);
1093 default: // Do nothing
1094 }
1095 }
1096 return null;
1097 }
1098
1099 @Override
1100 public boolean isCellEditable(int row, int col) {
1101 return col != 0 || getActiveLayer() != getLayers().get(row);
1102 }
1103
1104 @Override
1105 public void setValueAt(Object value, int row, int col) {
1106 List<Layer> layers = getLayers();
1107 if (row < layers.size()) {
1108 Layer l = layers.get(row);
1109 switch (col) {
1110 case 0:
1111 getLayerManager().setActiveLayer(l);
1112 l.setVisible(true);
1113 break;
1114 case 1:
1115 MapFrame map = MainApplication.getMap();
1116 NativeScaleLayer oldLayer = map.mapView.getNativeScaleLayer();
1117 if (oldLayer == l) {
1118 map.mapView.setNativeScaleLayer(null);
1119 } else if (l instanceof NativeScaleLayer) {
1120 map.mapView.setNativeScaleLayer((NativeScaleLayer) l);
1121 if (oldLayer instanceof Layer) {
1122 int idx = getLayers().indexOf((Layer) oldLayer);
1123 if (idx >= 0) {
1124 fireTableCellUpdated(idx, col);
1125 }
1126 }
1127 }
1128 break;
1129 case 2:
1130 // reset layer offset
1131 if (l instanceof AbstractTileSourceLayer<?>) {
1132 final TileSourceDisplaySettings displaySettings = ((AbstractTileSourceLayer<?>) l).getDisplaySettings();
1133 final OffsetBookmark offsetBookmark = displaySettings.getOffsetBookmark();
1134 if (offsetBookmark != null) {
1135 displaySettings.setOffsetBookmark(null);
1136 MainApplication.getMenu().imageryMenu.refreshOffsetMenu();
1137 } else {
1138 displaySettings.setOffsetBookmark(displaySettings.getPreviousOffsetBookmark());
1139 }
1140 }
1141 break;
1142 case 3:
1143 l.setVisible((Boolean) value);
1144 break;
1145 case 4:
1146 l.rename((String) value);
1147 break;
1148 default:
1149 throw new IllegalArgumentException("Wrong column: " + col);
1150 }
1151 fireTableCellUpdated(row, col);
1152 }
1153 }
1154
1155 /* ------------------------------------------------------------------------------ */
1156 /* Interface ActiveLayerChangeListener */
1157 /* ------------------------------------------------------------------------------ */
1158 @Override
1159 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
1160 Layer oldLayer = e.getPreviousActiveLayer();
1161 if (oldLayer != null) {
1162 int idx = getLayers().indexOf(oldLayer);
1163 if (idx >= 0) {
1164 fireTableRowsUpdated(idx, idx);
1165 }
1166 }
1167
1168 Layer newLayer = getActiveLayer();
1169 if (newLayer != null) {
1170 int idx = getLayers().indexOf(newLayer);
1171 if (idx >= 0) {
1172 fireTableRowsUpdated(idx, idx);
1173 }
1174 }
1175 ensureActiveSelected();
1176 }
1177
1178 /* ------------------------------------------------------------------------------ */
1179 /* Interface LayerChangeListener */
1180 /* ------------------------------------------------------------------------------ */
1181 @Override
1182 public void layerAdded(LayerAddEvent e) {
1183 onAddLayer(e.getAddedLayer());
1184 }
1185
1186 @Override
1187 public void layerRemoving(LayerRemoveEvent e) {
1188 onRemoveLayer(e.getRemovedLayer());
1189 }
1190
1191 @Override
1192 public void layerOrderChanged(LayerOrderChangeEvent e) {
1193 fireTableDataChanged();
1194 }
1195
1196 /* ------------------------------------------------------------------------------ */
1197 /* Interface PropertyChangeListener */
1198 /* ------------------------------------------------------------------------------ */
1199 @Override
1200 public void propertyChange(PropertyChangeEvent evt) {
1201 if (evt.getSource() instanceof Layer) {
1202 Layer layer = (Layer) evt.getSource();
1203 final int idx = getLayers().indexOf(layer);
1204 if (idx < 0)
1205 return;
1206 fireRefresh();
1207 }
1208 }
1209 }
1210
1211 /**
1212 * This component displays a list of layers and provides the methods needed by {@link LayerListModel}.
1213 */
1214 static class LayerList extends ScrollableTable {
1215
1216 LayerList(LayerListModel dataModel) {
1217 super(dataModel);
1218 dataModel.setLayerList(this);
1219 if (!GraphicsEnvironment.isHeadless()) {
1220 setDragEnabled(true);
1221 }
1222 setDropMode(DropMode.INSERT_ROWS);
1223 setTransferHandler(new LayerListTransferHandler());
1224 }
1225
1226 @Override
1227 public LayerListModel getModel() {
1228 return (LayerListModel) super.getModel();
1229 }
1230 }
1231
1232 /**
1233 * Creates a {@link ShowHideLayerAction} in the context of this {@link LayerListDialog}.
1234 *
1235 * @return the action
1236 */
1237 public ShowHideLayerAction createShowHideLayerAction() {
1238 return new ShowHideLayerAction(model);
1239 }
1240
1241 /**
1242 * Creates a {@link DeleteLayerAction} in the context of this {@link LayerListDialog}.
1243 *
1244 * @return the action
1245 */
1246 public DeleteLayerAction createDeleteLayerAction() {
1247 return new DeleteLayerAction(model);
1248 }
1249
1250 /**
1251 * Creates a {@link ActivateLayerAction} for <code>layer</code> in the context of this {@link LayerListDialog}.
1252 *
1253 * @param layer the layer
1254 * @return the action
1255 */
1256 public ActivateLayerAction createActivateLayerAction(Layer layer) {
1257 return new ActivateLayerAction(layer, model);
1258 }
1259
1260 /**
1261 * Creates a {@link MergeLayerAction} for <code>layer</code> in the context of this {@link LayerListDialog}.
1262 *
1263 * @param layer the layer
1264 * @return the action
1265 */
1266 public MergeAction createMergeLayerAction(Layer layer) {
1267 return new MergeAction(layer, model);
1268 }
1269
1270 /**
1271 * Creates a {@link DuplicateAction} for <code>layer</code> in the context of this {@link LayerListDialog}.
1272 *
1273 * @param layer the layer
1274 * @return the action
1275 */
1276 public DuplicateAction createDuplicateLayerAction(Layer layer) {
1277 return new DuplicateAction(layer, model);
1278 }
1279
1280 /**
1281 * Returns the layer at given index, or {@code null}.
1282 * @param index the index
1283 * @return the layer at given index, or {@code null} if index out of range
1284 */
1285 public static Layer getLayerForIndex(int index) {
1286 List<Layer> layers = MainApplication.getLayerManager().getLayers();
1287
1288 if (index < layers.size() && index >= 0)
1289 return layers.get(index);
1290 else
1291 return null;
1292 }
1293
1294 /**
1295 * Returns a list of info on all layers of a given class.
1296 * @param layerClass The layer class. This is not {@code Class<? extends Layer>} on purpose,
1297 * to allow asking for layers implementing some interface
1298 * @return list of info on all layers assignable from {@code layerClass}
1299 */
1300 public static List<MultikeyInfo> getLayerInfoByClass(Class<?> layerClass) {
1301 List<MultikeyInfo> result = new ArrayList<>();
1302
1303 List<Layer> layers = MainApplication.getLayerManager().getLayers();
1304
1305 int index = 0;
1306 for (Layer l: layers) {
1307 if (layerClass.isAssignableFrom(l.getClass())) {
1308 result.add(new MultikeyInfo(index, l.getName()));
1309 }
1310 index++;
1311 }
1312
1313 return result;
1314 }
1315
1316 /**
1317 * Determines if a layer is valid (contained in global layer list).
1318 * @param l the layer
1319 * @return {@code true} if layer {@code l} is contained in current layer list
1320 */
1321 public static boolean isLayerValid(Layer l) {
1322 if (l == null)
1323 return false;
1324
1325 return MainApplication.getLayerManager().containsLayer(l);
1326 }
1327
1328 /**
1329 * Returns info about layer.
1330 * @param l the layer
1331 * @return info about layer {@code l}
1332 */
1333 public static MultikeyInfo getLayerInfo(Layer l) {
1334 if (l == null)
1335 return null;
1336
1337 int index = MainApplication.getLayerManager().getLayers().indexOf(l);
1338 if (index < 0)
1339 return null;
1340
1341 return new MultikeyInfo(index, l.getName());
1342 }
1343
1344 @Override
1345 public void displaySettingsChanged(DisplaySettingsChangeEvent e) {
1346 if ("displacement".equals(e.getChangedSetting())) {
1347 layerList.repaint();
1348 }
1349 }
1350}
Note: See TracBrowser for help on using the repository browser.