source: josm/trunk/src/org/openstreetmap/josm/gui/layer/gpx/ChooseTrackVisibilityAction.java@ 7518

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

fix #10489 - Add additional attributes to GPX Export for nodes + GPX code improvements

File size: 11.4 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer.gpx;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.tr;
6
7import java.awt.Component;
8import java.awt.Dimension;
9import java.awt.GridBagLayout;
10import java.awt.event.ActionEvent;
11import java.awt.event.ActionListener;
12import java.awt.event.MouseAdapter;
13import java.awt.event.MouseEvent;
14import java.awt.event.MouseListener;
15import java.util.Arrays;
16import java.util.Comparator;
17import java.util.Map;
18
19import javax.swing.AbstractAction;
20import javax.swing.JComponent;
21import javax.swing.JLabel;
22import javax.swing.JPanel;
23import javax.swing.JScrollPane;
24import javax.swing.JTable;
25import javax.swing.JToggleButton;
26import javax.swing.ListSelectionModel;
27import javax.swing.event.ListSelectionEvent;
28import javax.swing.event.ListSelectionListener;
29import javax.swing.table.DefaultTableModel;
30import javax.swing.table.TableCellRenderer;
31import javax.swing.table.TableRowSorter;
32
33import org.openstreetmap.josm.Main;
34import org.openstreetmap.josm.data.gpx.GpxConstants;
35import org.openstreetmap.josm.data.gpx.GpxTrack;
36import org.openstreetmap.josm.gui.ExtendedDialog;
37import org.openstreetmap.josm.gui.NavigatableComponent;
38import org.openstreetmap.josm.gui.layer.GpxLayer;
39import org.openstreetmap.josm.tools.GBC;
40import org.openstreetmap.josm.tools.ImageProvider;
41import org.openstreetmap.josm.tools.OpenBrowser;
42import org.openstreetmap.josm.tools.WindowGeometry;
43
44/**
45 * allows the user to choose which of the downloaded tracks should be displayed.
46 * they can be chosen from the gpx layer context menu.
47 */
48public class ChooseTrackVisibilityAction extends AbstractAction {
49 private final GpxLayer layer;
50
51 DateFilterPanel dateFilter;
52 JTable table;
53
54 /**
55 * Constructs a new {@code ChooseTrackVisibilityAction}.
56 * @param layer The associated GPX layer
57 */
58 public ChooseTrackVisibilityAction(final GpxLayer layer) {
59 super(tr("Choose visible tracks"), ImageProvider.get("dialogs/filter"));
60 this.layer = layer;
61 putValue("help", ht("/Action/ChooseTrackVisibility"));
62 }
63
64 /**
65 * Class to format a length according to SystemOfMesurement.
66 */
67 private static final class TrackLength {
68 private double value;
69
70 /**
71 * Constructs a new {@code TrackLength} object with a given length.
72 * @param value length of the track
73 */
74 TrackLength(double value) {
75 this.value = value;
76 }
77
78 /**
79 * Provides string representation.
80 * @return String representation depending of SystemOfMeasurement
81 */
82 @Override
83 public String toString() {
84 return NavigatableComponent.getSystemOfMeasurement().getDistText(value);
85 }
86 }
87
88 /**
89 * Comparator for TrackLength objects
90 */
91 private static final class LengthContentComparator implements Comparator<TrackLength> {
92
93 /**
94 * Compare 2 TrackLength objects relative to the real length
95 */
96 @Override
97 public int compare(TrackLength l0, TrackLength l1) {
98 if(l0.value < l1.value)
99 return -1;
100 else if(l0.value > l1.value)
101 return 1;
102 return 0;
103 }
104 }
105
106 /**
107 * gathers all available data for the tracks and returns them as array of arrays
108 * in the expected column order */
109 private Object[][] buildTableContents() {
110 Object[][] tracks = new Object[layer.data.tracks.size()][5];
111 int i = 0;
112 for (GpxTrack trk : layer.data.tracks) {
113 Map<String, Object> attr = trk.getAttributes();
114 String name = (String) (attr.containsKey(GpxConstants.GPX_NAME) ? attr.get(GpxConstants.GPX_NAME) : "");
115 String desc = (String) (attr.containsKey(GpxConstants.GPX_DESC) ? attr.get(GpxConstants.GPX_DESC) : "");
116 String time = GpxLayer.getTimespanForTrack(trk);
117 TrackLength length = new TrackLength(trk.length());
118 String url = (String) (attr.containsKey("url") ? attr.get("url") : "");
119 tracks[i] = new Object[]{name, desc, time, length, url};
120 i++;
121 }
122 return tracks;
123 }
124
125 /**
126 * Builds an non-editable table whose 5th column will open a browser when double clicked.
127 * The table will fill its parent. */
128 private JTable buildTable(Object[][] content) {
129 final String[] headers = {tr("Name"), tr("Description"), tr("Timespan"), tr("Length"), tr("URL")};
130 DefaultTableModel model = new DefaultTableModel(content, headers);
131 final JTable t = new JTable(model) {
132 @Override
133 public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {
134 Component c = super.prepareRenderer(renderer, row, col);
135 if (c instanceof JComponent) {
136 JComponent jc = (JComponent) c;
137 jc.setToolTipText(getValueAt(row, col).toString());
138 }
139 return c;
140 }
141
142 @Override
143 public boolean isCellEditable(int rowIndex, int colIndex) {
144 return false;
145 }
146 };
147 // define how to sort row
148 TableRowSorter<DefaultTableModel> rowSorter = new TableRowSorter<>();
149 t.setRowSorter(rowSorter);
150 rowSorter.setModel(model);
151 rowSorter.setComparator(3, new LengthContentComparator());
152 // default column widths
153 t.getColumnModel().getColumn(0).setPreferredWidth(220);
154 t.getColumnModel().getColumn(1).setPreferredWidth(300);
155 t.getColumnModel().getColumn(2).setPreferredWidth(200);
156 t.getColumnModel().getColumn(3).setPreferredWidth(50);
157 t.getColumnModel().getColumn(4).setPreferredWidth(100);
158 // make the link clickable
159 final MouseListener urlOpener = new MouseAdapter() {
160 @Override
161 public void mouseClicked(MouseEvent e) {
162 if (e.getClickCount() != 2) {
163 return;
164 }
165 JTable t = (JTable) e.getSource();
166 int col = t.convertColumnIndexToModel(t.columnAtPoint(e.getPoint()));
167 if (col != 4) {
168 return;
169 }
170 int row = t.rowAtPoint(e.getPoint());
171 String url = (String) t.getValueAt(row, col);
172 if (url == null || url.isEmpty()) {
173 return;
174 }
175 OpenBrowser.displayUrl(url);
176 }
177 };
178 t.addMouseListener(urlOpener);
179 t.setFillsViewportHeight(true);
180 return t;
181 }
182
183 boolean noUpdates=false;
184
185 /** selects all rows (=tracks) in the table that are currently visible on the layer*/
186 private void selectVisibleTracksInTable() {
187 // don't select any tracks if the layer is not visible
188 if (!layer.isVisible()) {
189 return;
190 }
191 ListSelectionModel s = table.getSelectionModel();
192 s.clearSelection();
193 for (int i = 0; i < layer.trackVisibility.length; i++) {
194 if (layer.trackVisibility[i]) {
195 s.addSelectionInterval(i, i);
196 }
197 }
198 }
199
200 /** listens to selection changes in the table and redraws the map */
201 private void listenToSelectionChanges() {
202 table.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
203 @Override
204 public void valueChanged(ListSelectionEvent e) {
205 if (noUpdates || !(e.getSource() instanceof ListSelectionModel)) {
206 return;
207 }
208 updateVisibilityFromTable();
209 }
210 });
211 }
212
213 private void updateVisibilityFromTable() {
214 ListSelectionModel s = table.getSelectionModel();
215 for (int i = 0; i < layer.trackVisibility.length; i++) {
216 layer.trackVisibility[table.convertRowIndexToModel(i)] = s.isSelectedIndex(i);
217 }
218 Main.map.mapView.preferenceChanged(null);
219 Main.map.repaint(100);
220 }
221
222 @Override
223 public void actionPerformed(ActionEvent arg0) {
224 final JPanel msg = new JPanel(new GridBagLayout());
225
226 dateFilter = new DateFilterPanel(layer, "gpx.traces", false);
227 dateFilter.setFilterAppliedListener(new ActionListener(){
228 @Override public void actionPerformed(ActionEvent e) {
229 noUpdates = true;
230 selectVisibleTracksInTable();
231 noUpdates = false;
232 Main.map.mapView.preferenceChanged(null);
233 Main.map.repaint(100);
234 }
235 });
236 dateFilter.loadFromPrefs();
237
238 final JToggleButton b = new JToggleButton(new AbstractAction(tr("Select by date")) {
239 @Override public void actionPerformed(ActionEvent e) {
240 if (((JToggleButton) e.getSource()).isSelected()) {
241 dateFilter.setEnabled(true);
242 dateFilter.applyFilter();
243 } else {
244 dateFilter.setEnabled(false);
245 }
246 }
247 });
248 dateFilter.setEnabled(false);
249 msg.add(b, GBC.std().insets(0,0,5,0));
250 msg.add(dateFilter, GBC.eol().insets(0,0,10,0).fill(GBC.HORIZONTAL));
251
252 msg.add(new JLabel(tr("<html>Select all tracks that you want to be displayed. You can drag select a " + "range of tracks or use CTRL+Click to select specific ones. The map is updated live in the " + "background. Open the URLs by double clicking them.</html>")), GBC.eop().fill(GBC.HORIZONTAL));
253 // build table
254 final boolean[] trackVisibilityBackup = layer.trackVisibility.clone();
255 table = buildTable(buildTableContents());
256 selectVisibleTracksInTable();
257 listenToSelectionChanges();
258 // make the table scrollable
259 JScrollPane scrollPane = new JScrollPane(table);
260 msg.add(scrollPane, GBC.eol().fill(GBC.BOTH));
261
262 // build dialog
263 ExtendedDialog ed = new ExtendedDialog(Main.parent, tr("Set track visibility for {0}", layer.getName()), new String[]{tr("Show all"), tr("Show selected only"), tr("Cancel")});
264 ed.setButtonIcons(new String[]{"dialogs/layerlist/eye", "dialogs/filter", "cancel"});
265 ed.setContent(msg, false);
266 ed.setDefaultButton(2);
267 ed.setCancelButton(3);
268 ed.configureContextsensitiveHelp("/Action/ChooseTrackVisibility", true);
269 ed.setRememberWindowGeometry(getClass().getName() + ".geometry", WindowGeometry.centerInWindow(Main.parent, new Dimension(1000, 500)));
270 ed.showDialog();
271 dateFilter.saveInPrefs();
272 int v = ed.getValue();
273 // cancel for unknown buttons and copy back original settings
274 if (v != 1 && v != 2) {
275 layer.trackVisibility = Arrays.copyOf(trackVisibilityBackup, layer.trackVisibility.length);
276 Main.map.repaint();
277 return;
278 }
279 // set visibility (1 = show all, 2 = filter). If no tracks are selected
280 // set all of them visible and...
281 ListSelectionModel s = table.getSelectionModel();
282 final boolean all = v == 1 || s.isSelectionEmpty();
283 for (int i = 0; i < layer.trackVisibility.length; i++) {
284 layer.trackVisibility[table.convertRowIndexToModel(i)] = all || s.isSelectedIndex(i);
285 }
286 // ...sync with layer visibility instead to avoid having two ways to hide everything
287 layer.setVisible(v == 1 || !s.isSelectionEmpty());
288
289 Main.map.mapView.preferenceChanged(null);
290 Main.map.repaint();
291 }
292
293}
Note: See TracBrowser for help on using the repository browser.