001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs.layer; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005 006import java.awt.Component; 007import java.awt.GridBagLayout; 008import java.awt.event.ActionEvent; 009import java.awt.event.ActionListener; 010import java.util.Collections; 011import java.util.List; 012 013import javax.swing.JButton; 014import javax.swing.JCheckBox; 015import javax.swing.JLabel; 016import javax.swing.JOptionPane; 017import javax.swing.JPanel; 018import javax.swing.JScrollPane; 019import javax.swing.JTable; 020import javax.swing.ListSelectionModel; 021import javax.swing.event.ListSelectionEvent; 022import javax.swing.event.ListSelectionListener; 023import javax.swing.table.AbstractTableModel; 024import javax.swing.table.TableColumnModel; 025 026import org.openstreetmap.josm.data.SystemOfMeasurement; 027import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 028import org.openstreetmap.josm.gui.ExtendedDialog; 029import org.openstreetmap.josm.gui.layer.GpxLayer; 030import org.openstreetmap.josm.spi.preferences.Config; 031import org.openstreetmap.josm.tools.GBC; 032import org.openstreetmap.josm.tools.ImageProvider; 033import org.openstreetmap.josm.tools.ImageProvider.ImageSizes; 034 035/** 036 * The Dialog asking the user to prioritize GPX layers when cutting overlapping tracks. 037 * Shows a checkbox asking whether to combine the tracks on cuts. 038 * @since 14338 039 */ 040public class MergeGpxLayerDialog extends ExtendedDialog { 041 042 private final GpxLayersTableModel model; 043 private final JTable t; 044 private final JCheckBox c; 045 private final JButton btnUp; 046 private final JButton btnDown; 047 048 /** 049 * Constructs a new {@code MergeGpxLayerDialog} 050 * @param parent the parent 051 * @param layers the GpxLayers to choose from 052 */ 053 public MergeGpxLayerDialog(Component parent, List<GpxLayer> layers) { 054 super(parent, tr("Merge GPX layers"), tr("Merge"), tr("Cancel")); 055 setButtonIcons("dialogs/mergedown", "cancel"); 056 057 JPanel p = new JPanel(new GridBagLayout()); 058 p.add(new JLabel("<html>" + 059 tr("Please select the order of the selected layers:<br>Tracks will be cut, when timestamps of higher layers are overlapping.") + 060 "</html>"), GBC.std(0, 0).fill(GBC.HORIZONTAL).span(2)); 061 062 c = new JCheckBox(tr("Connect overlapping tracks on cuts")); 063 c.setSelected(Config.getPref().getBoolean("mergelayer.gpx.connect", true)); 064 p.add(c, GBC.std(0, 1).fill(GBC.HORIZONTAL).span(2)); 065 066 model = new GpxLayersTableModel(layers); 067 t = new JTable(model); 068 t.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 069 t.setRowSelectionInterval(0, 0); 070 071 JScrollPane sp = new JScrollPane(t); 072 p.add(sp, GBC.std(0, 2).fill().span(2)); 073 074 t.getSelectionModel().addListSelectionListener(new RowSelectionChangedListener()); 075 TableColumnModel cmodel = t.getColumnModel(); 076 cmodel.getColumn(0).setPreferredWidth((int) (sp.getPreferredSize().getWidth() - 150)); 077 cmodel.getColumn(1).setPreferredWidth(75); 078 cmodel.getColumn(2).setPreferredWidth(75); 079 080 btnUp = new JButton(tr("Move layer up")); 081 btnUp.setIcon(ImageProvider.get("dialogs", "up", ImageSizes.SMALLICON)); 082 btnUp.setEnabled(false); 083 084 btnDown = new JButton(tr("Move layer down")); 085 btnDown.setIcon(ImageProvider.get("dialogs", "down", ImageSizes.SMALLICON)); 086 087 p.add(btnUp, GBC.std(0, 3).fill(GBC.HORIZONTAL)); 088 p.add(btnDown, GBC.std(1, 3).fill(GBC.HORIZONTAL)); 089 090 btnUp.addActionListener(new MoveLayersActionListener(true)); 091 btnDown.addActionListener(new MoveLayersActionListener(false)); 092 093 setContent(p); 094 } 095 096 @Override 097 public MergeGpxLayerDialog showDialog() { 098 super.showDialog(); 099 if (getValue() == 1) { 100 Config.getPref().putBoolean("mergelayer.gpx.connect", c.isSelected()); 101 } 102 return this; 103 } 104 105 /** 106 * Whether the user chose to connect the tracks on cuts 107 * @return the checkbox state 108 */ 109 public boolean connectCuts() { 110 return c.isSelected(); 111 } 112 113 /** 114 * The {@code List<GpxLayer>} as sorted by the user 115 * @return the list 116 */ 117 public List<GpxLayer> getSortedLayers() { 118 return model.getSortedLayers(); 119 } 120 121 private class MoveLayersActionListener implements ActionListener { 122 123 private final boolean moveUp; 124 125 MoveLayersActionListener(boolean up) { 126 moveUp = up; 127 } 128 129 @Override 130 public void actionPerformed(ActionEvent e) { 131 int row = t.getSelectedRow(); 132 int newRow = row + (moveUp ? -1 : 1); 133 134 if ((row == 0 || newRow == 0) 135 && (!ConditionalOptionPaneUtil.showConfirmationDialog( 136 "gpx_target_change", 137 getParent(), 138 new JLabel("<html>" + 139 tr("This will change the target layer to \"{0}\".<br>Would you like to continue?", 140 model.getValueAt(1, 0).toString()) + "</html>"), 141 tr("Information"), 142 JOptionPane.OK_CANCEL_OPTION, 143 JOptionPane.INFORMATION_MESSAGE, 144 JOptionPane.OK_OPTION))) { 145 return; 146 } 147 148 model.moveRow(row, newRow); 149 t.getSelectionModel().setSelectionInterval(newRow, newRow); 150 t.repaint(); 151 } 152 } 153 154 private class RowSelectionChangedListener implements ListSelectionListener { 155 156 @Override 157 public void valueChanged(ListSelectionEvent e) { 158 btnUp.setEnabled(t.getSelectedRow() > 0); 159 btnDown.setEnabled(t.getSelectedRow() < model.getRowCount() - 1); 160 } 161 } 162 163 private static class GpxLayersTableModel extends AbstractTableModel { 164 165 private final String[] cols = {tr("GPX layer"), tr("Length"), tr("Segments")}; 166 private final List<GpxLayer> layers; 167 168 GpxLayersTableModel(List<GpxLayer> l) { 169 layers = l; 170 } 171 172 @Override 173 public String getColumnName(int column) { 174 return cols[column]; 175 } 176 177 @Override 178 public int getColumnCount() { 179 return cols.length; 180 } 181 182 @Override 183 public int getRowCount() { 184 return layers.size(); 185 } 186 187 void moveRow(int row, int newRow) { 188 Collections.swap(layers, row, newRow); 189 } 190 191 List<GpxLayer> getSortedLayers() { 192 return layers; 193 } 194 195 @Override 196 public Object getValueAt(int row, int col) { 197 switch (col) { 198 case 0: 199 String n = layers.get(row).getName(); 200 if (row == 0) { 201 return tr("{0} (target layer)", n); 202 } else { 203 return n; 204 } 205 case 1: 206 return SystemOfMeasurement.getSystemOfMeasurement().getDistText(layers.get(row).data.length()); 207 case 2: 208 return layers.get(row).data.getTrackSegsCount(); 209 default: 210 throw new IndexOutOfBoundsException(Integer.toString(col)); 211 } 212 } 213 } 214}