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

Last change on this file since 15651 was 15651, checked in by Don-vip, 4 years ago

see #18509 - fix icon warnings

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