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

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

see #8902 - (indexOf(X) >= 0) => contains(X) (patch by shinigami)

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