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

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

sonar - squid:S3052 - Fields should not be initialized to default values

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