- Timestamp:
- 2011-08-21T21:03:06+02:00 (13 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/gui/layer/GpxLayer.java
r4323 r4332 17 17 import java.awt.RenderingHints; 18 18 import java.awt.event.ActionEvent; 19 import java.awt.event.MouseAdapter; 20 import java.awt.event.MouseEvent; 21 import java.awt.event.MouseListener; 19 22 import java.awt.geom.Area; 20 23 import java.awt.geom.Rectangle2D; … … 30 33 import java.util.LinkedList; 31 34 import java.util.List; 35 import java.util.Map; 32 36 import java.util.concurrent.Future; 33 37 … … 35 39 import javax.swing.Action; 36 40 import javax.swing.Icon; 41 import javax.swing.JComponent; 37 42 import javax.swing.JFileChooser; 38 43 import javax.swing.JLabel; … … 42 47 import javax.swing.JPanel; 43 48 import javax.swing.JScrollPane; 49 import javax.swing.JTable; 50 import javax.swing.ListSelectionModel; 44 51 import javax.swing.SwingUtilities; 52 import javax.swing.event.ListSelectionEvent; 53 import javax.swing.event.ListSelectionListener; 45 54 import javax.swing.filechooser.FileFilter; 55 import javax.swing.table.TableCellRenderer; 46 56 47 57 import org.openstreetmap.josm.Main; … … 62 72 import org.openstreetmap.josm.data.projection.Projection; 63 73 import org.openstreetmap.josm.gui.ConditionalOptionPaneUtil; 74 import org.openstreetmap.josm.gui.ExtendedDialog; 64 75 import org.openstreetmap.josm.gui.HelpAwareOptionPane; 65 76 import org.openstreetmap.josm.gui.MapView; … … 80 91 import org.openstreetmap.josm.tools.GBC; 81 92 import org.openstreetmap.josm.tools.ImageProvider; 93 import org.openstreetmap.josm.tools.OpenBrowser; 82 94 import org.openstreetmap.josm.tools.UrlLabel; 83 95 import org.openstreetmap.josm.tools.Utils; 96 import org.openstreetmap.josm.tools.WindowGeometry; 84 97 85 98 public class GpxLayer extends Layer { … … 98 111 private int computeCacheColorTracksTune; 99 112 private boolean isLocalFile; 113 // used by ChooseTrackVisibilityAction to determine which tracks to show/hide 114 private boolean[] trackVisibility; 100 115 101 116 private final List<GpxTrack> lastTracks = new ArrayList<GpxTrack>(); // List of tracks at last paint … … 111 126 data = d; 112 127 computeCacheInSync = false; 128 trackVisibility = new boolean[d.tracks.size()]; 129 for(int i=0; i < d.tracks.size(); i++) { 130 trackVisibility[i] = true; 131 } 113 132 } 114 133 … … 122 141 this.setName(name); 123 142 this.isLocalFile = isLocal; 143 } 144 145 /** 146 * returns a human readable string that shows the timespan of the given track 147 */ 148 private static String getTimespanForTrack(GpxTrack trk) { 149 WayPoint earliest = null, latest = null; 150 151 for (GpxTrackSegment seg : trk.getSegments()) { 152 for (WayPoint pnt : seg.getWayPoints()) { 153 if (latest == null) { 154 latest = earliest = pnt; 155 } else { 156 if (pnt.compareTo(earliest) < 0) { 157 earliest = pnt; 158 } else { 159 latest = pnt; 160 } 161 } 162 } 163 } 164 165 String ts = ""; 166 167 if (earliest != null && latest != null) { 168 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); 169 String earliestDate = df.format(earliest.getTime()); 170 String latestDate = df.format(latest.getTime()); 171 172 if (earliestDate.equals(latestDate)) { 173 DateFormat tf = DateFormat.getTimeInstance(DateFormat.SHORT); 174 ts += earliestDate + " "; 175 ts += tf.format(earliest.getTime()) + " - " + tf.format(latest.getTime()); 176 } else { 177 DateFormat dtf = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); 178 ts += dtf.format(earliest.getTime()) + " - " + dtf.format(latest.getTime()); 179 } 180 181 int diff = (int) (latest.time - earliest.time); 182 ts += String.format(" (%d:%02d)", diff / 3600, (diff % 3600) / 60); 183 } 184 return ts; 124 185 } 125 186 … … 150 211 151 212 for (GpxTrack trk : data.tracks) { 152 WayPoint earliest = null, latest = null;153 154 213 info.append("<tr><td>"); 155 214 if (trk.getAttributes().containsKey("name")) { … … 161 220 } 162 221 info.append("</td><td>"); 163 164 for (GpxTrackSegment seg : trk.getSegments()) { 165 for (WayPoint pnt : seg.getWayPoints()) { 166 if (latest == null) { 167 latest = earliest = pnt; 168 } else { 169 if (pnt.compareTo(earliest) < 0) { 170 earliest = pnt; 171 } else { 172 latest = pnt; 173 } 174 } 175 } 176 } 177 178 if (earliest != null && latest != null) { 179 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); 180 String earliestDate = df.format(earliest.getTime()); 181 String latestDate = df.format(latest.getTime()); 182 183 if (earliestDate.equals(latestDate)) { 184 DateFormat tf = DateFormat.getTimeInstance(DateFormat.SHORT); 185 info.append(earliestDate).append(" "); 186 info.append(tf.format(earliest.getTime())).append(" - ").append(tf.format(latest.getTime())); 187 } else { 188 DateFormat dtf = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); 189 info.append(dtf.format(earliest.getTime())).append(" - ").append(dtf.format(latest.getTime())); 190 } 191 192 int diff = (int) (latest.time - earliest.time); 193 info.append(String.format(" (%d:%02d)", diff / 3600, (diff % 3600) / 60)); 194 } 195 222 info.append(getTimespanForTrack(trk)); 196 223 info.append("</td><td>"); 197 224 info.append(NavigatableComponent.getSystemOfMeasurement().getDistText(trk.length())); … … 255 282 new ConvertToDataLayerAction(), 256 283 SeparatorLayerAction.INSTANCE, 284 new ChooseTrackVisibilityAction(), 257 285 new RenameLayerAction(getAssociatedFile(), this), 258 286 SeparatorLayerAction.INSTANCE, … … 272 300 new DownloadAlongTrackAction(), 273 301 SeparatorLayerAction.INSTANCE, 302 new ChooseTrackVisibilityAction(), 274 303 new RenameLayerAction(getAssociatedFile(), this), 275 304 SeparatorLayerAction.INSTANCE, … … 592 621 LinkedList<WayPoint> visibleSegments = new LinkedList<WayPoint>(); 593 622 WayPoint last = null; 623 int i = 0; 594 624 for (GpxTrack trk: data.tracks) { 625 // hide tracks that were de-selected in ChooseTrackVisibilityAction 626 if(!trackVisibility[i++]) { 627 continue; 628 } 629 595 630 for (GpxTrackSegment trkSeg: trk.getSegments()) { 596 631 for(WayPoint pt : trkSeg.getWayPoints()) … … 822 857 public void setAssociatedFile(File file) { 823 858 data.storageFile = file; 859 } 860 861 /** 862 * allows the user to choose which of the downloaded tracks should be displayed. 863 * they can be chosen from the gpx layer context menu. 864 */ 865 public class ChooseTrackVisibilityAction extends AbstractAction { 866 public ChooseTrackVisibilityAction() { 867 super(tr("Choose visible tracks"), ImageProvider.get("dialogs/filter")); 868 } 869 870 /** 871 * gathers all available data for the tracks and returns them as array of arrays 872 * in the expected column order */ 873 private Object[][] buildTableContents() { 874 Object[][] tracks = new Object[data.tracks.size()][5]; 875 int i = 0; 876 for (GpxTrack trk : data.tracks) { 877 Map<String, Object> attr = trk.getAttributes(); 878 String name = (String) (attr.containsKey("name") ? attr.get("name") : ""); 879 String desc = (String) (attr.containsKey("desc") ? attr.get("desc") : ""); 880 String time = getTimespanForTrack(trk); 881 String length = NavigatableComponent.getSystemOfMeasurement().getDistText(trk.length()); 882 String url = (String) (attr.containsKey("url") ? attr.get("url") : ""); 883 tracks[i] = new String[] {name, desc, time, length, url}; 884 i++; 885 } 886 return tracks; 887 } 888 889 /** 890 * Builds an non-editable table whose 5th column will open a browser when double clicked. 891 * The table will fill its parent. */ 892 private JTable buildTable(String[] headers, Object[][] content) { 893 final JTable t = new JTable(content, headers) { 894 @Override 895 public Component prepareRenderer(TableCellRenderer renderer, int row, int col) { 896 Component c = super.prepareRenderer(renderer, row, col); 897 if (c instanceof JComponent) { 898 JComponent jc = (JComponent)c; 899 jc.setToolTipText((String)getValueAt(row, col)); 900 } 901 return c; 902 } 903 904 @Override 905 public boolean isCellEditable(int rowIndex, int colIndex) { 906 return false; 907 } 908 }; 909 // default column widths 910 t.getColumnModel().getColumn(0).setPreferredWidth(220); 911 t.getColumnModel().getColumn(1).setPreferredWidth(300); 912 t.getColumnModel().getColumn(2).setPreferredWidth(200); 913 t.getColumnModel().getColumn(3).setPreferredWidth(50); 914 t.getColumnModel().getColumn(4).setPreferredWidth(100); 915 // make the link clickable 916 final MouseListener urlOpener = new MouseAdapter() { 917 @Override 918 public void mouseClicked(MouseEvent e) { 919 if (e.getClickCount() != 2) 920 return; 921 JTable t = (JTable)e.getSource(); 922 int col = t.convertColumnIndexToModel(t.columnAtPoint(e.getPoint())); 923 if(col != 4) // only accept clicks on the URL column 924 return; 925 int row = t.rowAtPoint(e.getPoint()); 926 String url = (String) t.getValueAt(row, col); 927 if(url == "") 928 return; 929 OpenBrowser.displayUrl(url); 930 } 931 }; 932 t.addMouseListener(urlOpener); 933 t.setFillsViewportHeight(true); 934 return t; 935 } 936 937 /** selects all rows (=tracks) in the table that are currently visible */ 938 private void selectVisibleTracksInTable(JTable table) { 939 // don't select any tracks if the layer is not visible 940 if(!isVisible()) 941 return; 942 ListSelectionModel s = table.getSelectionModel(); 943 s.clearSelection(); 944 for(int i=0; i < trackVisibility.length; i++) 945 if(trackVisibility[i]) { 946 s.addSelectionInterval(i, i); 947 } 948 } 949 950 /** listens to selection changes in the table and redraws the map */ 951 private void listenToSelectionChanges(JTable table) { 952 table.getSelectionModel().addListSelectionListener(new ListSelectionListener(){ 953 public void valueChanged(ListSelectionEvent e) { 954 if(!(e.getSource() instanceof ListSelectionModel)) 955 return; 956 957 ListSelectionModel s = (ListSelectionModel) e.getSource(); 958 for(int i = 0; i < data.tracks.size(); i++) { 959 trackVisibility[i] = s.isSelectedIndex(i); 960 } 961 Main.map.mapView.preferenceChanged(null); 962 Main.map.repaint(100); 963 } 964 }); 965 } 966 967 @Override 968 public void actionPerformed(ActionEvent arg0) { 969 final JPanel msg = new JPanel(new GridBagLayout()); 970 msg.add(new JLabel(tr("<html>Select all tracks that you want to be displayed. You can drag select a " 971 + "range of tracks or use CTRL+Click to select specific ones. The map is updated live in the " 972 + "background. Open the URLs by double clicking them.</html>")), 973 GBC.eol().fill(GBC.HORIZONTAL)); 974 975 // build table 976 final boolean[] trackVisibilityBackup = trackVisibility.clone(); 977 final String[] headers = {tr("Name"), tr("Description"), tr("Timespan"), tr("Length"), tr("URL")}; 978 final JTable table = buildTable(headers, buildTableContents()); 979 selectVisibleTracksInTable(table); 980 listenToSelectionChanges(table); 981 982 // make the table scrollable 983 JScrollPane scrollPane = new JScrollPane(table); 984 msg.add(scrollPane, GBC.eol().fill(GBC.BOTH)); 985 986 // build dialog 987 ExtendedDialog ed = new ExtendedDialog( 988 Main.parent, tr("Set track visibility for {0}", getName()), 989 new String[] {tr("Show all"), tr("Show selected only"), tr("Cancel")}); 990 ed.setButtonIcons(new String[] {"dialogs/layerlist/eye", "dialogs/filter", "cancel"}); 991 ed.setContent(msg, false); 992 ed.setDefaultButton(2); 993 ed.setCancelButton(3); 994 ed.configureContextsensitiveHelp("/Action/ChooseTrackVisibility", true); 995 ed.setRememberWindowGeometry( 996 getClass().getName() + ".geometry", 997 WindowGeometry.centerInWindow(Main.parent, new Dimension(1000, 500)) 998 ); 999 ed.showDialog(); 1000 int v = ed.getValue(); 1001 // cancel for unknown buttons and copy back original settings 1002 if(v != 1 && v != 2) { 1003 for(int i = 0; i < data.tracks.size(); i++) { 1004 trackVisibility[i] = trackVisibilityBackup[i]; 1005 } 1006 Main.map.repaint(); 1007 return; 1008 } 1009 1010 // set visibility (1 = show all, 2 = filter). If no tracks are selected 1011 // set all of them visible and... 1012 ListSelectionModel s = table.getSelectionModel(); 1013 final boolean all = v == 1 || s.isSelectionEmpty(); 1014 for(int i = 0; i < data.tracks.size(); i++) { 1015 trackVisibility[i] = all || s.isSelectedIndex(i); 1016 } 1017 // ...sync with layer visibility instead to avoid having two ways to hide everything 1018 setVisible(v == 1 || !s.isSelectionEmpty()); 1019 Main.map.repaint(); 1020 } 824 1021 } 825 1022
Note:
See TracChangeset
for help on using the changeset viewer.