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

Last change on this file since 4333 was 4333, checked in by xeen, 13 years ago

add shortcuts to toggle layer visibility (defaults are ALT+1 .. ALT+0 for layers 1 through 10). Fixes #30

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